Files
workinf_Blender_Wasm/web/tests/e2e/geometry-node-field-budget.spec.ts
mes123456 0fe8d2bb56
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
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

109 lines
3.9 KiB
TypeScript

import { expect, test } from "@playwright/test";
import fs from "node:fs";
import path from "node:path";
import {
GEOMETRY_NODE_FIELD_BUDGET,
parseGeometryNodeFieldMaterializationBatch,
type GeometryNodeDomainCardinalityIR,
} from "../../protocol/geometry-nodes";
import { parseDepsgraphEvaluation, type DepsgraphEvaluationIR } from "../../protocol/depsgraph";
const root = path.resolve(import.meta.dirname, "../../..");
const fixture = fs.readFileSync(path.join(root, "tests/files/web/geometry_node_allowlist_evaluator.blend"));
test("M10-04 binds field conversion budgets to native mesh domain cardinality", async ({ page }) => {
test.setTimeout(120_000);
await page.goto("/");
const result = await page.evaluate(async ({ bytes }) => {
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: 90_000 });
await client.init();
await client.openBlend(source);
const evaluated = await client.evaluateDepsgraph();
client.terminate();
const mesh = evaluated.depsgraph.meshes.find((candidate) =>
candidate.objectId === "object:M10GN_StoreAttribute");
if (!mesh?.domainCardinality) throw new Error("Store Named Attribute domain cardinality is missing");
return {
depsgraph: evaluated.depsgraph,
domainCardinality: mesh.domainCardinality,
fieldMaterializations: mesh.fieldMaterializations ?? [],
attributes: mesh.attributes ?? {},
};
}, { bytes: new Uint8Array(fixture) });
expect(result.domainCardinality).toEqual({
POINT: 8,
EDGE: 12,
FACE: 6,
CORNER: 24,
CURVE: 0,
INSTANCE: 0,
LAYER: 0,
});
expect(result.fieldMaterializations).toEqual([{
schemaVersion: 1,
fieldId: "attribute:m10_value",
domain: "POINT",
dataType: "FLOAT",
elementCount: 8,
scalarValueCount: 8,
materializedByteLength: 32,
transport: "JSON",
}]);
expect(result.attributes.m10_value.values).toHaveLength(8);
expect(result.attributes.m10_value.values.every((value: number) => value === 0.375)).toBe(true);
const rejects = (mutate: (candidate: DepsgraphEvaluationIR) => void): void => {
const candidate = structuredClone(result.depsgraph);
mutate(candidate);
expect(() => parseDepsgraphEvaluation(candidate)).toThrow();
};
rejects((candidate) => {
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
target.domainCardinality!.POINT += 1;
});
rejects((candidate) => {
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
target.fieldMaterializations![0].materializedByteLength += 4;
});
rejects((candidate) => {
const target = candidate.meshes.find((entry) => entry.objectId === "object:M10GN_StoreAttribute")!;
(target.fieldMaterializations![0] as unknown as Record<string, unknown>).values = [0];
});
const common = {
schemaVersion: 1 as const,
graphId: "node-group:M10GN_StoreAttributeGraph",
graphHash: "a".repeat(64),
revision: 1,
transport: "JSON" as const,
domainCardinality: result.domainCardinality as GeometryNodeDomainCardinalityIR,
};
const batch = parseGeometryNodeFieldMaterializationBatch([
{
...common,
fieldId: "field:point-to-corner",
sourceDomain: "POINT",
targetDomain: "CORNER",
dataType: "FLOAT",
},
{
...common,
fieldId: "field:constant-offset",
sourceDomain: "CONSTANT",
targetDomain: "POINT",
dataType: "VECTOR",
},
]);
expect(batch).toMatchObject({
fieldCount: 2,
domainConversionCount: 1,
materializedElementCount: 32,
materializedByteLength: 192,
});
expect(batch.fields.every((field) => field.scalarValueCount <=
GEOMETRY_NODE_FIELD_BUDGET.maxJsonScalarValuesPerField)).toBe(true);
});