import { afterEach, describe, expect, it, vi } from "vitest"; import { uploadFile, type UploadProgress, type UploadTransport, } from "../src/react/index.js"; class FakeUploadTarget { onprogress: ((event: ProgressEvent) => void) | null = null; } class FakeXMLHttpRequest { static latest: FakeXMLHttpRequest | undefined; readonly upload = new FakeUploadTarget(); readonly headers: Record = {}; status = 0; method = ""; url = ""; body: unknown; onload: (() => void) | null = null; onerror: (() => void) | null = null; onabort: (() => void) | null = null; constructor() { FakeXMLHttpRequest.latest = this; } open(method: string, url: string): void { this.method = method; this.url = url; } setRequestHeader(name: string, value: string): void { this.headers[name.toLowerCase()] = value; } send(body: unknown): void { this.body = body; const size = (body as { size?: number }).size ?? 0; this.upload.onprogress?.({ loaded: size } as ProgressEvent); this.status = 200; this.onload?.(); } abort(): void { this.onabort?.(); } } describe("direct React upload helper", () => { const original = globalThis.XMLHttpRequest; afterEach(() => { globalThis.XMLHttpRequest = original; FakeXMLHttpRequest.latest = undefined; }); it("PUTs the file to object storage, reports progress, then completes", async () => { globalThis.XMLHttpRequest = FakeXMLHttpRequest as unknown as typeof XMLHttpRequest; const file = { name: "photo.png", type: "image/png", size: 4 } as File; const progress: UploadProgress[] = []; const transport: UploadTransport = { createUpload: vi.fn().mockResolvedValue({ id: "upl_1", uploadUrl: "https://objects.example/signed-put", method: "PUT", requiredHeaders: { "content-type": "image/png", "if-none-match": "*" }, expiresAt: "2026-07-15T00:10:00.000Z", }), completeUpload: vi.fn().mockResolvedValue({ id: "ast_1", publicId: "file_1", url: "https://stuffbox.example/f/file_1", filename: "photo.png", mimeType: "image/png", byteSize: 4, status: "active", createdAt: "2026-07-15T00:00:00.000Z", }), }; const asset = await uploadFile(transport, file, { onProgress: (value) => progress.push(value), }); expect(asset.id).toBe("ast_1"); expect(transport.createUpload).toHaveBeenCalledWith( { filename: "photo.png", mimeType: "image/png", size: 4 }, {}, ); expect(FakeXMLHttpRequest.latest).toMatchObject({ method: "PUT", url: "https://objects.example/signed-put", body: file, headers: { "content-type": "image/png", "if-none-match": "*" }, }); expect(transport.completeUpload).toHaveBeenCalledWith("upl_1", undefined, {}); expect(progress.map((value) => value.phase)).toEqual([ "creating", "uploading", "uploading", "uploading", "completing", "complete", ]); expect(progress.at(-1)?.percent).toBe(100); }); });