import { describe, it, expect, vi } from "vitest"; vi.mock("~/lib/auth.server", () => ({ auth: { api: { getSession: vi.fn() } }, })); vi.mock("~/lib/cloudinary.server", () => ({ deleteCloudinaryImageByUrl: vi.fn() })); vi.mock("~/lib/email.server", () => ({ sendEmail: vi.fn(), wrapInEmailTemplate: vi.fn(), emailParagraph: vi.fn(), escapeHtml: vi.fn(), })); vi.mock("~/models/user", () => ({ findUserById: vi.fn(), findUserByUsername: vi.fn(), isUserInActiveDraft: vi.fn(), updateUser: vi.fn(), anonymizeUserAccount: vi.fn(), USERNAME_RE: /^[a-zA-Z0-9_-]{3,30}$/, })); vi.mock("~/models/account", () => ({ findLinkedAccountsByUserId: vi.fn() })); vi.mock("~/models/team", () => ({ findTeamsByOwnerId: vi.fn(), removeTeamOwner: vi.fn() })); vi.mock("~/models/commissioner", () => ({ removeAllCommissionersByUserId: vi.fn() })); import { loader, shouldRevalidate } from "../settings"; import { auth } from "~/lib/auth.server"; type RevalidateArgs = Parameters[0]; function revalidateArgs(overrides: Partial): RevalidateArgs { return { currentParams: {}, nextParams: {}, formMethod: undefined, defaultShouldRevalidate: true, ...overrides, } as unknown as RevalidateArgs; } type LoaderArgs = Parameters[0]; function loaderArgs(section?: string): LoaderArgs { const path = section ? `/settings/${section}` : "/settings"; return { params: { section }, request: new Request(`http://localhost${path}`), context: {}, } as unknown as LoaderArgs; } describe("/settings loader section handling", () => { it("redirects an unknown section back to /settings", async () => { const result = (await loader(loaderArgs("bogus"))) as Response; expect(result).toBeInstanceOf(Response); expect(result.status).toBe(302); expect(result.headers.get("Location")).toBe("/settings"); // The bad section short-circuits before we ever touch the session. expect(auth.api.getSession).not.toHaveBeenCalled(); }); it("does not redirect a valid section before auth runs", async () => { vi.mocked(auth.api.getSession).mockResolvedValue(null as never); const result = (await loader(loaderArgs("account"))) as Response; // Falls through to the auth check, which redirects to login (not /settings). expect(result.headers.get("Location")).toBe("/login?redirectTo=/settings"); expect(auth.api.getSession).toHaveBeenCalled(); }); }); describe("/settings shouldRevalidate", () => { it("skips revalidation when only the section param changes", () => { expect( shouldRevalidate( revalidateArgs({ currentParams: { section: "profile" }, nextParams: { section: "account" } }) ) ).toBe(false); }); it("revalidates after a mutation regardless of section", () => { expect( shouldRevalidate( revalidateArgs({ currentParams: { section: "profile" }, nextParams: { section: "account" }, formMethod: "POST", }) ) ).toBe(true); }); it("defers to the default when the section is unchanged", () => { expect( shouldRevalidate( revalidateArgs({ currentParams: { section: "account" }, nextParams: { section: "account" }, defaultShouldRevalidate: true, }) ) ).toBe(true); }); });