import { expect, it } from "bun:test"; import { ExtendedPatch } from "../extended"; import { StateManager } from "../stateManager"; type Item = { id: string; name: string }; it("rewinds and operates on state", () => { const sm = new StateManager<{ count: number; name?: string }, void>({ count: 0, name: "original", }); sm.edit((draft) => { draft.count++; }); sm.edit((draft) => { draft.count++; }); expect(sm.state.count).toEqual(2); sm._rewindToIndex(0, (state) => { expect(state.count).toEqual(0); return { ...state, name: "updated", count: 10 }; }); expect(sm.state).toEqual({ count: 2, name: "updated" }); }); it("gets state at index", () => { const sm = new StateManager<{ count: number; name?: string }, void>({ count: 0, }); sm.edit((draft) => { draft.count++; }); sm.edit((draft) => { draft.count++; }); expect(sm.state.count).toEqual(2); expect(sm.getStateAtHistoryIndex(0).count).toEqual(0); expect(sm.getStateAtHistoryIndex(1).count).toEqual(1); expect(sm.getStateAtHistoryIndex(2).count).toEqual(2); }); it("gets array state at index", () => { const a = { id: "a", name: "item" }; const b = { id: "b", name: "item" }; const sm = new StateManager<{ items: Item[] }>({ items: [a], }); sm.operate(({ add }) => { add("items/-", b); }); expect(sm.state.items).toEqual([a, b]); expect(sm.getStateAtHistoryIndex(0).items).toEqual([a]); }); it("accepts patch at base", () => { const sm = new StateManager<{ count: number; name?: string }, void>({ count: 0, name: "original", }); sm.edit((draft) => { draft.count++; }); expect(sm.state).toEqual({ count: 1, name: "original" }); sm.acceptPatchesAtIndex(0, [ { op: "replace", path: ["name"], value: "updated" }, ]); expect(sm.state).toEqual({ count: 1, name: "updated" }); }); it("accepts patch that adds array item", () => { const sm = new StateManager<{ items: Item[] }, { patchId: string }>({ items: [], }); const a = { id: "a", name: "item" }; const patch: ExtendedPatch = { op: "add", path: ["items", "-"], value: a, }; sm.acceptPatchesAtIndex(0, [patch]); expect(sm.state.items).toEqual([a]); expect(sm.history.length).toEqual(0); }); it("accepts patch that adds array item and ignores own patch in history", () => { const sm = new StateManager<{ items: Item[] }, { patchId: string }>({ items: [], }); const a = { id: "a", name: "item" }; sm.operate({ patchId: "p1" }, ({ add }) => { add("items/-", a); }); const patch = sm.history[0].redoPatches![0]; expect(patch).toEqual({ op: "add", path: ["items", "-"], value: a, }); expect(sm.history[0].undoPatches).toEqual([ { op: "remove", path: ["items", { id: "a" }], }, ]); expect(sm.state.items).toEqual([a]); sm.acceptPatchesAtIndex(0, [patch], (entry) => { return entry.metadata.patchId !== "p1"; }); expect(sm.state.items).toEqual([a]); });