import { describe, expect, test } from "vitest"; import { screen, waitFor } from "@testing-library/react"; import { userEvent } from "@testing-library/user-event"; import { calls, fail, route } from "../fetch.ts"; import { currentPath, renderPage } from "../render.tsx"; describe("ResetPassword", () => { function setup(search = "?token=rst") { const user = userEvent.setup(); renderPage(`/reset-password${search}`); return user; } test("without a token, it asks for a link by email", async () => { route("POST /api/password-resets", () => ({})); const user = setup(""); await user.type( await screen.findByPlaceholderText("Your email address"), " a@b.c {Enter}", ); await screen.findByText(/you'll receive a reset link/); expect(calls("POST /api/password-resets")[0].body).toEqual({ email: "a@b.c", }); expect(screen.getByRole("link", { name: "Back to login" })).toHaveAttribute( "href", "/login", ); }); test("a failed request is reported, not claimed as sent", async () => { route("POST /api/password-resets", () => fail("Internal error", 500)); const user = setup(""); await user.type( await screen.findByPlaceholderText("Your email address"), "a@b.c{Enter}", ); expect(await screen.findByRole("alert")).toHaveTextContent( "Request failed", ); expect(screen.queryByText(/you'll receive a reset link/)).not .toBeInTheDocument(); }); test("the two passwords must match", async () => { const user = setup(); await user.type( await screen.findByPlaceholderText("New password"), "longenough", ); await user.type( screen.getByPlaceholderText("Confirm new password"), "different", ); expect(await screen.findByText("Passwords do not match")) .toBeInTheDocument(); await user.click(screen.getByRole("button", { name: "Set new password" })); expect(calls("PUT /api/password-resets/:token")).toHaveLength(0); }); test("sets the password, then offers login", async () => { route("PUT /api/password-resets/:token", () => ({})); const user = setup(); await user.type( await screen.findByPlaceholderText("New password"), "longenough", ); await user.type( screen.getByPlaceholderText("Confirm new password"), "longenough{Enter}", ); await screen.findByRole("heading", { name: "Password updated" }); const [reset] = calls("PUT /api/password-resets/:token"); expect(reset.params.token).toBe("rst"); expect(reset.body).toEqual({ newPassword: "longenough", }); await user.click(screen.getByRole("button", { name: "Go to login" })); await waitFor(() => expect(currentPath()).toBe("/login")); }); test("an expired token is reported", async () => { route( "PUT /api/password-resets/:token", () => fail("Reset link expired", 400), ); const user = setup(); await user.type( await screen.findByPlaceholderText("New password"), "longenough", ); await user.type( screen.getByPlaceholderText("Confirm new password"), "longenough{Enter}", ); expect(await screen.findByRole("alert")).toHaveTextContent( "Reset link expired", ); }); });