import { expect, test } from "../support/browser/strict-browser-test.ts"; test("selects, inspects, streams, and previews a real browser File", async ({ page, }) => { await page.goto("/"); await page.evaluate(async () => { const modulePath = "/src/adapters/browser-files/index.ts"; const { createBrowserFileRuntime, browserFilePolicyReference, } = await import(/* @vite-ignore */ modulePath); const input = document.createElement("input"); input.id = "capability-file-input"; input.type = "file"; const label = document.createElement("label"); label.htmlFor = input.id; label.textContent = "PNG file"; const button = document.createElement("button"); button.type = "button"; button.textContent = "Choose test file"; document.body.append(label, input, button); const filePolicy = browserFilePolicyReference( "avatar", "select-inspect-preview-png", ); const runtime = createBrowserFileRuntime({ input, policies: [ { reference: filePolicy, selection: { policyId: "avatar-v1", purpose: "avatar", classification: "PERSONAL", multiple: false, maxCount: 1, maxFileBytes: 1024, maxTotalBytes: 1024, allowEmpty: false, accept: [ { mediaType: "image/png", extensions: [".png"], }, ], }, inspection: { policyId: "png-signature-v1", maxInspectionBytes: 16, acceptedSignatures: [ { mediaType: "image/png", extensions: [".png"], patterns: [ { offset: 0, bytes: [137, 80, 78, 71, 13, 10, 26, 10], }, ], }, ], }, preview: { allowedMediaTypes: ["image/png"], maxPreviewBytes: 1024, }, }, ], limits: { hardMaxPreviewBytes: 1024, hardMaxObjectUrlBytes: 1024, hardMaxTransferBytes: 4096, }, download: { host: Object.freeze({ handoff() {} }), browserManagedCapabilities: { resolve() { return { ok: false as const, error: { code: "POLICY_REJECTED" as const, operation: "DOWNLOAD" as const, retryable: false, recovery: "NONE" as const, }, }; }, }, }, }); const state = globalThis as typeof globalThis & { __browserFileResult?: Promise; }; button.addEventListener("click", () => { state.__browserFileResult = (async () => { const selected = await runtime.baselinePicker.select({ policy: filePolicy, }); if ( !selected.ok || selected.value.kind !== "SELECTED" || selected.value.files.length !== 1 ) { runtime.dispose(); return selected; } const candidate = selected.value.files[0]!; const signal = new AbortController().signal; const inspected = await runtime.content.inspect({ ref: candidate.ref, policy: filePolicy, signal, }); const range = await runtime.content.readRange({ ref: candidate.ref, offset: 1, length: 4, signal, }); const opened = await runtime.content.openSource({ ref: candidate.ref, signal, }); const streamed: number[] = []; if (opened.ok) { for await (const chunk of opened.value.stream(signal)) { if (chunk.ok) streamed.push(...chunk.value); } } const verificationReceipt = inspected.ok ? inspected.value.verificationReceipt : null; if (!verificationReceipt) { runtime.dispose(); return inspected; } const preview = await runtime.previews.create({ ref: candidate.ref, verificationReceipt, policy: filePolicy, maxPreviewBytes: 1024, signal, }); let previewBytes: number[] = []; let previewProtocol: string | null = null; if (preview.ok) { previewProtocol = new URL(preview.value.url).protocol; previewBytes = Array.from( new Uint8Array(await (await fetch(preview.value.url)).arrayBuffer()), ); preview.value.release(); } runtime.baselinePicker.release(candidate.ref); runtime.dispose(); return { selected, inspected, range: range.ok ? Array.from(range.value) : range, streamed, preview: preview.ok ? { ok: true } : preview, previewBytes, previewProtocol, }; })(); }); }); const chooserPromise = page.waitForEvent("filechooser"); await page.getByRole("button", { name: "Choose test file" }).click(); const chooser = await chooserPromise; const bytes = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10, 1, 2, 3, 4]); await chooser.setFiles({ name: "sample.png", mimeType: "image/png", buffer: bytes, }); const result = await page.evaluate(async () => { const state = globalThis as typeof globalThis & { __browserFileResult?: Promise; }; return await state.__browserFileResult; }); expect(result).toMatchObject({ selected: { ok: true, value: { kind: "SELECTED", files: [{ sizeBytes: bytes.byteLength, source: "NATIVE_INPUT" }], }, }, inspected: { ok: true, value: { byteLength: bytes.byteLength, detectedMediaType: "image/png", signature: "MATCHED", }, }, range: [80, 78, 71, 13], streamed: Array.from(bytes), preview: { ok: true }, previewBytes: Array.from(bytes), previewProtocol: "blob:", }); }); test("hands a bounded generated Blob to the real browser download path", async ({ page, }) => { await page.goto("/"); await page.evaluate(async () => { const modulePath = "/src/adapters/browser-files/index.ts"; const { createBrowserFileRuntime, createAnchorDownloadHost, browserFilePolicyReference, } = await import(/* @vite-ignore */ modulePath); const input = document.createElement("input"); input.type = "file"; input.id = "unused-download-input"; const label = document.createElement("label"); label.htmlFor = input.id; label.textContent = "Unused file control"; const button = document.createElement("button"); button.type = "button"; button.textContent = "Download generated file"; document.body.append(label, input, button); const downloadPolicy = browserFilePolicyReference( "generated-export", "bounded-object-url-bin", ); const runtime = createBrowserFileRuntime({ input, policies: [ { reference: downloadPolicy, download: { strategy: "BOUNDED_OBJECT_URL", mediaType: "application/octet-stream", safeExtension: ".bin", maxTransferBytes: 1024, maxBufferedBytes: 1024, integrity: "OPTIONAL", }, }, ], limits: { hardMaxPreviewBytes: 1024, hardMaxObjectUrlBytes: 1024, hardMaxTransferBytes: 4096, }, download: { host: createAnchorDownloadHost(document), browserManagedCapabilities: { resolve() { return { ok: false as const, error: { code: "POLICY_REJECTED" as const, operation: "DOWNLOAD" as const, retryable: false, recovery: "NONE" as const, }, }; }, }, }, }); const state = globalThis as typeof globalThis & { __browserDownloadResult?: Promise; }; button.addEventListener("click", () => { state.__browserDownloadResult = runtime.downloads .deliver({ policy: downloadPolicy, source: { kind: "GENERATED", bytes: { byteLength: 5, async *stream() { yield { ok: true as const, value: new Uint8Array([1, 2]), }; yield { ok: true as const, value: new Uint8Array([3, 4, 5]), }; }, }, }, suggestedFileName: "report", maxTransferBytes: 1024, maxBufferedBytes: 1024, signal: new AbortController().signal, onProgress() {}, }) .finally(() => runtime.dispose()); }); }); const downloadPromise = page.waitForEvent("download"); await page .getByRole("button", { name: "Download generated file" }) .click(); const download = await downloadPromise; const stream = await download.createReadStream(); const chunks: Buffer[] = []; for await (const chunk of stream) { chunks.push(Buffer.from(chunk)); } const result = await page.evaluate(async () => { const state = globalThis as typeof globalThis & { __browserDownloadResult?: Promise; }; return await state.__browserDownloadResult; }); expect(download.suggestedFilename()).toBe("report.bin"); expect(Buffer.concat(chunks)).toEqual(Buffer.from([1, 2, 3, 4, 5])); expect(result).toMatchObject({ ok: true, value: { kind: "BROWSER_HANDOFF", }, }); });