127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { loadCircuitEditorSnapshot } from "../src/frontend/utils/circuit-editor-history.js";
|
|
import {
|
|
getProjectHistory,
|
|
redoProjectCommand,
|
|
undoProjectCommand,
|
|
} from "../src/frontend/utils/api.js";
|
|
import type {
|
|
CircuitTreeResponseDto,
|
|
ProjectHistoryStateDto,
|
|
} from "../src/frontend/types.js";
|
|
|
|
function tree(currentRevision: number): CircuitTreeResponseDto {
|
|
return {
|
|
id: "list-1",
|
|
projectId: "project-1",
|
|
distributionBoardId: "board-1",
|
|
name: "Liste",
|
|
currentRevision,
|
|
sections: [],
|
|
};
|
|
}
|
|
|
|
function history(currentRevision: number): ProjectHistoryStateDto {
|
|
return {
|
|
projectId: "project-1",
|
|
currentRevision,
|
|
undoDepth: currentRevision,
|
|
redoDepth: 0,
|
|
undoChangeSetId: currentRevision > 0 ? `change-${currentRevision}` : null,
|
|
redoChangeSetId: null,
|
|
};
|
|
}
|
|
|
|
describe("circuit editor history snapshot", () => {
|
|
it("returns tree and persistent history from the same project revision", async () => {
|
|
const snapshot = await loadCircuitEditorSnapshot(
|
|
async () => tree(4),
|
|
async () => history(4)
|
|
);
|
|
|
|
assert.equal(snapshot.tree.currentRevision, 4);
|
|
assert.equal(snapshot.history.undoDepth, 4);
|
|
});
|
|
|
|
it("retries when the project changes between the tree and history reads", async () => {
|
|
const treeRevisions = [4, 5];
|
|
const historyRevisions = [5, 5];
|
|
let attempts = 0;
|
|
|
|
const snapshot = await loadCircuitEditorSnapshot(
|
|
async () => tree(treeRevisions[attempts]),
|
|
async () => history(historyRevisions[attempts++])
|
|
);
|
|
|
|
assert.equal(attempts, 2);
|
|
assert.equal(snapshot.tree.currentRevision, 5);
|
|
assert.equal(snapshot.history.currentRevision, 5);
|
|
});
|
|
|
|
it("rejects a snapshot that remains inconsistent", async () => {
|
|
await assert.rejects(
|
|
() =>
|
|
loadCircuitEditorSnapshot(
|
|
async () => tree(4),
|
|
async () => history(5),
|
|
2
|
|
),
|
|
/während des Ladens geändert/
|
|
);
|
|
});
|
|
|
|
it("loads history and sends revision-safe undo/redo requests", async () => {
|
|
const requests: Array<{ url: string; body?: unknown }> = [];
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = async (input, init) => {
|
|
const url = String(input);
|
|
requests.push({
|
|
url,
|
|
...(init?.body
|
|
? { body: JSON.parse(String(init.body)) as unknown }
|
|
: {}),
|
|
});
|
|
const currentRevision = requests.length - 1;
|
|
const state = history(currentRevision);
|
|
return new Response(
|
|
JSON.stringify(
|
|
init?.method === "POST"
|
|
? {
|
|
revision: {
|
|
revisionId: `revision-${currentRevision}`,
|
|
changeSetId: `change-${currentRevision}`,
|
|
projectId: "project-1",
|
|
revisionNumber: currentRevision,
|
|
createdAtIso: "2026-07-25T00:00:00.000Z",
|
|
},
|
|
history: state,
|
|
}
|
|
: state
|
|
),
|
|
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
);
|
|
};
|
|
|
|
try {
|
|
await getProjectHistory("project-1");
|
|
await undoProjectCommand("project-1", 4);
|
|
await redoProjectCommand("project-1", 5);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
|
|
assert.deepEqual(requests, [
|
|
{ url: "/api/projects/project-1/history" },
|
|
{
|
|
url: "/api/projects/project-1/history/undo",
|
|
body: { expectedRevision: 4 },
|
|
},
|
|
{
|
|
url: "/api/projects/project-1/history/redo",
|
|
body: { expectedRevision: 5 },
|
|
},
|
|
]);
|
|
});
|
|
});
|