Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
124 lines
6.3 KiB
TypeScript
124 lines
6.3 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 fixtures = [
|
|
"modifier_generate_scene.blend",
|
|
"modifier_deform_scene.blend",
|
|
"modifier_deform_curve_scene.blend",
|
|
"modifier_extended_native.blend",
|
|
"modifier_physics_scene.blend",
|
|
"modifier_geometry_nodes_scene.blend",
|
|
"rigged_shape_scene.blend",
|
|
];
|
|
const sourceModules = {
|
|
"modifier-source:MOD_array": "ARRAY",
|
|
"modifier-source:MOD_armature": "ARMATURE",
|
|
"modifier-source:MOD_bevel": "BEVEL",
|
|
"modifier-source:MOD_boolean": "BOOLEAN",
|
|
"modifier-source:MOD_build": "BUILD",
|
|
"modifier-source:MOD_cast": "CAST",
|
|
"modifier-source:MOD_cast_web": "CAST",
|
|
"modifier-source:MOD_cloth": "CLOTH",
|
|
"modifier-source:MOD_collision": "COLLISION",
|
|
"modifier-source:MOD_curve": "CURVE",
|
|
"modifier-source:MOD_displace": "DISPLACE",
|
|
"modifier-source:MOD_displace_web": "DISPLACE",
|
|
"modifier-source:MOD_edgesplit": "EDGE_SPLIT",
|
|
"modifier-source:MOD_hook": "HOOK",
|
|
"modifier-source:MOD_lattice": "LATTICE",
|
|
"modifier-source:MOD_meshdeform": "MESH_DEFORM",
|
|
"modifier-source:MOD_mirror": "MIRROR",
|
|
"modifier-source:MOD_nodes": "NODES",
|
|
"modifier-source:MOD_nodes_web": "NODES",
|
|
"modifier-source:MOD_screw": "SCREW",
|
|
"modifier-source:MOD_solidify": "SOLIDIFY",
|
|
"modifier-source:MOD_simpledeform": "SIMPLE_DEFORM",
|
|
"modifier-source:MOD_smooth": "SMOOTH",
|
|
"modifier-source:MOD_softbody": "SOFT_BODY",
|
|
"modifier-source:MOD_subsurf": "SUBSURF",
|
|
"modifier-source:MOD_triangulate": "TRIANGULATE",
|
|
"modifier-source:MOD_surfacedeform": "SURFACE_DEFORM",
|
|
"modifier-source:MOD_wave": "WAVE",
|
|
"modifier-source:MOD_weld": "WELD",
|
|
"modifier-source:MOD_wireframe": "WIREFRAME",
|
|
};
|
|
const modifierRnaStructs = {
|
|
"rna-struct:ArrayModifier": "ARRAY",
|
|
"rna-struct:ArmatureModifier": "ARMATURE",
|
|
"rna-struct:BevelModifier": "BEVEL",
|
|
"rna-struct:BooleanModifier": "BOOLEAN",
|
|
"rna-struct:BuildModifier": "BUILD",
|
|
"rna-struct:CastModifier": "CAST",
|
|
"rna-struct:ClothModifier": "CLOTH",
|
|
"rna-struct:CollisionModifier": "COLLISION",
|
|
"rna-struct:CurveModifier": "CURVE",
|
|
"rna-struct:DisplaceModifier": "DISPLACE",
|
|
"rna-struct:EdgeSplitModifier": "EDGE_SPLIT",
|
|
"rna-struct:HookModifier": "HOOK",
|
|
"rna-struct:LatticeModifier": "LATTICE",
|
|
"rna-struct:MeshDeformModifier": "MESH_DEFORM",
|
|
"rna-struct:MirrorModifier": "MIRROR",
|
|
"rna-struct:NodesModifier": "NODES",
|
|
"rna-struct:ScrewModifier": "SCREW",
|
|
"rna-struct:SolidifyModifier": "SOLIDIFY",
|
|
"rna-struct:SimpleDeformModifier": "SIMPLE_DEFORM",
|
|
"rna-struct:SmoothModifier": "SMOOTH",
|
|
"rna-struct:SoftBodyModifier": "SOFT_BODY",
|
|
"rna-struct:SubsurfModifier": "SUBSURF",
|
|
"rna-struct:SurfaceDeformModifier": "SURFACE_DEFORM",
|
|
"rna-struct:TriangulateModifier": "TRIANGULATE",
|
|
"rna-struct:WaveModifier": "WAVE",
|
|
"rna-struct:WeldModifier": "WELD",
|
|
"rna-struct:WireframeModifier": "WIREFRAME",
|
|
};
|
|
const fixtureBytes = Object.fromEntries(fixtures.map((name) => [name, fs.readFileSync(path.join(root, "tests/files/web", name))]));
|
|
const fixtureSha256 = Object.fromEntries(Object.entries(fixtureBytes).map(([name, bytes]) => [name, crypto.createHash("sha256").update(bytes).digest("hex")]));
|
|
|
|
test("WBV2 modifier source modules preserve concrete Blender DNA through Chromium save/reopen", async ({ page }) => {
|
|
test.setTimeout(180_000);
|
|
await page.goto("/");
|
|
const result = await page.evaluate(async (input) => {
|
|
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
|
|
const canonical = (snapshot: any) => snapshot.meshes.flatMap((mesh: any) => (mesh.modifierStack ?? []).map((modifier: any) => ({ meshId: mesh.id, uuid: modifier.uuid, type: modifier.type, enabled: modifier.enabled, evaluationStatus: modifier.evaluationStatus })));
|
|
const output: any = {};
|
|
for (const [name, bytes] of Object.entries(input.fixtures as Record<string, 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 before = canonical(opened.snapshot);
|
|
const target = before[0];
|
|
const edited = await client.applyCommand({ type: "setModifierVisibility", meshId: target.meshId, modifierUuid: target.uuid, showViewport: false, showRender: false, showEditMode: true, showOnCage: true } as any);
|
|
const afterEdit = canonical(edited.snapshot);
|
|
const saved = await client.saveBlend();
|
|
const reopened = await reopenedClient.openBlend(saved.slice(0));
|
|
const after = canonical(reopened.snapshot);
|
|
output[name] = { before, afterEdit, after, exact: JSON.stringify(afterEdit) === JSON.stringify(after), savedSha256: await crypto.subtle.digest("SHA-256", saved).then((hash) => Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join("")) };
|
|
}
|
|
catch (error: any) {
|
|
output[name] = { error: { code: error?.code, message: error?.message, detail: String(error) } };
|
|
}
|
|
finally {
|
|
client.terminate();
|
|
reopenedClient.terminate();
|
|
}
|
|
}
|
|
return output;
|
|
}, { fixtures: Object.fromEntries(Object.entries(fixtureBytes).map(([name, bytes]) => [name, Array.from(bytes)])) });
|
|
|
|
fs.mkdirSync(path.join(root, "tests/golden/WBV2-P1107-000"), { recursive: true });
|
|
fs.writeFileSync(path.join(root, "tests/golden/WBV2-P1107-000/modifier-source-roundtrip-production.json"), `${JSON.stringify({ schemaVersion: 1, task: "WBV2-P1107-000", operation: "MODIFIER_SOURCE_DNA_ROUNDTRIP_MAIN_CHROMIUM", browser: "chromium", queueMutation: false, sourceModules, modifierRnaStructs, fixtureSha256, ...result }, null, 2)}\n`);
|
|
const observedTypes = new Set(Object.values(result).flatMap((entry: any) => entry?.afterEdit?.map((modifier: any) => modifier.type) ?? []));
|
|
for (const type of new Set(Object.values(sourceModules))) expect(observedTypes.has(type)).toBe(true);
|
|
for (const type of new Set(Object.values(modifierRnaStructs))) expect(observedTypes.has(type)).toBe(true);
|
|
for (const name of fixtures) {
|
|
expect(result[name]?.error).toBeUndefined();
|
|
expect(result[name]?.exact).toBe(true);
|
|
expect(result[name]?.before.length).toBeGreaterThan(0);
|
|
expect(result[name]?.afterEdit).toEqual(result[name]?.after);
|
|
}
|
|
});
|