import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { act, renderHook } from "@testing-library/react";
import type { PropsWithChildren } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useMemoSave } from "@/components/MemoEditor/hooks/useMemoSave";
const mocks = vi.hoisted(() => ({
dispatch: vi.fn(),
markNewMemo: vi.fn(),
memoSave: vi.fn(),
}));
vi.mock("@/components/MemoEditor/services", () => ({
errorService: { getErrorMessage: () => "save failed" },
memoService: { save: mocks.memoSave },
validationService: { canSave: () => ({ valid: true }) },
}));
vi.mock("@/components/MemoEditor/state", () => ({
useEditorContext: () => ({
actions: {
reset: () => ({ type: "reset" }),
setLoading: (key: string, value: boolean) => ({ type: "set-loading", key, value }),
setMetadata: () => ({ type: "set-metadata" }),
setTimestamps: () => ({ type: "set-timestamps" }),
},
dispatch: mocks.dispatch,
getState: () => ({}),
}),
}));
vi.mock("@/contexts/NewMemoContext", () => ({
useNewMemo: () => ({ markNewMemo: mocks.markNewMemo }),
}));
vi.mock("@/utils/i18n", () => ({
useTranslate: () => (key: string) => key,
}));
describe("useMemoSave", () => {
beforeEach(() => {
mocks.dispatch.mockReset();
mocks.markNewMemo.mockReset();
mocks.memoSave.mockReset();
});
it("invalidates scoped attachment libraries after a memo save", async () => {
mocks.memoSave.mockResolvedValue({ hasChanges: true, memoName: "memos/new" });
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
const invalidateQueries = vi.spyOn(queryClient, "invalidateQueries");
const wrapper = ({ children }: PropsWithChildren) => {children};
const discardDraft = vi.fn();
const { result } = renderHook(() => useMemoSave({ discardDraft }), { wrapper });
await act(async () => result.current());
expect(invalidateQueries).toHaveBeenCalledWith({ queryKey: ["attachments", "list"] });
expect(discardDraft).toHaveBeenCalledOnce();
expect(mocks.markNewMemo).toHaveBeenCalledWith("memos/new");
});
it("refreshes the parent memo total after creating a comment", async () => {
mocks.memoSave.mockResolvedValue({ hasChanges: true, memoName: "memos/comment" });
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
const invalidateQueries = vi.spyOn(queryClient, "invalidateQueries");
const wrapper = ({ children }: PropsWithChildren) => {children};
const { result } = renderHook(() => useMemoSave({ parentMemoName: "memos/parent", discardDraft: vi.fn() }), { wrapper });
await act(async () => result.current());
expect(invalidateQueries).toHaveBeenCalledWith({ queryKey: ["memos", "comments", "memos/parent"] });
expect(invalidateQueries).toHaveBeenCalledWith({ queryKey: ["memos", "detail", "memos/parent"] });
});
});