From d625cbb1fbf4e4c1b10b4931206969aa3591e11c Mon Sep 17 00:00:00 2001 From: "Ramvignesh B." Date: Thu, 7 May 2026 23:54:12 +0000 Subject: [PATCH] test: use testids instead of text queries for unit tests (#5) Co-authored-by: me Reviewed-on: https://git.ramvignesh.dev/me/pi-ku/pulls/5 --- frontend/src/components/RouteGuards.test.tsx | 36 +- frontend/src/components/SplashScreen.tsx | 2 +- .../src/components/drawer/DrawerSection.tsx | 1 + .../src/components/drawer/PasskeyModal.tsx | 2 +- .../src/components/reader/EnvelopeReveal.tsx | 2 +- frontend/src/components/ui/LogModal.tsx | 2 +- frontend/src/pages/Drawer.test.tsx | 12 +- frontend/src/pages/Drawer.tsx | 347 +++++++++--------- frontend/src/pages/Editor.test.tsx | 18 +- frontend/src/pages/Login.test.tsx | 9 +- frontend/src/pages/Login.tsx | 2 +- frontend/src/pages/Reader.test.tsx | 10 +- 12 files changed, 220 insertions(+), 223 deletions(-) diff --git a/frontend/src/components/RouteGuards.test.tsx b/frontend/src/components/RouteGuards.test.tsx index c0d59a7..6914576 100644 --- a/frontend/src/components/RouteGuards.test.tsx +++ b/frontend/src/components/RouteGuards.test.tsx @@ -9,8 +9,8 @@ function renderGuard(ui: React.ReactNode, mountPath: "/protected" | "/public") { return render( - Login Page} /> - Drawer Page} /> + Login Page} /> + Drawer Page} /> @@ -35,13 +35,13 @@ describe("ProtectedRoute", () => { }); renderGuard( -
Secret
+
Secret
, "/protected", ); - expect(screen.getByText(/Unsealing/i)).toBeInTheDocument(); - expect(screen.queryByText("Secret")).not.toBeInTheDocument(); + expect(screen.getByTestId("splash-screen")).toBeInTheDocument(); + expect(screen.queryByTestId("secret-page")).not.toBeInTheDocument(); }); it("should redirect unauthenticated users to /login", () => { @@ -52,12 +52,12 @@ describe("ProtectedRoute", () => { }); renderGuard( -
Secret
+
Secret
, "/protected", ); - expect(screen.getByText("Login Page")).toBeInTheDocument(); - expect(screen.queryByText("Secret")).not.toBeInTheDocument(); + expect(screen.getByTestId("login-page")).toBeInTheDocument(); + expect(screen.queryByTestId("secret-page")).not.toBeInTheDocument(); }); it("should render page for authenticated users", () => { @@ -68,12 +68,12 @@ describe("ProtectedRoute", () => { }); renderGuard( -
Secret
+
Secret
, "/protected", ); - expect(screen.getByText("Secret")).toBeInTheDocument(); + expect(screen.getByTestId("secret-page")).toBeInTheDocument(); }); }); @@ -86,12 +86,12 @@ describe("PublicRoute", () => { }); renderGuard( -
Login Page
+
Login Page
, "/public", ); - expect(screen.getByText(/Unsealing/i)).toBeInTheDocument(); - expect(screen.queryByText("Login Page")).not.toBeInTheDocument(); + expect(screen.getByTestId("splash-screen")).toBeInTheDocument(); + expect(screen.queryByTestId("mock-login-page")).not.toBeInTheDocument(); }); it("should redirect authenticated users to /drawer", () => { @@ -102,12 +102,12 @@ describe("PublicRoute", () => { }); renderGuard( -
Login Form
+
Login Form
, "/public", ); - expect(screen.getByText("Drawer Page")).toBeInTheDocument(); - expect(screen.queryByText("Login Form")).not.toBeInTheDocument(); + expect(screen.getByTestId("drawer-page")).toBeInTheDocument(); + expect(screen.queryByTestId("login-form")).not.toBeInTheDocument(); }); it("should render page for unauthenticated users", () => { @@ -118,10 +118,10 @@ describe("PublicRoute", () => { }); renderGuard( -
Login Form
+
Login Form
, "/public", ); - expect(screen.getByText("Login Form")).toBeInTheDocument(); + expect(screen.getByTestId("login-form")).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/SplashScreen.tsx b/frontend/src/components/SplashScreen.tsx index ca6ce1a..7c4297e 100644 --- a/frontend/src/components/SplashScreen.tsx +++ b/frontend/src/components/SplashScreen.tsx @@ -3,7 +3,7 @@ import Logo from "./Logo"; export default function SplashScreen() { return ( -
+
diff --git a/frontend/src/components/drawer/DrawerSection.tsx b/frontend/src/components/drawer/DrawerSection.tsx index f189bf3..50b44ca 100644 --- a/frontend/src/components/drawer/DrawerSection.tsx +++ b/frontend/src/components/drawer/DrawerSection.tsx @@ -40,6 +40,7 @@ export function DrawerSection({ >
-

+

You've been away a while.

diff --git a/frontend/src/components/reader/EnvelopeReveal.tsx b/frontend/src/components/reader/EnvelopeReveal.tsx index c76cf32..a830307 100644 --- a/frontend/src/components/reader/EnvelopeReveal.tsx +++ b/frontend/src/components/reader/EnvelopeReveal.tsx @@ -123,7 +123,7 @@ export function EnvelopeReveal({ to -

{recipient}

+

{recipient}

{date}

)} - {message} + {message} {log && ( <>
diff --git a/frontend/src/pages/Drawer.test.tsx b/frontend/src/pages/Drawer.test.tsx index b79aed3..f331659 100644 --- a/frontend/src/pages/Drawer.test.tsx +++ b/frontend/src/pages/Drawer.test.tsx @@ -48,10 +48,10 @@ describe("Drawer Page", () => { , ); - expect(screen.getByText(/Drafts/i)).toBeInTheDocument(); - expect(screen.getAllByText(/Kept/i).length).toBeGreaterThanOrEqual(1); - expect(screen.getByText(/Vault/i)).toBeInTheDocument(); - expect(screen.getByText(/This drawer remains silent/i)).toBeInTheDocument(); + expect(screen.getByTestId("drawer-section-drafts")).toBeInTheDocument(); + expect(screen.getAllByTestId("drawer-section-title").length).toBeGreaterThanOrEqual(1); + expect(screen.getByTestId("drawer-section-vault")).toBeInTheDocument(); + expect(screen.getByTestId("empty-drawer-message-drafts")).toBeInTheDocument(); }); it("renders the loading state", () => { @@ -70,7 +70,7 @@ describe("Drawer Page", () => { , ); - expect(screen.getByText(/Opening your cabinet/i)).toBeInTheDocument(); + expect(screen.getByTestId("drawer-loading-state")).toBeInTheDocument(); }); it("renders the authentication required modal when api requires auth", () => { @@ -89,7 +89,7 @@ describe("Drawer Page", () => { , ); - expect(screen.getByText(/You've been away a while./i)).toBeInTheDocument(); + expect(screen.getByTestId("passkey-modal-title")).toBeInTheDocument(); expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument(); }); diff --git a/frontend/src/pages/Drawer.tsx b/frontend/src/pages/Drawer.tsx index c0e65ac..03175e9 100644 --- a/frontend/src/pages/Drawer.tsx +++ b/frontend/src/pages/Drawer.tsx @@ -11,185 +11,190 @@ import { PATHS } from "../config/routes"; import { useAuth } from "../hooks/useAuth"; import { useLetters } from "../hooks/useLetters"; import { - formatRelativeDate, - formatRelativeDateWithoutTime, + formatRelativeDate, + formatRelativeDateWithoutTime, } from "../utils/dateFormat.ts"; export default function Drawer() { - const { user, logout } = useAuth(); + const { user, logout } = useAuth(); - const [openSection, setOpenSection] = useState(null); - const navigate = useNavigate(); - const location = useLocation(); - const [showWelcomeLetter, setShowWelcomeLetter] = useState( - !!location.state?.firstTime, - ); - const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters(); + const [openSection, setOpenSection] = useState(null); + const navigate = useNavigate(); + const location = useLocation(); + const [showWelcomeLetter, setShowWelcomeLetter] = useState( + !!location.state?.firstTime, + ); + const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters(); - if (!user) return null; + if (!user) return null; - const toggleSection = (id: string) => - setOpenSection(openSection === id ? null : id); + const toggleSection = (id: string) => + setOpenSection(openSection === id ? null : id); - return ( -
-
+ return ( +
+
- {showWelcomeLetter && ( - { - setShowWelcomeLetter(false); - navigate(location.pathname, { replace: true, state: {} }); - }} - /> - )} + {showWelcomeLetter && ( + { + setShowWelcomeLetter(false); + navigate(location.pathname, { replace: true, state: {} }); + }} + /> + )} - {isAuthRequired && } -
- -
- Personal Archive + {isAuthRequired && } +
+ +
+ Personal Archive +
+
+ Welcome Back{" "} + {user.full_name} + +
+
+ +
+ {loading ? ( +
+ + + Opening your cabinet... + +
+ ) : ( + <> + toggleSection("drafts")} + > + {drafts.map((draft) => ( + + ))} + {drafts.length === 0 && ( +

+ This drawer remains silent +

+ )} +
+ + toggleSection("kept")} + > + {kept.map((letter) => ( + + ))} + + toggleSection("sent")} + > + {sent.map((letter) => ( + + ))} + {sent.length === 0 && ( +

+ This drawer remains silent +

+ )} +
+ toggleSection("vault")} + > + {vault.map((letter) => ( + new Date().toISOString()} + /> + ))} + + + )} +
+ + + +
+ For your unsaid. +
+ {!showWelcomeLetter && ( +
+ +
+ )}
-
- Welcome Back{" "} - {user.full_name} - -
-
- -
- {loading ? ( -
- - - Opening your cabinet... - -
- ) : ( - <> - toggleSection("drafts")} - > - {drafts.map((draft) => ( - - ))} - - - toggleSection("kept")} - > - {kept.map((letter) => ( - - ))} - - toggleSection("sent")} - > - {sent.map((letter) => ( - - ))} - {sent.length === 0 && ( -

- This drawer remains silent -

- )} -
- toggleSection("vault")} - > - {vault.map((letter) => ( - new Date().toISOString()} - /> - ))} - - - )} -
- - - -
- For your unsaid. -
- {!showWelcomeLetter && ( -
- -
- )} -
- ); + ); } diff --git a/frontend/src/pages/Editor.test.tsx b/frontend/src/pages/Editor.test.tsx index d1e63c4..6b32945 100644 --- a/frontend/src/pages/Editor.test.tsx +++ b/frontend/src/pages/Editor.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react"; import { HttpResponse, http } from "msw"; import { MemoryRouter, Route, Routes } from "react-router-dom"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -79,9 +79,7 @@ describe("Editor Page", () => { ); // Wait for initial load to complete - await waitFor(() => { - expect(screen.queryByText(/Opening your draft/i)).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(() => screen.queryByTestId("opening-draft-overlay")); const canvas = screen.getByTestId("canvas"); expect(canvas.getAttribute("data-readonly")).toBe("false"); @@ -107,9 +105,7 @@ describe("Editor Page", () => { fireEvent.click(confirmVaultBtn); // Wait for save to complete and check readOnly - await waitFor(() => { - expect(screen.getByText(/Your letter is saved/i)).toBeInTheDocument(); - }); + expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument(); expect(canvas.getAttribute("data-readonly")).toBe("true"); expect(screen.getByLabelText(/recipient/i)).toBeDisabled(); @@ -140,9 +136,7 @@ describe("Editor Page", () => { , ); - await waitFor(() => { - expect(screen.queryByText(/Opening your draft/i)).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(() => screen.queryByTestId("opening-draft-overlay")); const canvas = screen.getByTestId("canvas"); @@ -156,9 +150,7 @@ describe("Editor Page", () => { if (!secondarySealBtn) throw new Error("Secondary seal button not found"); fireEvent.click(secondarySealBtn); - await waitFor(() => { - expect(screen.getByText(/Your letter is saved/i)).toBeInTheDocument(); - }); + expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument(); expect(canvas.getAttribute("data-readonly")).toBe("true"); expect(screen.getByLabelText(/recipient/i)).toBeDisabled(); diff --git a/frontend/src/pages/Login.test.tsx b/frontend/src/pages/Login.test.tsx index c6f4af3..7f58b75 100644 --- a/frontend/src/pages/Login.test.tsx +++ b/frontend/src/pages/Login.test.tsx @@ -31,7 +31,7 @@ describe("Login Page", () => { await userEvent.type(screen.getByLabelText(/password/i), "password123"); await userEvent.click(screen.getByRole("button", { name: /sign in/i })); - expect(await screen.findByText(/technical issues/i)).toBeInTheDocument(); + expect(await screen.findByTestId("login-error-message")).toHaveTextContent(/technical issues/i); }); it.each([ @@ -73,8 +73,8 @@ describe("Login Page", () => { > } /> - Drawer
} /> - Reader
} /> + Drawer
} /> + Reader
} /> , ); @@ -83,6 +83,7 @@ describe("Login Page", () => { await userEvent.type(screen.getByLabelText(/password/i), "password123"); await userEvent.click(screen.getByRole("button", { name: /sign in/i })); - expect(await screen.findByText(nextRoute)).toBeInTheDocument(); + const expectedTestId = nextRoute.toLowerCase() === "drawer" ? "drawer-page" : "reader-page"; + expect(await screen.findByTestId(expectedTestId)).toBeInTheDocument(); }); }); diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index c5de9c9..4ed7089 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -89,7 +89,7 @@ export default function Login() { {apiError && (
- {apiError} + {apiError}
)} diff --git a/frontend/src/pages/Reader.test.tsx b/frontend/src/pages/Reader.test.tsx index 8078e1b..50164b1 100644 --- a/frontend/src/pages/Reader.test.tsx +++ b/frontend/src/pages/Reader.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, waitFor } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import { HttpResponse, http } from "msw"; import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -76,9 +76,7 @@ describe("Reader Page", () => { , ); - await waitFor(() => { - expect(screen.getByText(/Guest/i)).toBeInTheDocument(); - }); + expect(await screen.findByTestId("envelope-recipient")).toHaveTextContent(/Guest/i); }); it("should display an error message if the server request fails", async () => { @@ -100,8 +98,8 @@ describe("Reader Page", () => { ); expect( - await screen.findByText(/Failed to load letter/i), - ).toBeInTheDocument(); + await screen.findByTestId("log-modal-message"), + ).toHaveTextContent(/Failed to load letter/i); }); it("should navigate to the login page with redirect url when the letter has no sharing key and the user is not logged in", async () => {