Files
workinf_Blender_Wasm/web/tests/e2e/wbv2-lighting-fields-production.spec.ts
mes123456 4a5c70910b
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
Advance deduplicated lighting capability coverage
2026-08-28 12:19:18 +08:00

60 lines
5.9 KiB
TypeScript

import { expect, test } from "@playwright/test";
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
const root = path.resolve(import.meta.dirname, "../../..");
const evidenceRoot = path.join(root, "tests/golden/WBV2-P15-002");
const fixturePath = path.join(root, "tests/files/web/basic_scene.blend");
const fixture = fs.readFileSync(fixturePath);
const fixtureSha256 = crypto.createHash("sha256").update(fixture).digest("hex");
const expected = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-02/lighting-field-roundtrip.json"), "utf8"));
function close(actual: unknown, expectedValue: unknown, tolerance = 1e-5): void {
if (typeof expectedValue === "number") { expect(actual as number).toBeCloseTo(expectedValue, Math.max(0, Math.ceil(-Math.log10(tolerance)))); return; }
if (Array.isArray(expectedValue)) { expect(actual).toHaveLength(expectedValue.length); expectedValue.forEach((value, index) => close((actual as unknown[])[index], value, tolerance)); return; }
if (expectedValue && typeof expectedValue === "object") { for (const [key, value] of Object.entries(expectedValue)) close((actual as Record<string, unknown>)[key], value, tolerance); return; }
expect(actual).toEqual(expectedValue);
}
test("WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main", async ({ page }) => {
test.setTimeout(120_000);
await page.goto("/");
const result = await page.evaluate(async (bytes) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 30_000 });
const reopened = new WebEngineClient({ timeoutMs: 30_000 });
const digest = async (value: ArrayBuffer): Promise<string> => { const hash = await crypto.subtle.digest("SHA-256", value); return Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join(""); };
try {
const opened = await client.openBlend(Uint8Array.from(bytes).buffer);
const camera = opened.snapshot.cameras.find((item) => item.id === "camera:Camera.001")!;
const light = opened.snapshot.lights.find((item) => item.id === "light:Area")!;
const world = opened.snapshot.worlds.find((item) => item.id === "world:World")!;
const beforeRevision = opened.snapshot.revision;
const cameraEdit = await client.applyCommand({ type: "setCameraProperties", dataId: camera.id, properties: { lensMm: 35, sensorWidthMm: 32, sensorHeightMm: 18, sensorFit: 2, shift: [0.1, -0.2], near: 0.2, far: 500, orthoScale: 8, depthOfField: { enabled: true, focusDistance: 4.5, apertureFStop: 1.8, apertureBlades: 7, apertureRotation: 0.25, apertureRatio: 1.2 } } } as any);
const cameraUndo = await client.applyCommand({ type: "undo" });
const cameraRedo = await client.applyCommand({ type: "redo" });
const lightEdit = await client.applyCommand({ type: "setLightProperties", dataId: light.id, properties: { color: [0.25, 0.5, 0.75], energy: 400, exposure: 1, temperature: 5000, useTemperature: true, castsShadow: false, radius: 0.3, spotAngle: 1.1, spotBlend: 0.25, areaSize: 3, areaSizeY: 2, areaSpread: 2.4, sunAngle: 0.1 } } as any);
const lightUndo = await client.applyCommand({ type: "undo" });
const lightRedo = await client.applyCommand({ type: "redo" });
const worldEdit = await client.applyCommand({ type: "setWorldProperties", dataId: world.id, properties: { color: [0.1, 0.2, 0.3], exposure: 0.5, mist: { enabled: true, type: "LINEAR", start: 2, depth: 50, intensity: 0.2, height: 3 } } } as any);
const worldUndo = await client.applyCommand({ type: "undo" });
const worldRedo = await client.applyCommand({ type: "redo" });
const beforeMalformed = (await client.snapshot()).snapshot;
const malformed = await client.applyCommand({ type: "setCameraProperties", dataId: camera.id, properties: { near: 10, far: 1 } } as any).then(() => ({ code: "NO_ERROR" })).catch((error: any) => ({ code: error?.code, message: error?.message }));
const afterMalformed = (await client.snapshot()).snapshot;
const saved = await client.saveBlend();
const reopenedResult = await reopened.openBlend(saved);
return { browser: "chromium", sourceSha256: await digest(Uint8Array.from(bytes).buffer), beforeRevision, revisions: [beforeRevision, cameraEdit.snapshot.revision, cameraUndo.snapshot.revision, cameraRedo.snapshot.revision, lightEdit.snapshot.revision, lightUndo.snapshot.revision, lightRedo.snapshot.revision, worldEdit.snapshot.revision, worldUndo.snapshot.revision, worldRedo.snapshot.revision], malformed, malformedRevisionPreserved: afterMalformed.revision === beforeMalformed.revision, camera: reopenedResult.snapshot.cameras.find((item) => item.id === camera.id), light: reopenedResult.snapshot.lights.find((item) => item.id === light.id), world: reopenedResult.snapshot.worlds.find((item) => item.id === world.id), reopenedRevision: reopenedResult.snapshot.revision, savedSha256: await digest(saved) };
} finally { client.terminate(); reopened.terminate(); }
}, Array.from(fixture));
fs.mkdirSync(evidenceRoot, { recursive: true });
const output = process.env.WBV2_LIGHTING_REPORT ?? "lighting-fields-production.json";
fs.writeFileSync(path.join(evidenceRoot, output), `${JSON.stringify({ schemaVersion: 1, task: "WBV2-P15-002", operation: "LIGHTING_FIELDS_ENGINE_CHROMIUM", queueMutation: false, ...result }, null, 2)}\n`);
expect(result.browser).toBe("chromium"); expect(result.sourceSha256).toBe(fixtureSha256); expect(result.revisions).toEqual(result.revisions.map((_, index) => result.beforeRevision + index)); expect(result.malformed.code).not.toBe("NO_ERROR"); expect(result.malformedRevisionPreserved).toBe(true);
close(result.camera, Object.fromEntries(Object.entries(expected.camera).filter(([key]) => !key.startsWith("viewport"))));
close(result.light, Object.fromEntries(Object.entries(expected.light).filter(([key]) => !key.startsWith("viewport"))));
close(result.world, expected.world);
});