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 fixture = fs.readFileSync(path.join(root, "tests/files/web/mask_scene.blend")); const fixtureSha256 = crypto.createHash("sha256").update(fixture).digest("hex"); test("WBV2-P1127 reads Blender Mask data and selection context in Chromium", async ({ page }) => { await page.goto("/"); const result = await page.evaluate(async (input) => { const [{ WebEngineClient }, { raycastMaskProject, selectMaskPointsInBounds }] = await Promise.all([ import("/src/engine-client/WebEngineClient.ts"), import("/src/tracking/MaskSelection.ts"), ]); const client = new WebEngineClient({ timeoutMs: 60_000 }); try { const opened = await client.openBlend(Uint8Array.from(input.bytes).buffer); const project = opened.snapshot.trackingMasks; if (!project) throw new Error("Blender Mask project is missing"); const layer = project.masks[0]?.layers[0]; if (!layer) throw new Error("Blender Mask layer is missing"); const locked = raycastMaskProject(project, [0.1, 0.2], 0.001); const editable = raycastMaskProject(project, [0.2, 0.2], 0.001); const selected = selectMaskPointsInBounds(project, [0.05, 0.15], [0.25, 0.25]); const toggled = selectMaskPointsInBounds(project, [0.05, 0.15], [0.25, 0.25], selected, "TOGGLE"); const saved = await client.saveBlend(); const reopenedClient = new WebEngineClient({ timeoutMs: 60_000 }); try { const reopened = await reopenedClient.openBlend(saved.slice(0)); return { fixtureSha256: input.fixtureSha256, status: opened.snapshot.trackingMaskStatus, maskIds: project.masks.map((mask: any) => mask.id), layerIds: project.masks.flatMap((mask: any) => mask.layers.map((entry: any) => entry.id)), layerPointCount: layer.splines.reduce((count: number, spline: any) => count + spline.points.length, 0), locked, editable, selected: selected.map((item: any) => item.pointId), toggled, reopenedStatus: reopened.snapshot.trackingMaskStatus, reopenedMaskCount: reopened.snapshot.trackingMasks?.masks.length ?? 0, }; } finally { reopenedClient.terminate(); } } finally { client.terminate(); } }, { bytes: Array.from(fixture), fixtureSha256 }); fs.mkdirSync(path.join(root, "tests/golden/WBV2-P1127-000"), { recursive: true }); fs.writeFileSync(path.join(root, "tests/golden/WBV2-P1127-000/tracking-mask-production.json"), `${JSON.stringify({ schemaVersion: 1, task: "WBV2-P1127-000", operation: "TRACKING_MASK_MAIN_CHROMIUM", browser: "chromium", queueMutation: false, sourceRnaStructs: { "rna-struct:Mask": "snapshot.trackingMasks" }, ...result }, null, 2)}\n`); expect(result.fixtureSha256).toBe(fixtureSha256); expect(result.status).toBe("AVAILABLE"); expect(result.maskIds).toEqual(["mask:WebMask"]); expect(result.layerPointCount).toBeGreaterThan(0); expect(result.locked).toBeNull(); expect(result.editable?.pointId).toBe("mask-point:1:0:0"); expect(result.selected).toEqual(["mask-point:1:0:0"]); expect(result.toggled).toEqual([]); expect(result.reopenedStatus).toBe("AVAILABLE"); expect(result.reopenedMaskCount).toBe(1); });