Files
workinf_Blender_Wasm/web/tests/e2e/file-import-progress.spec.ts
mes123456 0fe8d2bb56
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

113 lines
6.6 KiB
TypeScript

import { expect, test } from "@playwright/test";
import fs from "node:fs";
import path from "node:path";
const basicBlend = path.resolve(import.meta.dirname, "../../../tests/files/web/basic_scene.blend");
const largeBlend = path.resolve(import.meta.dirname, "../../../tests/files/web/image_resource_matrix.blend");
test("M7-03 records exact streamed byte progress for a large valid blend", async ({ page }) => {
test.setTimeout(60_000);
const expectedBytes = fs.statSync(largeBlend).size;
expect(expectedBytes).toBeGreaterThanOrEqual(512 * 1024);
await page.goto("/");
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
await page.getByTestId("blend-file-input").setInputFiles(largeBlend);
const app = page.locator(".blender-app");
await expect(app).toHaveAttribute("data-user-action-open-status", "SUCCEEDED", { timeout: 30_000 });
await expect(app).toHaveAttribute("data-open-read-phase", "COMPLETED");
await expect(app).toHaveAttribute("data-open-read-bytes", String(expectedBytes));
await expect(app).toHaveAttribute("data-open-read-total", String(expectedBytes));
expect(Number(await app.getAttribute("data-open-read-events"))).toBeGreaterThan(2);
expect(await app.getAttribute("data-open-read-action-id")).toBe(await app.getAttribute("data-user-action-open-id"));
});
test("M7-03 cancels a large streamed open before WebEngine and preserves the current project", async ({ page }) => {
test.setTimeout(60_000);
await page.goto("/");
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
await page.getByTestId("blend-file-input").setInputFiles(basicBlend);
await expect(page.getByText("BasicCube", { exact: true })).toBeVisible();
const previousStats = await page.getByTestId("scene-stats").textContent();
const totalBytes = 40 * 1024 * 1024;
const syntheticLargeBlend = Buffer.alloc(totalBytes, 0x7f);
fs.readFileSync(basicBlend).copy(syntheticLargeBlend);
await page.getByTestId("blend-file-input").setInputFiles({
name: "cancelled-large.blend",
mimeType: "application/octet-stream",
buffer: syntheticLargeBlend,
});
const observation = await page.waitForFunction((expectedTotal) => {
const element = document.querySelector<HTMLElement>('[data-testid="open-progress"]');
const bytesRead = Number(element?.getAttribute("data-bytes-read"));
const actualTotal = Number(element?.getAttribute("data-total-bytes"));
const cancel = document.querySelector<HTMLButtonElement>('button[aria-label="取消打开"]');
if (bytesRead <= 0 || bytesRead >= expectedTotal || !cancel) return false;
cancel.focus();
cancel.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true, cancelable: true }));
return { bytesRead, totalBytes: actualTotal };
}, totalBytes, { polling: "raf", timeout: 10_000 });
const observed = await observation.jsonValue() as { bytesRead: number; totalBytes: number };
expect(observed.bytesRead).toBeLessThan(totalBytes);
expect(observed.totalBytes).toBe(totalBytes);
const app = page.locator(".blender-app");
await expect(app).toHaveAttribute("data-user-action-open-status", "CANCELLED");
await expect(app).toHaveAttribute("data-user-action-open-error", "OPEN_CANCELLED");
await expect(app).toHaveAttribute("data-open-read-phase", "CANCELLED");
await expect(app).toHaveAttribute("data-open-cleanup-reader-count", "0");
await expect(app).toHaveAttribute("data-open-cleanup-reader-bytes", "0");
await expect(app).toHaveAttribute("data-open-cleanup-reader-staging", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-requests", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-bytes", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-handles", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-staging", "0");
const cancelledBytes = Number(await app.getAttribute("data-open-read-bytes"));
expect(cancelledBytes).toBeGreaterThan(0);
expect(cancelledBytes).toBeLessThan(totalBytes);
await expect(page.getByTestId("scene-stats")).toHaveText(previousStats ?? "");
await expect(page.getByText("BasicCube", { exact: true })).toBeVisible();
expect(await page.evaluate(() => localStorage.getItem("blender-web:last-project-id"))).toBe("basic_scene");
});
test("M7-04 destroys isolated native open resources before reporting cancellation", async ({ page }) => {
test.setTimeout(60_000);
await page.goto("/");
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
await page.getByTestId("blend-file-input").setInputFiles(basicBlend);
await expect(page.getByText("BasicCube", { exact: true })).toBeVisible();
const previousStats = await page.getByTestId("scene-stats").textContent();
await page.getByTestId("blend-file-input").setInputFiles(largeBlend);
const cancellation = await page.waitForFunction(() => {
const progress = document.querySelector<HTMLElement>('[data-testid="open-progress"]');
const stage = progress?.dataset.stage;
const cancel = document.querySelector<HTMLButtonElement>('button[aria-label="取消打开"]');
if (!cancel || (stage !== "NATIVE_INITIALIZE" && stage !== "NATIVE_OPENED")) return false;
cancel.click();
return stage;
}, undefined, { polling: "raf", timeout: 10_000 });
expect(["NATIVE_INITIALIZE", "NATIVE_OPENED"]).toContain(await cancellation.jsonValue());
const app = page.locator(".blender-app");
await expect(app).toHaveAttribute("data-user-action-open-status", "CANCELLED");
await expect(app).toHaveAttribute("data-user-action-open-error", "OPEN_CANCELLED");
await expect(app).toHaveAttribute("data-open-cleanup-reader-count", "0");
await expect(app).toHaveAttribute("data-open-cleanup-reader-bytes", "0");
await expect(app).toHaveAttribute("data-open-cleanup-reader-staging", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-requests", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-bytes", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-handles", "0");
await expect(app).toHaveAttribute("data-open-cleanup-engine-staging", "0");
await expect(page.getByTestId("scene-stats")).toHaveText(previousStats ?? "");
await expect(page.getByText("BasicCube", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "添加立方体" }).click();
await expect(page.getByTestId("scene-stats")).toContainText("Objects 4");
const download = page.waitForEvent("download");
await page.getByRole("button", { name: "保存项目" }).click();
await download;
});