import { waitFor } from "@testing-library/react"; import { createHttpMockingServer } from "@hyper-fetch/testing"; import { createRequest, renderUseFetch, client } from "../../utils"; const { resetMocks, startServer, stopServer, mockRequest } = createHttpMockingServer(); describe("useFetch [ keepPreviousData ]", () => { beforeAll(() => { startServer(); }); afterEach(() => { resetMocks(); }); afterAll(() => { stopServer(); }); beforeEach(() => { vi.resetModules(); client.clear(); }); describe('when keepPreviousData is "auto" (default)', () => { it("should clear state when URL params change (different resource)", async () => { const request = createRequest({ endpoint: "/users/:userId" }); const requestA = request.setParams({ userId: "1" }); mockRequest(requestA, { data: { name: "Alice" } }); const view = renderUseFetch(requestA, { keepPreviousData: "auto" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Alice" }); }); const requestB = request.setParams({ userId: "2" }); mockRequest(requestB, { data: { name: "Bob" }, delay: 100 }); view.rerender({ request: requestB }); await waitFor(() => { expect(view.result.current.data).toBeNull(); expect(view.result.current.loading).toBe(true); }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Bob" }); }); }); it("should preserve state when only query params change (pagination)", async () => { const request = createRequest({ endpoint: "/items" }); const requestPage1 = request.setQueryParams({ page: "1" } as any); mockRequest(requestPage1, { data: { items: [1, 2, 3] } }); const view = renderUseFetch(requestPage1, { keepPreviousData: "auto" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [1, 2, 3] }); }); const requestPage2 = request.setQueryParams({ page: "2" } as any); mockRequest(requestPage2, { data: { items: [4, 5, 6] }, delay: 100 }); view.rerender({ request: requestPage2 }); // Should preserve previous data while loading expect(view.result.current.data).toStrictEqual({ items: [1, 2, 3] }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [4, 5, 6] }); }); }); }); describe('when keepPreviousData is "preserve"', () => { it("should keep previous data even when URL params change", async () => { const request = createRequest({ endpoint: "/users/:userId" }); const requestA = request.setParams({ userId: "1" }); mockRequest(requestA, { data: { name: "Alice" } }); const view = renderUseFetch(requestA, { keepPreviousData: "preserve" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Alice" }); }); const requestB = request.setParams({ userId: "2" }); mockRequest(requestB, { data: { name: "Bob" }, delay: 100 }); view.rerender({ request: requestB }); // Should preserve previous data expect(view.result.current.data).toStrictEqual({ name: "Alice" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Bob" }); }); }); it("should keep previous data when query params change", async () => { const request = createRequest({ endpoint: "/items" }); const requestPage1 = request.setQueryParams({ page: "1" } as any); mockRequest(requestPage1, { data: { items: [1] } }); const view = renderUseFetch(requestPage1, { keepPreviousData: "preserve" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [1] }); }); const requestPage2 = request.setQueryParams({ page: "2" } as any); mockRequest(requestPage2, { data: { items: [2] }, delay: 100 }); view.rerender({ request: requestPage2 }); expect(view.result.current.data).toStrictEqual({ items: [1] }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [2] }); }); }); }); describe('when keepPreviousData is "clean"', () => { it("should clear state when URL params change", async () => { const request = createRequest({ endpoint: "/users/:userId" }); const requestA = request.setParams({ userId: "1" }); mockRequest(requestA, { data: { name: "Alice" } }); const view = renderUseFetch(requestA, { keepPreviousData: "clean" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Alice" }); }); const requestB = request.setParams({ userId: "2" }); mockRequest(requestB, { data: { name: "Bob" }, delay: 100 }); view.rerender({ request: requestB }); await waitFor(() => { expect(view.result.current.data).toBeNull(); expect(view.result.current.loading).toBe(true); }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Bob" }); }); }); it("should clear state when query params change", async () => { const request = createRequest({ endpoint: "/items" }); const requestPage1 = request.setQueryParams({ page: "1" } as any); mockRequest(requestPage1, { data: { items: [1] } }); const view = renderUseFetch(requestPage1, { keepPreviousData: "clean" }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [1] }); }); const requestPage2 = request.setQueryParams({ page: "2" } as any); mockRequest(requestPage2, { data: { items: [2] }, delay: 100 }); view.rerender({ request: requestPage2 }); await waitFor(() => { expect(view.result.current.data).toBeNull(); expect(view.result.current.loading).toBe(true); }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ items: [2] }); }); }); }); describe("default behavior (no option specified)", () => { it('should default to "auto" mode', async () => { const request = createRequest({ endpoint: "/users/:userId" }); const requestA = request.setParams({ userId: "1" }); mockRequest(requestA, { data: { name: "Alice" } }); const view = renderUseFetch(requestA); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Alice" }); }); const requestB = request.setParams({ userId: "2" }); mockRequest(requestB, { data: { name: "Bob" }, delay: 100 }); view.rerender({ request: requestB }); // Auto mode should clear for param changes await waitFor(() => { expect(view.result.current.data).toBeNull(); }); await waitFor(() => { expect(view.result.current.data).toStrictEqual({ name: "Bob" }); }); }); }); });