Files
siftlode/frontend/src/lib/api/diagnostics.dom.test.ts
T

57 lines
2.4 KiB
TypeScript

// @vitest-environment jsdom
//
// The zip export is the one api method that reads a RAW response instead of a parsed answer, so it
// carries contract of its own: the bytes, and the name the server picked. The composer's DOM test
// mocks this method wholesale, so without this the blob and the filename parse ship untested — and
// a broken header regex would silently save every report under the fallback name.
import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("../errorDialog", () => ({ reportError: vi.fn() }));
vi.mock("../notifications", async (importOriginal) => ({
...(await importOriginal<typeof import("../notifications")>()),
notify: vi.fn(() => 1),
remove: vi.fn(),
getActiveToasts: vi.fn((): { id: number }[] => []),
}));
vi.mock("../../i18n", () => ({ default: { t: (k: string) => k, language: "en" } }));
import { debugSessionsApi } from "./diagnostics";
const zip = (disposition?: string) =>
new Response("PK", {
status: 200,
headers: {
"content-type": "application/zip",
...(disposition ? { "content-disposition": disposition } : {}),
},
});
beforeEach(() => vi.restoreAllMocks());
describe("debugExport", () => {
it("hands back the bytes and the name the server chose", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
zip('attachment; filename="siftlode-debug-20260919-1402-abc123.zip"'),
);
const out = await debugSessionsApi.debugExport("0123456789abcdef");
expect(out.name).toBe("siftlode-debug-20260919-1402-abc123.zip");
// The body is read as bytes, not parsed: `r.json()` on a zip would throw on its first one.
// Brand, not `toBeInstanceOf(Blob)` — that asks which realm built the body: under jsdom the
// ambient `Blob` is jsdom's class, while `Response.blob()` returns node's own on Node 22 and
// jsdom's on Node 24, so the instance check passed locally and failed on the server gate.
expect(Object.prototype.toString.call(out.blob)).toBe("[object Blob]");
expect(out.blob.size).toBe(4);
});
it("names the file itself when the answer does not", async () => {
// A proxy that strips the header must not leave the browser saving something called "export".
vi.spyOn(globalThis, "fetch").mockResolvedValue(zip());
const out = await debugSessionsApi.debugExport("0123456789abcdef");
expect(out.name).toBe("siftlode-debug-0123456789abcdef.zip");
});
});