113 lines
6.6 KiB
TypeScript
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;
|
|
});
|