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 task = process.env.WBV2_BATCH_TASK ?? "WBV2-P1157-001"; const fixture = "tests/files/web/rigged_shape_scene.blend"; const fixtureBytes = fs.readFileSync(path.join(root, fixture)); const fixtureSha256 = crypto.createHash("sha256").update(fixtureBytes).digest("hex"); const sourceUnits = JSON.parse(process.env.WBV2_BATCH_UNITS ?? '["rna-struct:ShapeKeyPoint"]'); test("shape key reader preserves Blender Main point metadata", async ({ page }) => { test.setTimeout(180_000); await page.goto("/"); const result = await page.evaluate(async (bytes) => { const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts"); const canonical = (snapshot: any) => (snapshot.meshes ?? []).map((mesh: any) => ({ id: mesh.id, shapeKeys: (mesh.shapeKeys ?? []).map((key: any) => ({ name: key.name, value: key.value, pointCount: key.points?.length ?? 0 })), })); const client = new WebEngineClient({ timeoutMs: 60_000 }); const reopenedClient = new WebEngineClient({ timeoutMs: 60_000 }); const secondClient = new WebEngineClient({ timeoutMs: 60_000 }); const output: any = {}; try { const opened = await client.openBlend(Uint8Array.from(bytes).buffer); output.before = canonical(opened.snapshot); output.shapeKeyCount = output.before.reduce((total: number, mesh: any) => total + mesh.shapeKeys.length, 0); const saved = await client.saveBlend(); output.after = canonical((await reopenedClient.openBlend(saved.slice(0))).snapshot); output.second = canonical((await secondClient.openBlend(Uint8Array.from(bytes).buffer)).snapshot); output.saveReopenExact = JSON.stringify(output.before) === JSON.stringify(output.after); output.deterministicExact = JSON.stringify(output.before) === JSON.stringify(output.second); output.savedSha256 = await crypto.subtle.digest("SHA-256", saved).then((hash) => Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join("")); try { await client.openBlend(Uint8Array.from([0x42, 0x4c, 0x45, 0x4e, 0x44]).buffer); output.malformed = { code: "ACCEPTED_MALFORMED" }; } catch (error: any) { output.malformed = { code: error?.code, message: error?.message }; } } catch (error: any) { output.error = { code: error?.code, message: error?.message, detail: String(error) }; } finally { client.terminate(); reopenedClient.terminate(); secondClient.terminate(); } return output; }, Array.from(fixtureBytes)); const report = { schemaVersion: 1, task, operation: "SHAPE_KEY_POINT_READER_CHROMIUM_SAVE_REOPEN", browser: "chromium", queueMutation: false, fixture, fixtureSha256, sourceUnits, ...result, }; const outputPath = path.join(root, `tests/golden/${task}/shape-key-reader-production.json`); fs.mkdirSync(path.dirname(outputPath), { recursive: true }); fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`); expect(result.error).toBeUndefined(); expect(result.shapeKeyCount).toBeGreaterThan(0); expect(result.saveReopenExact).toBe(true); expect(result.deterministicExact).toBe(true); expect(result.malformed?.code).not.toBe("ACCEPTED_MALFORMED"); });