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); });