84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
// @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" });
|
|
});
|
|
});
|