tsx runs tests without type checking and tsconfig.json covers only src, so 71 type errors had accumulated unnoticed. tsconfig.tests.json now checks every test and helper, and npm run typecheck includes it. Fixtures gain the fields the DTOs have grown since they were written (manualQuantity, circuit apparent power/cos phi, downstream board loads, section totals, building, cover and board info fields). Repository lookups that return "row ?? null" now declare the null in their type, which their callers already handle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
3.8 KiB
TypeScript
132 lines
3.8 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 {
|
|
circuitListId: "list-1",
|
|
distributionBoardId: "board-1",
|
|
distributionBoardName: "UV 1",
|
|
currentRevision,
|
|
singlePhaseVoltageV: 230,
|
|
threePhaseVoltageV: 400,
|
|
distributionBoardSimultaneityFactor: 1,
|
|
distributionBoardTotalPower: 0,
|
|
distributionBoardTotalPowerWithSimultaneityFactor: 0,
|
|
headerComponents: [],
|
|
sections: [],
|
|
footerComponents: [],
|
|
};
|
|
}
|
|
|
|
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 },
|
|
},
|
|
]);
|
|
});
|
|
});
|