diff --git a/front-end/tests/hashtag.spec.mjs b/front-end/tests/hashtag.spec.mjs new file mode 100644 index 0000000..a816810 --- /dev/null +++ b/front-end/tests/hashtag.spec.mjs @@ -0,0 +1,24 @@ +import { test, expect } from "@playwright/test"; + +test("should not make infinite hashtag endpoint requests", async ({ page }) => { + // ===== ARRANGE + const requests = []; + page.on("request", (request) => { + if ( + request.url().includes(":3000/hashtag/do") && + request.resourceType() === "fetch" + ) { + requests.push(request); + } + }); + // ====== ACT + // When I navigate to the hashtag + await page.goto("/#/hashtag/do"); + // And I wait a reasonable time for any additional requests + await page.waitForTimeout(200); + + // ====== ASSERT + // Then the number of requests should be 1 + console.log("Number of requests:", requests.length); + expect(requests.length).toEqual(1); +}); diff --git a/front-end/tests/profile-logout-login.spec.mjs b/front-end/tests/profile-logout-login.spec.mjs new file mode 100644 index 0000000..ff49ab1 --- /dev/null +++ b/front-end/tests/profile-logout-login.spec.mjs @@ -0,0 +1,28 @@ +import { test, expect } from "@playwright/test"; +import { loginAsSample, logout } from "./test-utils.mjs"; + +test("can visit another user's profile after logout and re-login", async ({ + page, +}) => { + // Given I am logged in + await loginAsSample(page); + + // And I visit another user's profile + await page.goto("/#/profile/AS"); + + // And I log out + await logout(page); + + // When I log in again + await loginAsSample(page); + + // And I visit the same profile again + await page.goto("/#/profile/AS"); + + // Then I should see the profile view (NOT a server error) + await expect(page.locator("#profile-container")).toBeVisible(); + + await expect(page.locator("body")).not.toContainText( + "Server does not support this operation", + ); +}); diff --git a/front-end/views/hashtag.mjs b/front-end/views/hashtag.mjs index 7b7e996..f15918d 100644 --- a/front-end/views/hashtag.mjs +++ b/front-end/views/hashtag.mjs @@ -17,14 +17,21 @@ import {createHeading} from "../components/heading.mjs"; function hashtagView(hashtag) { destroy(); - apiService.getBloomsByHashtag(hashtag); + // Normalize the hashtag to always include a leading '#' and + // prevent infinite API calls by only fetching if the hashtag has changed + const normalizedHashtag = hashtag.startsWith("#") ? hashtag : `#${hashtag}`; + if (state.currentHashtag !== normalizedHashtag) { + state.currentHashtag = normalizedHashtag; + apiService.getBloomsByHashtag(normalizedHashtag); + } renderOne( state.isLoggedIn, getLogoutContainer(), "logout-template", - createLogout + createLogout, ); + document .querySelector("[data-action='logout']") ?.addEventListener("click", handleLogout); @@ -32,23 +39,23 @@ function hashtagView(hashtag) { state.isLoggedIn, getLoginContainer(), "login-template", - createLogin + createLogin, ); document - .querySelector("[data-action='login']") - ?.addEventListener("click", handleLogin); + .querySelector("[data-form='login']") + ?.addEventListener("submit", handleLogin); renderOne( state.currentHashtag, getHeadingContainer(), "heading-template", - createHeading + createHeading, ); renderEach( state.hashtagBlooms || [], getTimelineContainer(), "bloom-template", - createBloom + createBloom, ); } diff --git a/front-end/views/profile.mjs b/front-end/views/profile.mjs index dd2b92a..31139a9 100644 --- a/front-end/views/profile.mjs +++ b/front-end/views/profile.mjs @@ -39,8 +39,8 @@ function profileView(username) { createLogin ); document - .querySelector("[data-action='login']") - ?.addEventListener("click", handleLogin); + .querySelector("[data-form='login']") + ?.addEventListener("submit", handleLogin); const profileData = state.profiles.find((p) => p.username === username); if (profileData) {