import { screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import ForgotPasswordPage from "@/app/forgot-password/page"; import ResetPasswordPage from "@/app/reset-password/page"; import UnbanRequestPage from "@/app/unban-request/page"; import VerifyEmailPage from "@/app/verify-email/page"; import { renderWithQueryClient } from "./test-utils"; let searchParams = new URLSearchParams(); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), useSearchParams: () => searchParams, })); beforeEach(() => { searchParams = new URLSearchParams(); vi.stubGlobal( "fetch", vi.fn().mockResolvedValue({ ok: false, status: 401, json: async () => ({ detail: "Not authenticated" }), }), ); }); describe("VerifyEmailPage", () => { it("shows a validation error for an empty code", async () => { const user = userEvent.setup(); renderWithQueryClient(); await user.click(await screen.findByRole("button", { name: /verify email/i })); expect(await screen.findByText(/^enter the 6-digit code$/i)).toBeInTheDocument(); }); it("shows the email address pulled from the query string", async () => { searchParams = new URLSearchParams({ email: "ada@example.com" }); renderWithQueryClient(); expect(await screen.findByText(/ada@example\.com/i)).toBeInTheDocument(); }); }); describe("ForgotPasswordPage", () => { it("rejects an invalid email", async () => { const user = userEvent.setup(); renderWithQueryClient(); await user.type(screen.getByLabelText(/email/i), "not-an-email"); await user.click(screen.getByRole("button", { name: /send reset code/i })); await waitFor(() => { expect(screen.getByText(/enter a valid email address/i)).toBeInTheDocument(); }); }); it("links back to sign in", async () => { renderWithQueryClient(); expect(await screen.findByRole("link", { name: /back to sign in/i })).toHaveAttribute( "href", "/login", ); }); }); describe("ResetPasswordPage", () => { it("prefills email from the query string", async () => { searchParams = new URLSearchParams({ email: "ada@example.com" }); renderWithQueryClient(); expect(await screen.findByDisplayValue("ada@example.com")).toBeInTheDocument(); }); it("rejects a weak new password", async () => { const user = userEvent.setup(); renderWithQueryClient(); await user.type(screen.getByLabelText(/^email$/i), "ada@example.com"); await user.type(screen.getByLabelText(/reset code/i), "123456"); await user.type(screen.getByLabelText(/new password/i), "allletters"); await user.click(screen.getByRole("button", { name: /reset password/i })); await waitFor(() => { expect( screen.getByText(/must contain at least one letter and one digit/i), ).toBeInTheDocument(); }); }); }); describe("UnbanRequestPage", () => { it("submits and shows a confirmation", async () => { vi.stubGlobal( "fetch", vi.fn().mockResolvedValue({ ok: true, status: 204, json: async () => ({}), }), ); const user = userEvent.setup(); renderWithQueryClient(); await user.type(screen.getByLabelText(/message/i), "This looks like a mistake."); await user.click(screen.getByRole("button", { name: /send request/i })); expect(await screen.findByText(/request received/i)).toBeInTheDocument(); }); it("links back to sign in", async () => { renderWithQueryClient(); expect(await screen.findByRole("link", { name: /back to sign in/i })).toHaveAttribute( "href", "/login", ); }); });