Files
workinf_Blender_Wasm/web/tests/e2e/mesh-main-reader-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

93 lines
5.1 KiB
TypeScript

import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { expect, test } from "@playwright/test";
const root = path.resolve(import.meta.dirname, "../../..");
const fixturePath = path.join(root, "tests/files/web/attribute_scene.blend");
const geometryFixturePath = path.join(root, "tests/files/web/generated/WBV2-P2-003-MESH.EDIT_CORE.blend");
const fixture = fs.readFileSync(fixturePath);
const geometryFixture = fs.readFileSync(geometryFixturePath);
const fixtureSha256 = crypto.createHash("sha256").update(fixture).digest("hex");
test("WBV2 reads Mesh topology collections from Blender Main without JS simulation", async ({ page }) => {
test.setTimeout(120_000);
await page.goto("/?engine=manual");
const result = await page.evaluate(async (inputs) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const summarize = (snapshot: any) => snapshot.meshes.map((mesh: any) => ({
id: mesh.id,
name: mesh.name,
vertexCount: mesh.vertexCount,
edgeCount: mesh.edgeCount,
faceCount: mesh.faceCount,
cornerCount: mesh.cornerCount,
positionsLength: mesh.positions?.length ?? 0,
indicesLength: mesh.indices?.length ?? 0,
edgeVertexIndicesLength: mesh.edgeVertexIndices?.length ?? 0,
triangleCornerIndicesLength: mesh.triangleCornerIndices?.length ?? 0,
triangleFaceIndicesLength: mesh.triangleFaceIndices?.length ?? 0,
attributes: (mesh.attributes ?? []).map((attribute: any) => ({ name: attribute.name, domain: attribute.domain, dataType: attribute.dataType })).sort((a: any, b: any) => JSON.stringify(a).localeCompare(JSON.stringify(b))),
uvLayers: (mesh.uvLayers ?? []).map((layer: any) => ({ name: layer.name, active: layer.active, activeRender: layer.activeRender })).sort((a: any, b: any) => a.name.localeCompare(b.name)),
}));
const roundTrip = async (bytes: number[]) => {
const client = new WebEngineClient({ timeoutMs: 60_000 });
const reopenedClient = new WebEngineClient({ timeoutMs: 60_000 });
try {
const opened = await client.openBlend(Uint8Array.from(bytes).buffer);
const meshes = summarize(opened.snapshot);
if (meshes.length === 0) throw new Error("Blender Main returned no meshes");
const saved = await client.saveBlend();
client.terminate();
const reopened = await reopenedClient.openBlend(saved.slice(0));
return { meshes, reopenedMeshes: summarize(reopened.snapshot), openedRevision: opened.snapshot.revision, reopenedRevision: reopened.snapshot.revision };
}
finally {
client.terminate();
reopenedClient.terminate();
}
};
const attribute = await roundTrip(inputs.attributeBytes);
const geometry = await roundTrip(inputs.geometryBytes);
return {
browser: "chromium",
fixtureSha256: await crypto.subtle.digest("SHA-256", Uint8Array.from(inputs.attributeBytes)).then((hash) => Array.from(new Uint8Array(hash), (value) => value.toString(16).padStart(2, "0")).join("")),
geometryFixtureSha256: await crypto.subtle.digest("SHA-256", Uint8Array.from(inputs.geometryBytes)).then((hash) => Array.from(new Uint8Array(hash), (value) => value.toString(16).padStart(2, "0")).join("")),
attribute,
geometry,
};
}, { attributeBytes: Array.from(fixture), geometryBytes: Array.from(geometryFixture) });
fs.mkdirSync(path.join(root, "tests/golden/WBV2-P1100-002"), { recursive: true });
fs.writeFileSync(path.join(root, "tests/golden/WBV2-P1100-002/mesh-main-reader-production.json"), `${JSON.stringify({
schemaVersion: 1,
task: "WBV2-P1100-002",
operation: "MESH_MAIN_READER_CHROMIUM",
browser: "chromium",
queueMutation: false,
sourceRnaStructs: {
"rna-struct:MeshVertex": "mesh.vertexCount + mesh.attributes[name=position]",
"rna-struct:MeshEdges": "mesh.edgeCount",
"rna-struct:MeshLoops": "mesh.cornerCount",
"rna-struct:MeshPolygons": "mesh.faceCount",
"rna-struct:MeshUVLoopLayer": "mesh.uvLayers",
"rna-struct:AttributeGroupMesh": "mesh.attributes",
},
...result,
}, null, 2)}\n`);
expect(result.browser).toBe("chromium");
expect(result.fixtureSha256).toBe(fixtureSha256);
expect(result.geometryFixtureSha256).toBe(crypto.createHash("sha256").update(geometryFixture).digest("hex"));
expect(result.attribute.openedRevision).toBe(1);
expect(result.attribute.reopenedRevision).toBe(1);
expect(result.geometry.openedRevision).toBe(1);
expect(result.geometry.reopenedRevision).toBe(1);
expect(result.attribute.meshes).toEqual(result.attribute.reopenedMeshes);
expect(result.geometry.meshes).toEqual(result.geometry.reopenedMeshes);
const meshes = [...result.attribute.meshes, ...result.geometry.meshes];
expect(meshes.every((mesh: any) => mesh.vertexCount > 0 && mesh.edgeCount > 0 && mesh.faceCount > 0 && mesh.cornerCount > 0)).toBe(true);
expect(meshes.some((mesh: any) => mesh.edgeCount > 0)).toBe(true);
expect(meshes.some((mesh: any) => mesh.faceCount > 0 && mesh.cornerCount > 0)).toBe(true);
expect(meshes.some((mesh: any) => mesh.attributes.length > 0)).toBe(true);
});