Files
multica/packages/views/settings/components/use-auto-save.test.tsx
2026-07-11 17:26:45 +08:00

86 lines
2.4 KiB
TypeScript

import { act, render, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useAutoSave } from "./use-auto-save";
function deferred<T>() {
let resolve!: (value: T | PromiseLike<T>) => void;
let reject!: (reason?: unknown) => void;
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
resolve = resolvePromise;
reject = rejectPromise;
});
return { promise, resolve, reject };
}
function AutoSaveHarness({
value,
onSave,
onSuccess,
}: {
value: string;
onSave: (value: string) => Promise<void>;
onSuccess: (value: string) => void;
}) {
useAutoSave({
value,
savedValue: "",
onSave,
onSuccess,
delay: 650,
isEqual: (left, right) => left === right,
});
return null;
}
describe("useAutoSave success feedback", () => {
afterEach(() => {
vi.useRealTimers();
});
it("reports success after the persisted value resolves", async () => {
vi.useFakeTimers({ shouldAdvanceTime: true });
const onSave = vi.fn(async () => undefined);
const onSuccess = vi.fn();
render(
<AutoSaveHarness value="saved" onSave={onSave} onSuccess={onSuccess} />,
);
act(() => vi.advanceTimersByTime(650));
await waitFor(() => {
expect(onSave).toHaveBeenCalledWith("saved");
expect(onSuccess).toHaveBeenCalledWith("saved");
});
});
it("waits for the latest queued value before reporting success", async () => {
vi.useFakeTimers({ shouldAdvanceTime: true });
const first = deferred<void>();
const second = deferred<void>();
const onSave = vi
.fn<(value: string) => Promise<void>>()
.mockReturnValueOnce(first.promise)
.mockReturnValueOnce(second.promise);
const onSuccess = vi.fn();
const { rerender } = render(
<AutoSaveHarness value="first" onSave={onSave} onSuccess={onSuccess} />,
);
act(() => vi.advanceTimersByTime(650));
await waitFor(() => expect(onSave).toHaveBeenCalledWith("first"));
rerender(
<AutoSaveHarness value="second" onSave={onSave} onSuccess={onSuccess} />,
);
act(() => vi.advanceTimersByTime(650));
await act(async () => first.resolve());
await waitFor(() => expect(onSave).toHaveBeenCalledWith("second"));
expect(onSuccess).not.toHaveBeenCalled();
await act(async () => second.resolve());
await waitFor(() => expect(onSuccess).toHaveBeenCalledWith("second"));
expect(onSuccess).toHaveBeenCalledTimes(1);
});
});