Advance M8-M11 parity workflows
This commit is contained in:
100
web/tests/e2e/nla-operator.spec.ts
Normal file
100
web/tests/e2e/nla-operator.spec.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const fixture = fs.readFileSync(path.resolve(
|
||||
import.meta.dirname,
|
||||
"../../../tests/files/web/nla_time_mapping_scene.blend",
|
||||
));
|
||||
|
||||
test("M10-12 moves one NLA strip through Main, undo, redo and save/reopen", 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 source = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
|
||||
const first = new WebEngineClient({ timeoutMs: 60_000 });
|
||||
await first.init();
|
||||
const opened = await first.openBlend(source);
|
||||
const track = opened.snapshot.nlaTracks?.find((candidate) => candidate.name === "M10 Time Mapping");
|
||||
const strip = track?.strips.find((candidate) => candidate.id === "M10 Scaled Clip");
|
||||
if (!track || !strip) throw new Error("NLA move fixture is incomplete");
|
||||
const initialRevision = opened.snapshot.revision;
|
||||
const initialTracks = JSON.stringify(opened.snapshot.nlaTracks);
|
||||
|
||||
let staleCode = "";
|
||||
try {
|
||||
await first.applyCommand({
|
||||
type: "moveNLAStrip",
|
||||
objectId: track.ownerId,
|
||||
trackId: track.id,
|
||||
stripId: strip.id,
|
||||
frameStart: 5,
|
||||
baseRevision: initialRevision - 1,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
staleCode = (error as { code?: string }).code ?? "";
|
||||
}
|
||||
const afterStale = await first.snapshot();
|
||||
|
||||
const moved = await first.applyCommand({
|
||||
type: "moveNLAStrip",
|
||||
objectId: track.ownerId,
|
||||
trackId: track.id,
|
||||
stripId: strip.id,
|
||||
frameStart: 5,
|
||||
baseRevision: initialRevision,
|
||||
});
|
||||
const movedStrip = moved.snapshot.nlaTracks?.find((candidate) => candidate.name === track.name)
|
||||
?.strips.find((candidate) => candidate.id === strip.id);
|
||||
|
||||
const undone = await first.applyCommand({ type: "undo" });
|
||||
const undoneStrip = undone.snapshot.nlaTracks?.find((candidate) => candidate.name === track.name)
|
||||
?.strips.find((candidate) => candidate.id === strip.id);
|
||||
const redone = await first.applyCommand({ type: "redo" });
|
||||
const redoneStrip = redone.snapshot.nlaTracks?.find((candidate) => candidate.name === track.name)
|
||||
?.strips.find((candidate) => candidate.id === strip.id);
|
||||
const saved = await first.saveBlend();
|
||||
first.terminate();
|
||||
|
||||
const second = new WebEngineClient({ timeoutMs: 60_000 });
|
||||
await second.init();
|
||||
const reopened = await second.openBlend(saved);
|
||||
const reopenedStrip = reopened.snapshot.nlaTracks?.find((candidate) => candidate.name === track.name)
|
||||
?.strips.find((candidate) => candidate.id === strip.id);
|
||||
await second.applyCommand({ type: "setFrame", frame: 10 });
|
||||
const report = await second.evaluateDepsgraph();
|
||||
const mesh = report.depsgraph.meshes.find((candidate) => candidate.objectId === track.ownerId);
|
||||
second.terminate();
|
||||
|
||||
return {
|
||||
staleCode,
|
||||
stalePreserved: JSON.stringify(afterStale.snapshot.nlaTracks) === initialTracks,
|
||||
revisions: [initialRevision, moved.snapshot.revision, undone.snapshot.revision, redone.snapshot.revision],
|
||||
delta: [moved.delta.baseRevision, moved.delta.nextRevision],
|
||||
moved: movedStrip ? [movedStrip.frameStart, movedStrip.frameEnd] : null,
|
||||
undone: undoneStrip ? [undoneStrip.frameStart, undoneStrip.frameEnd] : null,
|
||||
redone: redoneStrip ? [redoneStrip.frameStart, redoneStrip.frameEnd] : null,
|
||||
reopened: reopenedStrip ? [reopenedStrip.frameStart, reopenedStrip.frameEnd] : null,
|
||||
evaluatedX: mesh?.worldMatrix[3] ?? null,
|
||||
evaluationStatus: report.depsgraph.status,
|
||||
};
|
||||
}, { bytes: new Uint8Array(fixture) });
|
||||
|
||||
expect(result.staleCode).toBe("REVISION_CONFLICT");
|
||||
expect(result.stalePreserved).toBe(true);
|
||||
expect(result.revisions).toEqual([
|
||||
result.revisions[0],
|
||||
result.revisions[0] + 1,
|
||||
result.revisions[0] + 2,
|
||||
result.revisions[0] + 3,
|
||||
]);
|
||||
expect(result.delta).toEqual([result.revisions[0], result.revisions[0] + 1]);
|
||||
expect(result.moved).toEqual([5, 25]);
|
||||
expect(result.undone).toEqual([20, 40]);
|
||||
expect(result.redone).toEqual([5, 25]);
|
||||
expect(result.reopened).toEqual([5, 25]);
|
||||
expect(result.evaluationStatus).toBe("EVALUATED");
|
||||
expect(result.evaluatedX).toBeCloseTo(2.5, 5);
|
||||
});
|
||||
Reference in New Issue
Block a user