import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { processFiles, processImages } from "#/utils/file-processing"; const reads = vi.hoisted(() => ({ arrayBuffers: [] as string[], dataUrls: [] as string[], })); class ControlledFileReader { onload: (() => void) | null = null; onerror: (() => void) | null = null; onabort: (() => void) | null = null; private finish(file: File) { queueMicrotask(() => { if (file.name.startsWith("error")) { this.onerror?.(); } else if (file.name.startsWith("abort")) { this.onabort?.(); } else { this.onload?.(); } }); } readAsArrayBuffer(file: File) { reads.arrayBuffers.push(file.name); this.finish(file); } readAsDataURL(file: File) { reads.dataUrls.push(file.name); this.finish(file); } } function file(name: string, type = "text/plain") { return new File(["content"], name, { type }); } describe("file processing", () => { beforeEach(() => { reads.arrayBuffers = []; reads.dataUrls = []; vi.stubGlobal("FileReader", ControlledFileReader); }); afterEach(() => { vi.unstubAllGlobals(); }); it("returns successful files in input order while retaining read failures", async () => { const first = file("first.txt"); const failed = file("error-report.txt"); const last = file("last.txt"); const result = await processFiles([first, failed, last]); expect(reads.arrayBuffers).toEqual([ "first.txt", "error-report.txt", "last.txt", ]); expect(result.successful).toEqual([first, last]); expect(result.failed).toEqual([ { file: failed, error: new Error("Failed to read file: error-report.txt"), }, ]); }); it("reports aborted regular-file reads", async () => { const aborted = file("abort-upload.txt"); await expect(processFiles([aborted])).resolves.toEqual({ successful: [], failed: [ { file: aborted, error: new Error("File reading was aborted: abort-upload.txt"), }, ], }); }); it("reads images as data URLs and isolates errors and aborts", async () => { const successful = file("photo.png", "image/png"); const failed = file("error-photo.png", "image/png"); const aborted = file("abort-photo.png", "image/png"); const result = await processImages([successful, failed, aborted]); expect(reads.dataUrls).toEqual([ "photo.png", "error-photo.png", "abort-photo.png", ]); expect(result.successful).toEqual([successful]); expect(result.failed).toEqual([ { file: failed, error: new Error("Failed to read image: error-photo.png"), }, { file: aborted, error: new Error("Image reading was aborted: abort-photo.png"), }, ]); }); it("handles empty file and image batches without creating readers", async () => { await expect(processFiles([])).resolves.toEqual({ successful: [], failed: [], }); await expect(processImages([])).resolves.toEqual({ successful: [], failed: [], }); expect(reads.arrayBuffers).toEqual([]); expect(reads.dataUrls).toEqual([]); }); });