import { addSensorPoint, createSchemeEditorState, deleteSensorPoint, isSchemeDirty, redoSchemeEdit, replaceSensorPoint, resetSchemeEdit, summarizeChanges, undoSchemeEdit, } from "./schemeEditor"; import type { SensorPlacementScheme, SensorPoint } from "./types"; const point = (node_id: string): SensorPoint => ({ node_id, project_x: Number(node_id.slice(1)) * 10, project_y: Number(node_id.slice(1)) * 20, map_x: 13500000 + Number(node_id.slice(1)) * 10, map_y: 3600000 + Number(node_id.slice(1)) * 20, longitude: 121, latitude: 31, elevation: 5, }); const scheme: SensorPlacementScheme = { id: 1, scheme_name: "测试方案", sensor_number: 2, min_diameter: 300, username: "alice", create_time: "2026-07-30T08:00:00+08:00", sensor_location: ["J1", "J2"], sensor_points: [point("J1"), point("J2")], can_edit: true, }; describe("scheme editor", () => { it("adds unique nodes and can undo", () => { const initial = createSchemeEditorState(scheme); const added = addSensorPoint(initial, point("J3")); expect(added.points.map((item) => item.node_id)).toEqual(["J1", "J2", "J3"]); expect(added.statuses.J3).toBe("added"); expect(isSchemeDirty(added)).toBe(true); expect(undoSchemeEdit(added).points).toEqual(initial.points); }); it("can redo an undone edit and clears redo history after a new edit", () => { const initial = createSchemeEditorState(scheme); const added = addSensorPoint(initial, point("J3")); const undone = undoSchemeEdit(added); const redone = redoSchemeEdit(undone); expect(redone.points.map((item) => item.node_id)).toEqual([ "J1", "J2", "J3", ]); expect(redone.history).toHaveLength(1); expect(redone.future).toEqual([]); const editedAfterUndo = replaceSensorPoint(undone, "J1", point("J4")); expect(editedAfterUndo.future).toEqual([]); }); it("replaces a node without changing row order", () => { const initial = createSchemeEditorState(scheme); const replaced = replaceSensorPoint(initial, "J1", point("J3")); expect(replaced.points.map((item) => item.node_id)).toEqual(["J3", "J2"]); expect(replaced.statuses.J3).toBe("replaced"); expect(summarizeChanges(replaced)).toEqual({ added: 0, removed: 0, replaced: 1, }); }); it("keeps an added status when replacing a newly added node", () => { const added = addSensorPoint(createSchemeEditorState(scheme), point("J3")); const replaced = replaceSensorPoint(added, "J3", point("J4")); expect(replaced.statuses.J3).toBeUndefined(); expect(replaced.statuses.J4).toBe("added"); }); it("rejects duplicate replacements and deleting the final row", () => { const initial = createSchemeEditorState(scheme); expect(replaceSensorPoint(initial, "J1", point("J2"))).toBe(initial); const oneLeft = deleteSensorPoint(initial, "J1"); expect(deleteSensorPoint(oneLeft, "J2")).toBe(oneLeft); }); it("resets to the loaded baseline", () => { const edited = addSensorPoint(createSchemeEditorState(scheme), point("J3")); const reset = resetSchemeEdit(edited); expect(reset.points.map((item) => item.node_id)).toEqual(["J1", "J2"]); expect(isSchemeDirty(reset)).toBe(false); expect(reset.history).toEqual([]); expect(reset.future).toEqual([]); }); });