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",
);
});
});