// @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", () => ({ 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"); }); });