// @vitest-environment jsdom // // The undo container chains calls made in one tick: a `reset` not yet committed is what the next // call builds on. A `rebase` read the mirror ref, which only follows a commit, so it rebased the // value from BEFORE the reset and wrote that back — the playlist page, remounted under StrictMode // over a cached list, re-ran its seeding effect (reset, then rebase) and painted an empty list. import { describe, expect, it } from "vitest"; import { act, renderHook } from "@testing-library/react"; import { useUndoable } from "./useUndoable"; describe("useUndoable", () => { it("rebases the value a reset in the same tick installed, not the one before it", () => { const { result } = renderHook(() => useUndoable([])); act(() => { result.current.reset([1, 2]); result.current.rebase((xs) => xs.map((x) => x * 10)); }); expect(result.current.value).toEqual([10, 20]); }); it("keeps the undo history through a rebase", () => { const { result } = renderHook(() => useUndoable([1])); act(() => result.current.set([1, 2])); act(() => result.current.rebase((xs) => xs.map((x) => x + 1))); expect(result.current.value).toEqual([2, 3]); act(() => result.current.undo()); expect(result.current.value).toEqual([2]); }); it("undoes to a rebased snapshot when the undo comes in the same tick as the rebase", () => { const { result } = renderHook(() => useUndoable([1, 2])); act(() => result.current.set([2, 1])); act(() => { result.current.rebase((xs) => xs.filter((x) => x !== 2)); // a row removed result.current.undo(); }); expect(result.current.value).toEqual([1]); expect(result.current.canRedo).toBe(true); }); });