Files
workinf_Blender_Wasm/web/tests/e2e/lighting-field-roundtrip.spec.ts
mes123456 0fe8d2bb56
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

144 lines
8.2 KiB
TypeScript

import { expect, test } from "@playwright/test";
import fs from "node:fs";
import path from "node:path";
const root = path.resolve(import.meta.dirname, "../../..");
const blend = fs.readFileSync(path.join(root, "tests/files/web/basic_scene.blend"));
const expected = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-02/lighting-field-roundtrip.json"), "utf8"));
function expectClose(actual: unknown, reference: unknown): void {
if (typeof reference === "number") {
expect(actual).toBeCloseTo(reference, 5);
return;
}
if (Array.isArray(reference)) {
expect(Array.isArray(actual)).toBe(true);
expect(actual).toHaveLength(reference.length);
reference.forEach((value, index) => expectClose((actual as unknown[])[index], value));
return;
}
if (reference && typeof reference === "object") {
expect(actual && typeof actual === "object").toBe(true);
for (const [field, value] of Object.entries(reference)) {
expectClose((actual as Record<string, unknown>)[field], value);
}
return;
}
expect(actual).toEqual(reference);
}
test("M11-02 edits, undoes, redoes, reopens and maps supported lighting fields", async ({ page }) => {
test.setTimeout(60_000);
await page.goto("/");
const result = await page.evaluate(async (input) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const { configurePBRCamera, configurePBRLight, createPBRLight } = await import("/src/three-adapter/pbr.ts");
const { Object3D, PerspectiveCamera } = await import("/src/vendor/three/three.module.js");
const client = new WebEngineClient({ timeoutMs: 30_000 });
const reopened = new WebEngineClient({ timeoutMs: 30_000 });
try {
const opened = await client.openBlend(Uint8Array.from(input).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 revisions = [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 },
} });
revisions.push(cameraEdit.snapshot.revision);
const cameraUndo = await client.applyCommand({ type: "undo" });
revisions.push(cameraUndo.snapshot.revision);
const cameraRedo = await client.applyCommand({ type: "redo" });
revisions.push(cameraRedo.snapshot.revision);
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,
} });
revisions.push(lightEdit.snapshot.revision);
const lightUndo = await client.applyCommand({ type: "undo" });
revisions.push(lightUndo.snapshot.revision);
const lightRedo = await client.applyCommand({ type: "redo" });
revisions.push(lightRedo.snapshot.revision);
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 },
} });
revisions.push(worldEdit.snapshot.revision);
const worldUndo = await client.applyCommand({ type: "undo" });
revisions.push(worldUndo.snapshot.revision);
const worldRedo = await client.applyCommand({ type: "redo" });
revisions.push(worldRedo.snapshot.revision);
const saved = await client.saveBlend();
const reopenedResult = await reopened.openBlend(saved);
const reopenedCamera = reopenedResult.snapshot.cameras.find((item) => item.id === camera.id)!;
const reopenedLight = reopenedResult.snapshot.lights.find((item) => item.id === light.id)!;
const reopenedWorld = reopenedResult.snapshot.worlds.find((item) => item.id === world.id)!;
const mappedCamera = new PerspectiveCamera();
configurePBRCamera(mappedCamera, reopenedCamera);
const horizontalCamera = new PerspectiveCamera();
configurePBRCamera(horizontalCamera, { ...reopenedCamera, sensorFit: 1 });
const spot = createPBRLight({ ...reopenedLight, lightType: 2 });
const area = createPBRLight({ ...reopenedLight, lightType: 4 });
const lightNode = reopenedResult.snapshot.nodes.find((item) => item.dataId === light.id)!;
configurePBRLight(spot, lightNode, new Object3D());
return {
revisions,
cameraUndoLens: cameraUndo.snapshot.cameras.find((item) => item.id === camera.id)!.lensMm,
cameraRedoLens: cameraRedo.snapshot.cameras.find((item) => item.id === camera.id)!.lensMm,
lightUndoEnergy: lightUndo.snapshot.lights.find((item) => item.id === light.id)!.energy,
lightRedoEnergy: lightRedo.snapshot.lights.find((item) => item.id === light.id)!.energy,
worldUndoColor: worldUndo.snapshot.worlds.find((item) => item.id === world.id)!.color,
worldRedoColor: worldRedo.snapshot.worlds.find((item) => item.id === world.id)!.color,
camera: reopenedCamera,
light: reopenedLight,
world: reopenedWorld,
viewport: {
camera: { fov: mappedCamera.fov, horizontalFov: horizontalCamera.fov, near: mappedCamera.near, far: mappedCamera.far, filmGauge: mappedCamera.filmGauge, filmOffset: mappedCamera.filmOffset },
spot: { color: spot.color.toArray(), intensity: spot.intensity, angle: "angle" in spot ? spot.angle : 0, penumbra: "penumbra" in spot ? spot.penumbra : 0, castShadow: spot.castShadow },
area: { intensity: area.intensity, width: "width" in area ? area.width : 0, height: "height" in area ? area.height : 0 },
worldColor: reopenedWorld.color,
},
};
}
finally {
client.terminate();
reopened.terminate();
}
}, Array.from(blend));
expect(result.revisions.every((revision, index) => index === 0 || revision === result.revisions[index - 1] + 1)).toBe(true);
expect(result.cameraUndoLens).toBe(50);
expect(result.cameraRedoLens).toBe(expected.camera.lensMm);
expect(result.lightUndoEnergy).toBe(800);
expect(result.lightRedoEnergy).toBe(expected.light.energy);
expect(result.worldUndoColor).not.toEqual(expected.world.color);
expectClose(result.worldRedoColor, expected.world.color);
for (const [field, value] of Object.entries(expected.camera).filter(([field]) => !field.startsWith("viewport"))) {
expectClose(result.camera[field as keyof typeof result.camera], value);
}
for (const [field, value] of Object.entries(expected.light).filter(([field]) => !field.startsWith("viewport"))) {
expectClose(result.light[field as keyof typeof result.light], value);
}
expectClose(result.world, expected.world);
expect(result.viewport.camera.fov).toBeCloseTo(expected.camera.viewportFov, 5);
expect(result.viewport.camera.horizontalFov).toBeCloseTo(expected.camera.viewportHorizontalFov, 5);
expectClose(result.viewport.camera.near, expected.camera.near);
expectClose(result.viewport.camera.far, expected.camera.far);
expectClose(result.viewport.camera.filmGauge, expected.camera.viewportFilmGauge);
expect(result.viewport.camera.filmOffset).toBeCloseTo(expected.camera.viewportFilmOffset, 6);
expectClose(result.viewport.spot.intensity, expected.light.viewportIntensity);
expectClose(result.viewport.spot.color, expected.light.viewportColor);
expectClose(result.viewport.spot.angle, expected.light.spotAngle);
expectClose(result.viewport.spot.penumbra, expected.light.spotBlend);
expect(result.viewport.spot.castShadow).toBe(false);
expectClose(result.viewport.area, { intensity: expected.light.viewportIntensity, width: expected.light.areaSize, height: expected.light.areaSizeY });
expectClose(result.viewport.worldColor, expected.world.color);
});