import { expect, test, type Locator, type Page } from "@playwright/test"; import { createMalformedDocx, createMinimalDocx, createMinimalPptx, createMinimalXlsx, type OoxmlFixture, } from "../fixtures/ooxml"; const entry = "/deep/nested/office/"; async function openFixture(page: Page, fixture: OoxmlFixture) { await page.goto(entry); await page .getByTestId("office-file-input") .setInputFiles( fixture as unknown as Parameters[0], ); } async function expectRenderedPackage( page: Page, format: "docx" | "xlsx" | "pptx", ) { await expect(page.getByTestId("opened-file")).toBeVisible(); await expect(page.getByText("Rendered locally", { exact: true })).toBeVisible( { timeout: 90_000, }, ); await expect( page.locator(`.office-render-stage--${format} canvas`).first(), ).toBeVisible(); const canvas = page.locator(`.office-render-stage--${format} canvas`).first(); const dimensions = await canvas.evaluate((element) => { const rendered = element as HTMLCanvasElement; const bounds = rendered.getBoundingClientRect(); return { bitmapHeight: rendered.height, bitmapWidth: rendered.width, cssHeight: bounds.height, cssWidth: bounds.width, }; }); expect(dimensions.bitmapWidth).toBeGreaterThan(1); expect(dimensions.bitmapHeight).toBeGreaterThan(1); expect(dimensions.cssWidth).toBeGreaterThan(1); expect(dimensions.cssHeight).toBeGreaterThan(1); } async function expectOneSearchMatch(page: Page, text: string) { await page.getByRole("search").getByPlaceholder("Find in file").fill(text); await page.getByRole("search").getByRole("button", { name: "Find" }).click(); await expect(page.getByText("1 match", { exact: true })).toBeVisible(); } test("opens, renders, searches and navigates a project-authored DOCX", async ({ page, }) => { const externalHosts = new Set(); const runtimeErrors: string[] = []; const wasmResponses: Array<{ cacheControl: string; contentType: string; url: URL; }> = []; page.on("request", (request) => { const url = new URL(request.url()); if (url.hostname !== "127.0.0.1") externalHosts.add(url.hostname); }); page.on("response", (response) => { const url = new URL(response.url()); if (url.pathname.endsWith(".wasm")) { wasmResponses.push({ cacheControl: response.headers()["cache-control"] ?? "", contentType: response.headers()["content-type"] ?? "", url, }); } }); page.on("console", (message) => { if (message.type() === "error") runtimeErrors.push(message.text()); }); page.on("pageerror", (error) => runtimeErrors.push(error.message)); await openFixture(page, createMinimalDocx()); await expectRenderedPackage(page, "docx"); await expect(page.getByText(/page 1 of [2-9]\d*/i)).toBeVisible(); await expectOneSearchMatch(page, "Second Page Search Marker"); await page.getByRole("button", { name: "Next page" }).click(); await expect(page.getByText(/page 2 of [2-9]\d*/i)).toBeVisible(); expect(externalHosts).toEqual(new Set()); expect(wasmResponses.length).toBeGreaterThan(0); expect( wasmResponses.every( ({ cacheControl, contentType, url }) => url.origin === "http://127.0.0.1:4173" && url.pathname.startsWith("/deep/nested/office/assets/") && contentType.startsWith("application/wasm") && cacheControl.includes("max-age=31536000") && cacheControl.includes("immutable"), ), ).toBe(true); expect(runtimeErrors).toEqual([]); }); test("opens, renders, searches and switches sheets in a project-authored XLSX", async ({ page, }) => { await openFixture(page, createMinimalXlsx()); await expectRenderedPackage(page, "xlsx"); await expect(page.getByText("sheet 1 of 2", { exact: true })).toBeVisible(); await expectOneSearchMatch(page, "Office Tools Workbook Fixture"); await page.getByRole("button", { name: "Next sheet" }).click(); await expect(page.getByText("sheet 2 of 2", { exact: true })).toBeVisible(); }); test("opens, renders, searches and navigates a project-authored PPTX", async ({ page, }) => { await openFixture(page, createMinimalPptx()); await expectRenderedPackage(page, "pptx"); await expect(page.getByText("slide 1 of 2", { exact: true })).toBeVisible(); await expectOneSearchMatch(page, "Second Slide Search Marker"); await page.getByRole("button", { name: "Next slide" }).click(); await expect(page.getByText("slide 2 of 2", { exact: true })).toBeVisible(); }); test("fails closed with an inline error for a malformed OOXML package", async ({ page, }) => { await openFixture(page, createMalformedDocx()); const alert = page.getByRole("alert"); await expect(alert).toBeVisible({ timeout: 90_000 }); await expect(alert).not.toBeEmpty(); await expect(page.locator(".file-heading")).toHaveCount(0); await expect( page.getByRole("heading", { name: "Choose or drop a file" }), ).toBeVisible(); }); test("rejects a valid package whose contents do not match its extension", async ({ page, }) => { const workbook = createMinimalXlsx(); await openFixture(page, { ...workbook, mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", name: "workbook-disguised-as-document.docx", }); const alert = page.getByRole("alert"); await expect(alert).toBeVisible({ timeout: 90_000 }); await expect(alert).not.toBeEmpty(); await expect( page.locator(".file-heading__state .state-dot--error"), ).toBeVisible(); });