// @vitest-environment happy-dom import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; describe("auth/api fetch wrapper", () => { let originalFetch: typeof globalThis.fetch; let originalLocation: Location; beforeEach(() => { originalFetch = globalThis.fetch; originalLocation = window.location; delete (window as any).__navCalls; (window as any).__navCalls = []; }); afterEach(() => { globalThis.fetch = originalFetch; window.location = originalLocation as any; }); it("redirects to /login on 401 response", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: false, status: 401, statusText: "Unauthorized", headers: new Headers(), json: async () => ({ error: "session_expired" }), } as unknown as Response) as unknown as typeof globalThis.fetch; // Stub window.location.href setter to capture navigation without // actually navigating (jsdom does not implement location.href assignment). let href = originalLocation.href; Object.defineProperty(window, "location", { configurable: true, get: () => ({ ...originalLocation, get href() { return href; }, set href(v: string) { (window as any).__navCalls.push(v); href = v; }, }), }); const { authedFetch } = await import("./api"); await expect(authedFetch("/api/anything")).rejects.toThrow(); expect((window as any).__navCalls).toContainEqual(expect.stringContaining("/login")); }); it("does NOT redirect on 403", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: false, status: 403, statusText: "Forbidden", headers: new Headers(), json: async () => ({ error: "forbidden" }), } as unknown as Response) as unknown as typeof globalThis.fetch; Object.defineProperty(window, "location", { configurable: true, get: () => ({ ...originalLocation, set href(_: string) { throw new Error("should not nav"); }, }), }); const { authedFetch } = await import("./api"); await expect(authedFetch("/api/anything")).rejects.toThrow(); }); it("returns parsed JSON on 200", async () => { globalThis.fetch = vi.fn().mockResolvedValue({ ok: true, status: 200, headers: new Headers(), json: async () => ({ hello: "world" }), } as unknown as Response) as unknown as typeof globalThis.fetch; const { authedFetch } = await import("./api"); const data = await authedFetch("/api/anything"); expect(data).toEqual({ hello: "world" }); }); });