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 }); const types = [ "WEIGHTED_NORMAL", "CORRECTIVE_SMOOTH", "LAPLACIANSMOOTH", "MASK", "NORMAL_EDIT", "SKIN", "UV_PROJECT", "UV_WARP", "WARP", "DATA_TRANSFER", "VERTEX_WEIGHT_EDIT", "VERTEX_WEIGHT_MIX", "VERTEX_WEIGHT_PROXIMITY", "LAPLACIANDEFORM", "PARTICLE_INSTANCE", "SURFACE", "MESH_CACHE", "MESH_SEQUENCE_CACHE", "MULTIRES", "EXPLODE", ]; test("expanded modifier whitelist adds, evaluates and removes in native Main", async ({ page }) => { test.setTimeout(180_000); await page.goto("/?engine=manual"); const result = await page.evaluate(async ({ bytes, types }) => { const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts"); const client = new WebEngineClient({ timeoutMs: 60_000 }); const reopened = new WebEngineClient({ timeoutMs: 60_000 }); const cases: Record[] = []; 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; let revision = open.snapshot.revision; for (const modifierType of types) { try { const added = await client.applyCommand({ type: "addModifier", objectId: node.id, meshId, modifierType, baseRevision: revision } as never); revision = added.snapshot.revision; const stack = added.snapshot.meshes.find((m) => m.id === meshId)?.modifierStack ?? []; const modifier = stack.at(-1); if (!modifier?.uuid) throw new Error("modifier was not published"); const removed = await client.applyCommand({ type: "removeModifier", objectId: node.id, meshId, modifierUuid: modifier.uuid, baseRevision: revision } as never); revision = removed.snapshot.revision; const remaining = removed.snapshot.meshes.find((m) => m.id === meshId)?.modifierStack ?? []; cases.push({ modifierType, status: remaining.some((m) => m.uuid === modifier.uuid) ? "REMOVE_FAILED" : "FINISHED", publishedType: modifier.type, revision }); } catch (error) { const e = error as { message?: string; code?: string }; cases.push({ modifierType, status: "ERROR", message: e?.message, code: e?.code }); } } const saved = await client.saveBlend(); client.terminate(); const reopen = await reopened.openBlend(saved); return { ok: true, cases, reopenRevision: reopen.snapshot.revision }; } catch (error) { const e = error as { message?: string; code?: string }; return { ok: false, cases, message: e?.message, code: e?.code }; } finally { client.terminate(); reopened.terminate(); } }, { bytes: Array.from(fixture), types }); fs.writeFileSync(path.join(receiptDir, "modifier-matrix-production.json"), JSON.stringify({ schemaVersion: 1, browser: "chromium", scope: "20 expanded modifiers add/publication/remove/save/reopen", ...result }, null, 2)); expect(result.ok).toBe(true); expect(result.reopenRevision).toBe(1); const failed = (result.cases as Record[]).filter((item) => item.status !== "FINISHED"); expect(failed).toEqual([]); });