import { afterEach, describe, expect, it, vi } from "vitest"; import { readImportedFile, readImportedImageFile, } from "./media-file"; class PendingImage extends EventTarget { static instance: PendingImage | undefined; height = 0; naturalHeight = 0; naturalWidth = 0; width = 0; private currentSrc = ""; constructor() { super(); PendingImage.instance = this; } get src(): string { return this.currentSrc; } set src(value: string) { this.currentSrc = value; } } afterEach(() => { vi.useRealTimers(); vi.unstubAllGlobals(); PendingImage.instance = undefined; Reflect.deleteProperty(URL, "createObjectURL"); Reflect.deleteProperty(URL, "revokeObjectURL"); }); describe("media file cancellation", () => { it("rejects with AbortError when cancellation happens during byte reading", async () => { let resolveBytes!: (value: ArrayBuffer) => void; const file = new File(["notes"], "notes.txt", { type: "text/plain" }); vi.spyOn(file, "arrayBuffer").mockReturnValue( new Promise((resolve) => { resolveBytes = resolve; }), ); const controller = new AbortController(); const result = readImportedFile(file, controller.signal); const outcomePromise = result.then( (value) => ({ kind: "resolved" as const, value }), (error: unknown) => ({ error, kind: "rejected" as const }), ); controller.abort(); resolveBytes(new Uint8Array([1]).buffer); await expect(outcomePromise).resolves.toMatchObject({ error: { name: "AbortError" }, kind: "rejected", }); }); it("clears an active image and its fallback timer when aborted", async () => { vi.useFakeTimers(); vi.stubGlobal("Image", PendingImage); const createObjectURL = vi.fn(() => "blob:test-image"); const revokeObjectURL = vi.fn(); Object.defineProperties(URL, { createObjectURL: { configurable: true, value: createObjectURL }, revokeObjectURL: { configurable: true, value: revokeObjectURL }, }); const controller = new AbortController(); const file = new File(["image"], "image.png", { type: "image/png" }); vi.spyOn(file, "arrayBuffer").mockResolvedValue( new Uint8Array([1, 2, 3]).buffer, ); const result = readImportedImageFile( file, { height: 200, unit: "px", width: 300 }, controller.signal, ); const outcomePromise = result.then( (value) => ({ kind: "resolved" as const, value }), (error: unknown) => ({ error, kind: "rejected" as const }), ); await Promise.resolve(); await Promise.resolve(); expect(PendingImage.instance).toBeDefined(); controller.abort(); await vi.runAllTimersAsync(); const outcome = await outcomePromise; expect(outcome).toMatchObject({ error: { name: "AbortError" }, kind: "rejected", }); expect(PendingImage.instance?.src).toBe(""); expect(revokeObjectURL).toHaveBeenCalledWith("blob:test-image"); expect(vi.getTimerCount()).toBe(0); }); });