feat: release Office Tools 0.1.0
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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<Locator["setInputFiles"]>[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<string>();
|
||||
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__state .state-dot--error"),
|
||||
).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();
|
||||
});
|
||||
Reference in New Issue
Block a user