- 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>
62 lines
3.5 KiB
TypeScript
62 lines
3.5 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const root = path.resolve(import.meta.dirname, "../../..");
|
|
const fixture = fs.readFileSync(path.join(root, "tests/files/web/basic_scene.blend"));
|
|
const receiptDir = path.join(root, "tests/golden/WBV2-P1425-001/verification");
|
|
fs.mkdirSync(receiptDir, { recursive: true });
|
|
|
|
const types = [
|
|
"WEIGHTED_NORMAL", "CORRECTIVE_SMOOTH", "LAPLACIANSMOOTH", "MASK", "NORMAL_EDIT",
|
|
"SKIN", "UV_PROJECT", "UV_WARP", "WARP", "DATA_TRANSFER", "VERTEX_WEIGHT_EDIT",
|
|
"VERTEX_WEIGHT_MIX", "VERTEX_WEIGHT_PROXIMITY", "LAPLACIANDEFORM", "PARTICLE_INSTANCE",
|
|
"SURFACE", "MESH_CACHE", "MESH_SEQUENCE_CACHE", "MULTIRES", "EXPLODE",
|
|
];
|
|
|
|
test("expanded modifier whitelist adds, evaluates and removes in native Main", async ({ page }) => {
|
|
test.setTimeout(180_000);
|
|
await page.goto("/?engine=manual");
|
|
const result = await page.evaluate(async ({ bytes, types }) => {
|
|
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
|
|
const client = new WebEngineClient({ timeoutMs: 60_000 });
|
|
const reopened = new WebEngineClient({ timeoutMs: 60_000 });
|
|
const cases: Record<string, unknown>[] = [];
|
|
try {
|
|
const open = await client.openBlend(Uint8Array.from(bytes).buffer);
|
|
const node = open.snapshot.nodes.find((n) => n.type === "MESH");
|
|
if (!node?.dataId) throw new Error("mesh fixture is missing");
|
|
const meshId = node.dataId;
|
|
let revision = open.snapshot.revision;
|
|
for (const modifierType of types) {
|
|
try {
|
|
const added = await client.applyCommand({ type: "addModifier", objectId: node.id, meshId, modifierType, baseRevision: revision } as never);
|
|
revision = added.snapshot.revision;
|
|
const stack = added.snapshot.meshes.find((m) => m.id === meshId)?.modifierStack ?? [];
|
|
const modifier = stack.at(-1);
|
|
if (!modifier?.uuid) throw new Error("modifier was not published");
|
|
const removed = await client.applyCommand({ type: "removeModifier", objectId: node.id, meshId, modifierUuid: modifier.uuid, baseRevision: revision } as never);
|
|
revision = removed.snapshot.revision;
|
|
const remaining = removed.snapshot.meshes.find((m) => m.id === meshId)?.modifierStack ?? [];
|
|
cases.push({ modifierType, status: remaining.some((m) => m.uuid === modifier.uuid) ? "REMOVE_FAILED" : "FINISHED", publishedType: modifier.type, revision });
|
|
} catch (error) {
|
|
const e = error as { message?: string; code?: string };
|
|
cases.push({ modifierType, status: "ERROR", message: e?.message, code: e?.code });
|
|
}
|
|
}
|
|
const saved = await client.saveBlend();
|
|
client.terminate();
|
|
const reopen = await reopened.openBlend(saved);
|
|
return { ok: true, cases, reopenRevision: reopen.snapshot.revision };
|
|
} catch (error) {
|
|
const e = error as { message?: string; code?: string };
|
|
return { ok: false, cases, message: e?.message, code: e?.code };
|
|
} finally { client.terminate(); reopened.terminate(); }
|
|
}, { bytes: Array.from(fixture), types });
|
|
fs.writeFileSync(path.join(receiptDir, "modifier-matrix-production.json"), JSON.stringify({ schemaVersion: 1, browser: "chromium", scope: "20 expanded modifiers add/publication/remove/save/reopen", ...result }, null, 2));
|
|
expect(result.ok).toBe(true);
|
|
expect(result.reopenRevision).toBe(1);
|
|
const failed = (result.cases as Record<string, unknown>[]).filter((item) => item.status !== "FINISHED");
|
|
expect(failed).toEqual([]);
|
|
});
|