import { expect, test } from "@playwright/test"; import fs from "node:fs"; import path from "node:path"; const root = path.resolve(import.meta.dirname, "../../.."); const expected = JSON.parse(fs.readFileSync( path.join(root, "tests/golden/M10-02/geometry-node-allowlist.json"), "utf8", )); const blendBytes = fs.readFileSync(path.join(root, expected.fixture)); test("M10-02 blocks non-allowlisted Main nodes without replacing the preserved graph", async ({ page }) => { test.setTimeout(120_000); expect((await import("node:crypto")).createHash("sha256").update(blendBytes).digest("hex")) .toBe(expected.fixtureSha256); await page.goto("/"); const result = await page.evaluate(async ({ bytes, expected }) => { const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts"); const source = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer; const client = new WebEngineClient({ timeoutMs: 60_000 }); await client.init(); const opened = await client.openBlend(source); const supported = opened.snapshot.geometryNodeGraphs?.find((graph) => graph.name === expected.supportedGraph); const unsupported = opened.snapshot.geometryNodeGraphs?.find((graph) => graph.name === expected.unsupportedGraph); const meshId = opened.snapshot.meshes[0]?.id ?? "mesh:missing"; if (!supported || !unsupported) throw new Error("Geometry Node allowlist fixture graphs are missing"); const baseline = JSON.stringify(opened.snapshot.geometryNodeGraphs); const revision = opened.snapshot.revision; const unsupportedTypes = unsupported.nodes .filter((node) => !expected.allowlist.includes(node.type)) .map((node) => node.type); const attempt = async (graph: typeof supported) => { try { await client.applyCommand({ type: "setGeometryNodeGraph", meshId, graph }); return null; } catch (error) { return error as { code?: string; message?: string }; } }; const unsupportedError = await attempt(unsupported); const afterUnsupported = await client.snapshot(); const supportedError = await attempt(supported); const afterSupported = await client.snapshot(); client.terminate(); return { unsupportedTypes, unsupportedErrorCode: unsupportedError?.code, supportedErrorCode: supportedError?.code, unsupportedPreserved: JSON.stringify(afterUnsupported.snapshot.geometryNodeGraphs) === baseline, supportedPreserved: JSON.stringify(afterSupported.snapshot.geometryNodeGraphs) === baseline, revisions: [revision, afterUnsupported.snapshot.revision, afterSupported.snapshot.revision], graphHashes: [unsupported.graphHash, afterUnsupported.snapshot.geometryNodeGraphs?.find((graph) => graph.name === expected.unsupportedGraph)?.graphHash], }; }, { bytes: new Uint8Array(blendBytes), expected }); expect(result).toEqual({ unsupportedTypes: expected.unsupportedNodeTypes, unsupportedErrorCode: expected.unsupportedErrorCode, supportedErrorCode: expected.evaluatorUnavailableErrorCode, unsupportedPreserved: true, supportedPreserved: true, revisions: [result.revisions[0], result.revisions[0], result.revisions[0]], graphHashes: [result.graphHashes[0], result.graphHashes[0]], }); expect(result.graphHashes[0]).toMatch(/^[0-9a-f]{64}$/); });