Files
workinf_Blender_Wasm/web/tests/e2e/wbv2-modifier-reader-multi-fixture-production.spec.ts
mes123456 bd00c932c7
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(web): continuous handoff WBV2-P1156-001
2026-09-03 16:31:15 +08:00

94 lines
4.7 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 = JSON.parse(process.env.WBV2_BATCH_FIXTURES ?? JSON.stringify(process.env.WBV2_BATCH_FIXTURE ? [process.env.WBV2_BATCH_FIXTURE] : [])) as string[];
const task = process.env.WBV2_BATCH_TASK ?? "WBV2-P1152-001";
const productionPath = process.env.WBV2_BATCH_PRODUCTION ?? `tests/golden/${task}/modifier-reader-batch-production.json`;
const expectedTypes = JSON.parse(process.env.WBV2_BATCH_TYPES ?? "[]") as string[];
const sourceUnits = JSON.parse(process.env.WBV2_BATCH_UNITS ?? "[]") as string[];
if (!fixtures.length || !expectedTypes.length || !sourceUnits.length) throw new Error("multi-fixture batch requires fixtures, expected types and source units");
const fixtureBytes = Object.fromEntries(fixtures.map((name) => [name, Array.from(fs.readFileSync(path.join(root, name)))]));
const fixtureSha256 = Object.fromEntries(fixtures.map((name) => [name, crypto.createHash("sha256").update(Buffer.from(fixtureBytes[name])).digest("hex")]));
test("multi-fixture modifier reader batch preserves Blender Main metadata", 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 ?? []), ...(snapshot.nodes ?? []), ...(snapshot.volumes ?? []), ...(snapshot.objects ?? [])].flatMap((mesh: any) =>
(mesh.modifierStack ?? []).map((modifier: any) => ({
meshId: mesh.id,
uuid: modifier.uuid,
name: modifier.name,
type: modifier.type,
enabled: modifier.enabled,
evaluationStatus: modifier.evaluationStatus,
parameters: modifier.parameters ?? null,
})));
const output: Record<string, 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 });
const secondClient = new WebEngineClient({ timeoutMs: 60_000 });
try {
const opened = await client.openBlend(Uint8Array.from(bytes).buffer);
const before = canonical(opened.snapshot);
const saved = await client.saveBlend();
const after = canonical((await reopenedClient.openBlend(saved.slice(0))).snapshot);
const second = canonical((await secondClient.openBlend(Uint8Array.from(bytes).buffer)).snapshot);
output[name] = {
before,
types: before.map((modifier: any) => modifier.type),
after,
second,
saveReopenExact: JSON.stringify(before) === JSON.stringify(after),
deterministicExact: JSON.stringify(before) === JSON.stringify(second),
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[name].malformed = { code: "ACCEPTED_MALFORMED" };
} catch (error: any) {
output[name].malformed = { code: error?.code, message: error?.message };
}
} catch (error: any) {
output[name] = { error: { code: error?.code, message: error?.message, detail: String(error) } };
} finally {
client.terminate();
reopenedClient.terminate();
secondClient.terminate();
}
}
return output;
}, { fixtures: fixtureBytes });
const report = {
schemaVersion: 1,
task,
operation: "MODIFIER_READER_MULTI_FIXTURE_CHROMIUM_SAVE_REOPEN",
browser: "chromium",
queueMutation: false,
fixtures,
fixtureSha256,
expectedTypes,
sourceUnits,
...result,
};
fs.mkdirSync(path.join(root, `tests/golden/${task}`), { recursive: true });
fs.mkdirSync(path.dirname(path.join(root, productionPath)), { recursive: true });
fs.writeFileSync(path.join(root, productionPath), `${JSON.stringify(report, null, 2)}\n`);
const observedTypes = new Set(Object.values(result).flatMap((entry: any) => entry?.types ?? []));
for (const type of expectedTypes) expect(observedTypes.has(type)).toBe(true);
for (const fixture of fixtures) {
expect(result[fixture]?.error).toBeUndefined();
expect(result[fixture]?.saveReopenExact).toBe(true);
expect(result[fixture]?.deterministicExact).toBe(true);
expect(result[fixture]?.malformed?.code).not.toBe("ACCEPTED_MALFORMED");
}
});