rebase updated the state only, so the mirror the callbacks read still held the removed row and an undo right after it wrote that row back for a moment.
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
// @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<number[]>([]));
|
|
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<number[]>([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<number[]>([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);
|
|
});
|
|
});
|