Files
workinf_Blender_Wasm/web/tests/e2e/wbv2-three-domain-focused-production.spec.ts
mes123456 d1e6b63d54
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
chore: checkpoint web Blender feature-gap work, docs and tests
- update native web_engine sources and rebuilt wasm artifacts
- add task cards, status notes and tooling scripts
- add web e2e specs, tutorial UI sources and tutorial assets
- sync golden task manifests and audit records

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-11 10:46:55 +08:00

106 lines
7.6 KiB
TypeScript

import { test, expect } from "@playwright/test";
import fs from "node:fs";
import path from "node:path";
const root = path.resolve(import.meta.dirname, "../../..");
const fixture = fs.readFileSync(path.join(root, "tests/files/web/basic_scene.blend"));
const receiptDir = path.join(root, "tests/golden/WBV2-P1425-001/verification");
fs.mkdirSync(receiptDir, { recursive: true });
test("focused creation chain", async ({ page }) => {
await page.goto("/?engine=manual");
const result = await page.evaluate(async (bytes) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 30000 });
const reopened = new WebEngineClient({ timeoutMs: 30000 });
try {
const open = await client.openBlend(Uint8Array.from(bytes).buffer);
const created = await client.applyCommand({ type: "createPrimitive", primitive: "CUBE", name: "VerifyCube", location: [1, 2, 3] } as never);
const node = created.snapshot.nodes.find((n) => n.name === "VerifyCube");
const undone = await client.applyCommand({ type: "undo" });
const redone = await client.applyCommand({ type: "redo" });
const saved = await client.saveBlend();
const savedBytes = saved.byteLength;
client.terminate();
const reopen = await reopened.openBlend(saved);
return { ok: true, base: open.snapshot.revision, createRevision: created.snapshot.revision, undoRevision: undone.snapshot.revision, redoRevision: redone.snapshot.revision, createdType: node?.type, createdId: node?.id, savedBytes, reopenRevision: reopen.snapshot.revision, reopenHasCube: reopen.snapshot.nodes.some(n => n.name === 'VerifyCube') };
} catch (error) {
const e = error as { message?: string; code?: string; detail?: string };
return { ok: false, message: e?.message, code: e?.code, detail: e?.detail };
} finally { client.terminate(); reopened.terminate(); }
}, Array.from(fixture));
fs.writeFileSync(path.join(receiptDir, "create-production.json"), JSON.stringify({ schemaVersion: 1, browser: "chromium", scope: "createPrimitive/undo/redo/save/reopen", ...result }, null, 2));
expect(result.ok).toBe(true);
expect(result.createdType).toBe("MESH");
expect(result.createRevision).toBeGreaterThan(result.base!);
expect(result.reopenRevision).toBe(1);
});
test("focused hierarchy chain", async ({ page }) => {
await page.goto("/?engine=manual");
const result = await page.evaluate(async (bytes) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 30000 });
const reopened = new WebEngineClient({ timeoutMs: 30000 });
try {
const open = await client.openBlend(Uint8Array.from(bytes).buffer);
const rootId = open.snapshot.scenes[0]?.rootCollectionId ?? null;
const first = await client.applyCommand({ type: "createCollection", name: "VerifyParent", parentCollectionId: rootId } as never);
const parent = first.snapshot.collections.find((c) => c.name === "VerifyParent");
const second = await client.applyCommand({ type: "createCollection", name: "VerifyChild", parentCollectionId: parent?.id ?? null } as never);
const child = second.snapshot.collections.find((c) => c.name === "VerifyChild");
const moved = await client.applyCommand({ type: "moveCollection", collectionId: child?.id, parentCollectionId: parent?.id } as never);
const active = await client.applyCommand({ type: "setActiveCollection", collectionId: child?.id } as never);
const deleted = await client.applyCommand({ type: "deleteCollection", collectionId: child?.id } as never);
const undone = await client.applyCommand({ type: "undo" });
const redone = await client.applyCommand({ type: "redo" });
const saved = await client.saveBlend();
const savedBytes = saved.byteLength;
client.terminate();
const reopen = await reopened.openBlend(saved);
return { ok: true, base: open.snapshot.revision, parentId: parent?.id, childId: child?.id, createdCollections: first.snapshot.collections.length, movedRevision: moved.snapshot.revision, activeRevision: active.snapshot.revision, deletedRevision: deleted.snapshot.revision, undoRevision: undone.snapshot.revision, redoRevision: redone.snapshot.revision, savedBytes, reopenRevision: reopen.snapshot.revision, reopenedCollections: reopen.snapshot.collections.length, reopenHasParent: reopen.snapshot.collections.some(c => c.name === 'VerifyParent') };
} catch (error) {
const e = error as { message?: string; code?: string; detail?: string };
return { ok: false, message: e?.message, code: e?.code, detail: e?.detail };
} finally { client.terminate(); reopened.terminate(); }
}, Array.from(fixture));
fs.writeFileSync(path.join(receiptDir, "hierarchy-production.json"), JSON.stringify({ schemaVersion: 1, browser: "chromium", scope: "createCollection/moveCollection/setActiveCollection/deleteCollection/undo/redo/save/reopen", ...result }, null, 2));
expect(result.ok).toBe(true);
expect(result.parentId).toBeTruthy();
expect(result.childId).toBeTruthy();
expect(result.reopenRevision).toBe(1);
});
test("focused modeling chain", async ({ page }) => {
await page.goto("/?engine=manual");
const result = await page.evaluate(async (bytes) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 30000 });
const reopened = new WebEngineClient({ timeoutMs: 30000 });
try {
const open = await client.openBlend(Uint8Array.from(bytes).buffer);
const node = open.snapshot.nodes.find((n) => n.type === "MESH");
if (!node?.dataId) throw new Error("mesh fixture is missing");
const meshId = node.dataId;
const triangulated = await client.applyCommand({ type: "meshTriangulate", meshId, faceIndices: [0] } as never);
const undone = await client.applyCommand({ type: "undo" });
const redone = await client.applyCommand({ type: "redo" });
const added = await client.applyCommand({ type: "addModifier", objectId: node.id, meshId, modifierType: "SUBSURF", baseRevision: redone.snapshot.revision } as never);
const modifier = added.snapshot.meshes.find((m) => m.id === meshId)?.modifierStack?.at(-1);
const removed = await client.applyCommand({ type: "removeModifier", objectId: node.id, meshId, modifierUuid: modifier?.uuid, baseRevision: added.snapshot.revision } as never);
const saved = await client.saveBlend();
const savedBytes = saved.byteLength;
client.terminate();
const reopen = await reopened.openBlend(saved);
return { ok: true, base: open.snapshot.revision, meshId, triangulateRevision: triangulated.snapshot.revision, undoRevision: undone.snapshot.revision, redoRevision: redone.snapshot.revision, modifierType: modifier?.type, addRevision: added.snapshot.revision, removeRevision: removed.snapshot.revision, savedBytes, reopenRevision: reopen.snapshot.revision, reopenedNodes: reopen.snapshot.nodes.length };
} catch (error) {
const e = error as { message?: string; code?: string; detail?: string };
return { ok: false, message: e?.message, code: e?.code, detail: e?.detail };
} finally { client.terminate(); reopened.terminate(); }
}, Array.from(fixture));
fs.writeFileSync(path.join(receiptDir, "modeling-production.json"), JSON.stringify({ schemaVersion: 1, browser: "chromium", scope: "meshTriangulate/addModifier/removeModifier/undo/redo/save/reopen", ...result }, null, 2));
expect(result.ok).toBe(true);
expect(result.modifierType).toBe("SUBSURF");
expect(result.reopenRevision).toBe(1);
});