import { expect, test, type Page } from "@playwright/test"; import { pngFixture, tiffFixture } from "../fixtures/images"; const ORIGIN = "http://127.0.0.1:4173"; async function localOnly(page: Page) { const external: string[] = []; await page.route("**/*", async (route) => { const url = new URL(route.request().url()); if (url.origin !== ORIGIN) { external.push(url.href); await route.abort(); } else await route.continue(); }); return external; } test("runs from a nested path without external requests", async ({ page }) => { const errors: string[] = []; page.on("pageerror", (error) => errors.push(error.message)); page.on("console", (message) => { if (message.type() === "error") errors.push(message.text()); }); const external = await localOnly(page); await page.goto("/deep/nested/privacy/"); await expect( page.getByRole("heading", { name: "Privacy Tools" }), ).toBeVisible(); expect(external).toEqual([]); expect(errors).toEqual([]); }); test("serves the release identity and hardened headers", async ({ request, }) => { const index = await request.get("/deep/nested/privacy/"); expect(index.ok()).toBe(true); expect(index.headers()["content-security-policy"]).toContain( "default-src 'self'", ); expect(await index.text()).not.toMatch(/\b(?:src|href)=["']\//u); const manifest = await request.get("/deep/nested/privacy/toolbox-app.json"); await expect(manifest.json()).resolves.toMatchObject({ id: "de.add-ideas.privacy-tools", version: "0.1.0", entry: "./", }); }); test("inspects and independently verifies a re-encoded PNG without network access", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (error) => errors.push(error.message)); page.on("console", (message) => { if (message.type() === "error") errors.push(message.text()); }); const external = await localOnly(page); await page.goto("/deep/nested/privacy/"); await page.locator('input[type="file"]').setInputFiles([ { name: "metadata-fixture.png", mimeType: "image/png", buffer: Buffer.from(pngFixture()), }, { name: "inventory-only.pdf", mimeType: "application/pdf", buffer: Buffer.from("%PDF-1.7\n% fixture"), }, ]); await expect( page.getByRole("heading", { name: "Batch inventory" }), ).toBeVisible(); await expect(page.getByText("Alice PNG").first()).toBeVisible(); await expect(page.getByText("PNG Alice").first()).toBeVisible(); await expect(page.getByText("inventory-only.pdf").first()).toBeVisible(); await page.getByRole("button", { name: "Re-encode & verify" }).click(); await expect(page.getByText("Mandatory output re-scan")).toBeVisible(); await expect(page.locator(".verification")).not.toHaveClass(/is-failed/u); await expect( page.getByRole("button", { name: "Download re-encoded output" }), ).toBeEnabled(); const imageDownload = page.waitForEvent("download"); await page .getByRole("button", { name: "Download re-encoded output" }) .click(); expect((await imageDownload).suggestedFilename()).toBe( "metadata-fixture.clean.png", ); const reportDownload = page.waitForEvent("download"); await page.getByRole("button", { name: "Download JSON report" }).click(); expect((await reportDownload).suggestedFilename()).toBe( "privacy-tools-report.json", ); const archiveDownload = page.waitForEvent("download"); await page .getByRole("button", { name: "Download 1 re-encoded image + report" }) .click(); expect((await archiveDownload).suggestedFilename()).toBe( "privacy-tools-re-encoded-images.zip", ); expect(external).toEqual([]); expect(errors).toEqual([]); }); test("normalizes EXIF orientation while re-encoding JPEG pixels", async ({ page, }) => { const external = await localOnly(page); await page.goto("/deep/nested/privacy/"); const encoded = await page.evaluate(async () => { const canvas = document.createElement("canvas"); canvas.width = 2; canvas.height = 1; const context = canvas.getContext("2d"); if (!context) throw new Error("Missing test canvas"); context.fillStyle = "#ff0000"; context.fillRect(0, 0, 1, 1); context.fillStyle = "#0000ff"; context.fillRect(1, 0, 1, 1); const blob = await new Promise((resolve, reject) => canvas.toBlob( (value) => value ? resolve(value) : reject(new Error("JPEG encode failed")), "image/jpeg", 0.95, ), ); return [...new Uint8Array(await blob.arrayBuffer())]; }); const jpeg = addExifOrientation(Buffer.from(encoded), 6); await page.locator('input[type="file"]').setInputFiles({ name: "oriented.jpg", mimeType: "image/jpeg", buffer: jpeg, }); await expect(page.getByText("2 × 1 pixels")).toBeVisible(); await page.getByRole("button", { name: "Re-encode & verify" }).click(); await expect(page.getByText("Mandatory output re-scan")).toBeVisible(); await expect(page.locator(".verification")).not.toHaveClass(/is-failed/u); await expect(page.getByText("Normalized", { exact: true })).toBeVisible(); expect(external).toEqual([]); }); function addExifOrientation(jpeg: Buffer, orientation: number): Buffer { const payload = Buffer.concat([ Buffer.from("Exif\0\0", "binary"), Buffer.from(tiffFixture({ orientation })), ]); const segment = Buffer.from([ 0xff, 0xe1, ((payload.length + 2) >>> 8) & 0xff, (payload.length + 2) & 0xff, ]); return Buffer.concat([ jpeg.subarray(0, 2), segment, payload, jpeg.subarray(2), ]); }