Files
usememos--memos/web/tests/use-auto-save.test.tsx
wehub-resource-sync 4cddfcf2f3
Backend Tests / Tests (other) (push) Failing after 1s
Backend Tests / Static Checks (push) Failing after 2s
Backend Tests / Tests (internal) (push) Failing after 2s
Backend Tests / Tests (server) (push) Failing after 1s
Backend Tests / Tests (store) (push) Failing after 1s
Build Canary Image / build-frontend (push) Failing after 1s
Frontend Tests / Lint (push) Failing after 1s
Build Canary Image / build-push (linux/amd64) (push) Has been skipped
Build Canary Image / build-push (linux/arm64) (push) Has been skipped
Build Canary Image / merge (push) Has been skipped
Frontend Tests / Build (push) Failing after 1s
Release Please / release-please (push) Failing after 0s
Proto Linter / Lint Protos (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:02:24 +08:00

86 lines
3.0 KiB
TypeScript

import { act, render } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useAutoSave } from "@/components/MemoEditor/hooks/useAutoSave";
import { cacheService } from "@/components/MemoEditor/services/cacheService";
import { EditorProvider, useEditorContext } from "@/components/MemoEditor/state";
// Probe surfaces the store's dispatch/actions plus the autosave API so tests can
// drive content changes the way the editor does and assert on cache writes.
let api: {
dispatch: ReturnType<typeof useEditorContext>["dispatch"];
actions: ReturnType<typeof useEditorContext>["actions"];
discardDraft: () => void;
};
function Probe({ username, cacheKey, enabled }: { username: string; cacheKey?: string; enabled?: boolean }) {
const { dispatch, actions } = useEditorContext();
const { discardDraft } = useAutoSave(username, cacheKey, enabled);
api = { dispatch, actions, discardDraft };
return null;
}
describe("useAutoSave (store-subscribed)", () => {
let saveSpy: ReturnType<typeof vi.spyOn>;
let saveNowSpy: ReturnType<typeof vi.spyOn>;
let clearSpy: ReturnType<typeof vi.spyOn>;
beforeEach(() => {
saveSpy = vi.spyOn(cacheService, "save").mockImplementation(() => {});
saveNowSpy = vi.spyOn(cacheService, "saveNow").mockImplementation(() => {});
clearSpy = vi.spyOn(cacheService, "clear").mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
});
it("persists content to the draft cache when content changes", () => {
render(
<EditorProvider>
<Probe username="users/steven" cacheKey="k" enabled />
</EditorProvider>,
);
const key = cacheService.key("users/steven", "k");
saveSpy.mockClear(); // ignore the mount-time persist of the initial empty content
act(() => {
api.dispatch(api.actions.updateContent("hello world"));
});
expect(saveSpy).toHaveBeenCalledWith(key, "hello world");
});
it("does not persist when disabled", () => {
render(
<EditorProvider>
<Probe username="users/steven" cacheKey="k" enabled={false} />
</EditorProvider>,
);
saveSpy.mockClear();
act(() => {
api.dispatch(api.actions.updateContent("ignored"));
});
expect(saveSpy).not.toHaveBeenCalled();
});
it("discardDraft clears the cache and suppresses the unmount flush", () => {
const { unmount } = render(
<EditorProvider>
<Probe username="users/steven" cacheKey="k2" enabled />
</EditorProvider>,
);
const key = cacheService.key("users/steven", "k2");
act(() => {
api.dispatch(api.actions.updateContent("draft body"));
});
act(() => {
api.discardDraft();
});
expect(clearSpy).toHaveBeenCalledWith(key);
saveNowSpy.mockClear();
unmount();
// The just-discarded content equals the latest content, so the unmount
// flush must NOT re-persist it as a stale draft.
expect(saveNowSpy).not.toHaveBeenCalled();
});
});