diff --git a/src/components/timeline/__tests__/use-life-event-mutations.test.ts b/src/components/timeline/__tests__/use-life-event-mutations.test.ts new file mode 100644 index 000000000..8a973de6c --- /dev/null +++ b/src/components/timeline/__tests__/use-life-event-mutations.test.ts @@ -0,0 +1,42 @@ +/** + * A life event save settles once the server stored it. + * + * The hooks returned the timeline refresh from `onSuccess`, which a mutation + * waits for, so the sheet kept its spinner until the timeline had refetched. + */ +import { describe, expect, it, vi } from "vitest"; + +const client = vi.hoisted(() => ({ + // A refetch that never answers, as on a slow connection. + invalidateQueries: vi.fn(() => new Promise(() => {})), + getQueryCache: () => ({ findAll: () => [] }), +})); + +vi.mock("react", async (importOriginal) => ({ + ...(await importOriginal()), + useRef: (initial: T) => ({ current: initial }), +})); + +vi.mock("@tanstack/react-query", () => ({ + useQuery: (options: unknown) => options, + // The hook's options are what is under test; return them as they are. + useMutation: (options: unknown) => options, + useQueryClient: () => client, +})); + +import { useLifeEventMutations } from "../use-timeline"; + +describe("life event saves", () => { + it.each(["create", "update", "remove"] as const)( + "%s starts the refresh and does not wait for it", + (name) => { + client.invalidateQueries.mockClear(); + const options = useLifeEventMutations()[name] as unknown as { + onSuccess: () => unknown; + }; + + expect(options.onSuccess()).toBeUndefined(); + expect(client.invalidateQueries).toHaveBeenCalled(); + }, + ); +}); diff --git a/src/components/timeline/use-timeline.ts b/src/components/timeline/use-timeline.ts index ab0aaae45..0db31301f 100644 --- a/src/components/timeline/use-timeline.ts +++ b/src/components/timeline/use-timeline.ts @@ -73,8 +73,10 @@ export function useLifeEvents(enabled = true) { export function useLifeEventMutations() { const qc = useQueryClient(); + // Not returned, so the sheet closes without waiting for the timeline to + // refetch. const invalidate = () => - invalidateKeys(qc, [queryKeys.timelineRoot(), queryKeys.dayRoot()]); + void invalidateKeys(qc, [queryKeys.timelineRoot(), queryKeys.dayRoot()]); // One key until a save lands, so a retry after a timeout is not a second // event.