diff --git a/.size-limits.json b/.size-limits.json index b13d95f84a8..09bf55362fa 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39605, + "dist/apollo-client.min.cjs": 39604, "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32852 } diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx index 34981cf9b4e..08f94df5c60 100644 --- a/src/react/hooks/__tests__/useLazyQuery.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -26,8 +26,6 @@ import { useLazyQuery } from "../useLazyQuery"; import { QueryResult } from "../../types/types"; import { profileHook } from "../../../testing/internal"; -const IS_REACT_18 = React.version.startsWith("18"); - describe("useLazyQuery Hook", () => { const helloQuery: TypedDocumentNode<{ hello: string; @@ -42,7 +40,7 @@ describe("useLazyQuery Hook", () => { { request: { query: helloQuery }, result: { data: { hello: "world" } }, - delay: 20, + delay: 50, }, ]; const { result } = renderHook(() => useLazyQuery(helloQuery), { @@ -100,32 +98,34 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook(() => useLazyQuery(helloQuery), { + const ProfiledHook = profileHook(() => useLazyQuery(helloQuery)); + + render(, { wrapper: ({ children }) => ( {children} ), }); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].called).toBe(false); - const execute = result.current[0]; + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.called).toBe(false); + } + + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].called).toBe(true); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.called).toBe(true); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].called).toBe(true); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.called).toBe(true); + } }); it("should override `skip` if lazy mode execution function is called", async () => { @@ -137,36 +137,35 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook( + const ProfiledHook = profileHook( // skip isn’t actually an option on the types - () => useLazyQuery(helloQuery, { skip: true } as any), - { - wrapper: ({ children }) => ( - {children} - ), - } + () => useLazyQuery(helloQuery, { skip: true } as any) ); + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].called).toBe(false); - const execute = result.current[0]; + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.called).toBe(false); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].called).toBe(true); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.called).toBe(true); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].called).toBe(true); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.called).toBe(true); + } }); it("should use variables defined in hook options (if any), when running the lazy execution function", async () => { @@ -184,36 +183,34 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook( - () => - useLazyQuery(query, { - variables: { id: 1 }, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } + const ProfiledHook = profileHook(() => + useLazyQuery(query, { + variables: { id: 1 }, + }) ); + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); - const execute = result.current[0]; - setTimeout(() => execute()); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); + const execute = ProfiledHook.getCurrentSnapshot()[0]; + setTimeout(() => execute()); - expect(result.current[1].data).toEqual({ hello: "world 1" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 1" }); + } }); it("should use variables passed into lazy execution function, overriding similar variables defined in Hook options", async () => { @@ -544,7 +541,8 @@ describe("useLazyQuery Hook", () => { ]; const cache = new InMemoryCache(); - const { result } = renderHook(() => useLazyQuery(query1), { + const ProfiledHook = profileHook(() => useLazyQuery(query1)); + render(, { wrapper: ({ children }) => ( {children} @@ -552,43 +550,37 @@ describe("useLazyQuery Hook", () => { ), }); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].data).toBe(undefined); - const execute = result.current[0]; + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world" }); + } setTimeout(() => execute({ query: query2 })); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ name: "changed" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ name: "changed" }); + } }); it('should fetch data each time the execution function is called, when using a "network-only" fetch policy', async () => { @@ -605,54 +597,48 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook( - () => - useLazyQuery(helloQuery, { - fetchPolicy: "network-only", - }), - { - wrapper: ({ children }) => ( - {children} - ), - } + const ProfiledHook = profileHook(() => + useLazyQuery(helloQuery, { + fetchPolicy: "network-only", + }) ); - expect(result.current[1].loading).toBe(false); - const execute = result.current[0]; + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 1" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 1" }); + } setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 1" }); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 2" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toEqual({ hello: "world 1" }); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 2" }); + } }); it("should persist previous data when a query is re-run", async () => { @@ -669,62 +655,55 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook( - () => - useLazyQuery(helloQuery, { - notifyOnNetworkStatusChange: true, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } + const ProfiledHook = profileHook(() => + useLazyQuery(helloQuery, { + notifyOnNetworkStatusChange: true, + }) ); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].data).toBe(undefined); - expect(result.current[1].previousData).toBe(undefined); - const execute = result.current[0]; + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + expect(result.previousData).toBe(undefined); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute()); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].data).toBe(undefined); - expect(result.current[1].previousData).toBe(undefined); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.previousData).toBe(undefined); + } - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 1" }); - expect(result.current[1].previousData).toBe(undefined); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 1" }); + expect(result.previousData).toBe(undefined); + } - const refetch = result.current[1].refetch; + const refetch = ProfiledHook.getCurrentSnapshot()[1].refetch; setTimeout(() => refetch!()); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 1" }); - expect(result.current[1].previousData).toEqual({ hello: "world 1" }); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual({ hello: "world 2" }); - expect(result.current[1].previousData).toEqual({ hello: "world 1" }); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toEqual({ hello: "world 1" }); + expect(result.previousData).toEqual({ hello: "world 1" }); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 2" }); + expect(result.previousData).toEqual({ hello: "world 1" }); + } }); it("should allow for the query to start with polling", async () => { @@ -828,55 +807,49 @@ describe("useLazyQuery Hook", () => { }, ]; - const { result } = renderHook(() => useLazyQuery(CAR_QUERY_BY_ID), { + const ProfiledHook = profileHook(() => useLazyQuery(CAR_QUERY_BY_ID)); + render(, { wrapper: ({ children }) => ( {children} ), }); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].data).toBe(undefined); - expect(result.current[1].previousData).toBe(undefined); - const execute = result.current[0]; + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + expect(result.previousData).toBe(undefined); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(() => execute({ variables: { id: 1 } })); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].data).toBe(undefined); - expect(result.current[1].previousData).toBe(undefined); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual(data1); - expect(result.current[1].previousData).toBe(undefined); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.previousData).toBe(undefined); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual(data1); + expect(result.previousData).toBe(undefined); + } setTimeout(() => execute({ variables: { id: 2 } })); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current[1].data).toBe(undefined); - expect(result.current[1].previousData).toEqual(data1); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current[1].data).toEqual(data2); - expect(result.current[1].previousData).toEqual(data1); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.previousData).toEqual(data1); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual(data2); + expect(result.previousData).toEqual(data1); + } }); it("should work with cache-and-network fetch policy", async () => { @@ -1769,72 +1742,39 @@ describe("useLazyQuery Hook", () => { ), }); - const { result } = renderHook( - () => - useLazyQuery(helloQuery, { - errorPolicy, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } + const ProfiledHook = profileHook(() => + useLazyQuery(helloQuery, { + errorPolicy, + }) ); + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); - const execute = result.current[0]; - expect(result.current[1].loading).toBe(false); - expect(result.current[1].networkStatus).toBe(NetworkStatus.ready); - expect(result.current[1].data).toBeUndefined(); - + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.networkStatus).toBe(NetworkStatus.ready); + expect(result.data).toBeUndefined(); + } + const execute = ProfiledHook.getCurrentSnapshot()[0]; setTimeout(execute); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - await waitFor( - () => { - if (IS_REACT_18) { - expect(result.current[1].networkStatus).toBe(NetworkStatus.loading); - } else { - expect(result.current[1].networkStatus).toBe(NetworkStatus.error); - } - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].data).toBeUndefined(); - }, - { interval: 1 } - ); - - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].networkStatus).toBe(NetworkStatus.error); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].data).toBeUndefined(); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].error!.message).toBe("from the network"); - }, - { interval: 1 } - ); + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.networkStatus).toBe(NetworkStatus.loading); + expect(result.data).toBeUndefined(); + } + { + const [, result] = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.networkStatus).toBe(NetworkStatus.error); + expect(result.data).toBeUndefined(); + expect(result.error!.message).toBe("from the network"); + } } // For errorPolicy:"none", we expect result.error to be defined and diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index f900f61bbda..bfcd534c7e3 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -23,18 +23,21 @@ import { mockSingleLink, tick, wait, + MockedResponse, } from "../../../testing"; import { QueryResult } from "../../types/types"; import { useQuery } from "../useQuery"; import { useMutation } from "../useMutation"; import { createProfiler, + disableActWarnings, profileHook, spyOnConsole, } from "../../../testing/internal"; import { useApolloClient } from "../useApolloClient"; import { useLazyQuery } from "../useLazyQuery"; +const IS_REACT_17 = React.version.startsWith("17"); const IS_REACT_19 = React.version.startsWith("19"); describe("useQuery Hook", () => { @@ -769,6 +772,7 @@ describe("useQuery Hook", () => { { request: { query: query1 }, result: { data: allPeopleData }, + delay: 3, }, { request: { query: query2 }, @@ -781,76 +785,49 @@ describe("useQuery Hook", () => { link, cache: new InMemoryCache(), }); + const ProfiledHook = profileHook(() => [ + useQuery(query1, { fetchPolicy: "no-cache" }), + useQuery(query2), + ]); + const { rerender } = render(, { + wrapper: ({ children }) => ( + {children} + ), + }); - const { result, rerender } = renderHook( - () => [useQuery(query1, { fetchPolicy: "no-cache" }), useQuery(query2)], - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - - expect(result.current[0].loading).toBe(true); - expect(result.current[0].data).toBe(undefined); - expect(result.current[1].loading).toBe(true); - expect(result.current[1].data).toBe(undefined); + { + const [result0, result1] = await ProfiledHook.takeSnapshot(); + expect(result0.loading).toBe(true); + expect(result0.data).toStrictEqual(undefined); + expect(result1.loading).toBe(true); + expect(result1.data).toStrictEqual(undefined); + } - await waitFor( - () => { - expect(result.current[0].loading).toBe(false); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[0].data).toEqual(allPeopleData); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].loading).toBe(true); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].data).toBe(undefined); - }, - { interval: 1 } - ); + { + const [result0, result1] = await ProfiledHook.takeSnapshot(); + expect(result0.loading).toBe(false); + expect(result0.data).toStrictEqual(allPeopleData); + expect(result1.loading).toBe(true); + expect(result1.data).toStrictEqual(undefined); + } - await waitFor( - () => { - expect(result.current[0].loading).toBe(false); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[0].data).toEqual(allPeopleData); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].loading).toBe(false); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current[1].data).toEqual(allThingsData); - }, - { interval: 1 } - ); + { + const [result0, result1] = await ProfiledHook.takeSnapshot(); + expect(result0.loading).toBe(false); + expect(result0.data).toStrictEqual(allPeopleData); + expect(result1.loading).toBe(false); + expect(result1.data).toStrictEqual(allThingsData); + } - rerender(); - expect(result.current[0].loading).toBe(false); - expect(result.current[0].data).toEqual(allPeopleData); - expect(result.current[1].loading).toBe(false); - expect(result.current[1].data).toEqual(allThingsData); + rerender(); + { + const [result0, result1] = await ProfiledHook.takeSnapshot(); + expect(result0.loading).toBe(false); + expect(result0.data).toStrictEqual(allPeopleData); + expect(result1.loading).toBe(false); + expect(result1.data).toStrictEqual(allThingsData); + } + await expect(ProfiledHook).not.toRerender(); }); it("changing queries", async () => { @@ -1840,18 +1817,21 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the hello } `; - const mocks = [ + const mocks: MockedResponse[] = [ { request: { query }, result: { data: { hello: "world 1" } }, + delay: 10, }, { request: { query }, result: { data: { hello: "world 2" } }, + delay: 10, }, { request: { query }, result: { data: { hello: "world 3" } }, + delay: 10, }, ]; @@ -1867,30 +1847,24 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); - const { result, unmount } = renderHook( - () => useQuery(query, { pollInterval: 10 }), - { wrapper } + const ProfiledHook = profileHook(() => + useQuery(query, { pollInterval: 20 }) ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); + const { unmount } = render(, { wrapper }); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - await waitFor( - () => { - expect(result.current.data).toEqual({ hello: "world 1" }); - }, - { interval: 1 } - ); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + } - await waitFor(() => { + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 1" }); expect(requestSpy).toHaveBeenCalled(); - }); + } const requestCount = requestSpy.mock.calls.length; expect(requestCount).toBeGreaterThan(0); @@ -1905,7 +1879,7 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the const newRequestCount = requestSpy.mock.calls.length; expect(newRequestCount).toBeGreaterThan(requestCount); }, - { interval: 1, timeout: 20 } + { interval: 1, timeout: 40 } ) ).rejects.toThrow(); @@ -1925,18 +1899,21 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the } `; - const mocks = [ + const mocks: MockedResponse[] = [ { request: { query }, result: { data: { hello: "world 1" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 2" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 3" } }, + delay: 3, }, ]; @@ -1992,6 +1969,7 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the await expect(ProfiledHook).not.toRerender({ timeout: 50 }); + // TODO rarely seeing 3 here (also old `useQuery` implementation) expect(requestSpy).toHaveBeenCalledTimes(2); expect(onErrorFn).toHaveBeenCalledTimes(0); }); @@ -2078,18 +2056,21 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the } `; - const mocks = [ + const mocks: MockedResponse[] = [ { request: { query }, result: { data: { hello: "world 1" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 2" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 3" } }, + delay: 3, }, ]; @@ -2142,7 +2123,7 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the unmount(); await expect(ProfiledHook).not.toRerender({ timeout: 50 }); - + // TODO rarely seeing 3 here investigate further expect(requestSpy).toHaveBeenCalledTimes(2); expect(onErrorFn).toHaveBeenCalledTimes(0); }); @@ -3581,47 +3562,42 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); - const { result } = renderHook( - () => useQuery(query, { notifyOnNetworkStatusChange: true }), - { wrapper } + const ProfiledHook = profileHook(() => + useQuery(query, { notifyOnNetworkStatusChange: true }) ); + render(, { wrapper }); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBe(undefined); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.error).toBe(undefined); + } - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("error 1"); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + expect(result.error).toBeInstanceOf(ApolloError); + expect(result.error!.message).toBe("error 1"); + } const catchFn = jest.fn(); + ProfiledHook.getCurrentSnapshot().refetch().catch(catchFn); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.error).toBe(undefined); + } - result.current.refetch().catch(catchFn); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("error 2"); - + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + expect(result.error).toBeInstanceOf(ApolloError); + expect(result.error!.message).toBe("error 2"); + } expect(catchFn.mock.calls.length).toBe(1); expect(catchFn.mock.calls[0].length).toBe(1); expect(catchFn.mock.calls[0][0]).toBeInstanceOf(ApolloError); @@ -3729,68 +3705,55 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); - const { result } = renderHook( - () => useQuery(query, { notifyOnNetworkStatusChange: true }), - { wrapper } - ); - - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("same error"); - - result.current.refetch(); - - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } + const ProfiledHook = profileHook(() => + useQuery(query, { notifyOnNetworkStatusChange: true }) ); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBe(undefined); + render(, { wrapper }); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.data).toEqual({ hello: "world" }); - expect(result.current.error).toBe(undefined); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.error).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toBe(undefined); + expect(result.error).toBeInstanceOf(ApolloError); + expect(result.error!.message).toBe("same error"); + } + ProfiledHook.getCurrentSnapshot().refetch(); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.error).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world" }); + expect(result.error).toBe(undefined); + } const catchFn = jest.fn(); - result.current.refetch().catch(catchFn); - - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.data).toEqual({ hello: "world" }); - expect(result.current.error).toBe(undefined); + ProfiledHook.getCurrentSnapshot().refetch().catch(catchFn); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - // TODO: Is this correct behavior here? - expect(result.current.data).toEqual({ hello: "world" }); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("same error"); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toEqual({ hello: "world" }); + expect(result.error).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + // TODO: Is this correct behavior here? + expect(result.data).toEqual({ hello: "world" }); + expect(result.error).toBeInstanceOf(ApolloError); + expect(result.error!.message).toBe("same error"); + } expect(catchFn.mock.calls.length).toBe(1); expect(catchFn.mock.calls[0].length).toBe(1); @@ -5222,42 +5185,35 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); - const { result } = renderHook( - () => - useQuery(query, { - variables: { id: 1 }, - notifyOnNetworkStatusChange: true, - }), - { wrapper } + const ProfiledHook = profileHook(() => + useQuery(query, { + variables: { id: 1 }, + notifyOnNetworkStatusChange: true, + }) ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.data).toEqual({ hello: "world 1" }); - - result.current.refetch({ id: 2 }); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.data).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.data).toEqual({ hello: "world 2" }); + render(, { wrapper }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 1" }); + } + ProfiledHook.getCurrentSnapshot().refetch({ id: 2 }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world 2" }); + } }); it("refetching after an error", async () => { @@ -5285,69 +5241,60 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the const cache = new InMemoryCache(); - const { result } = renderHook( - () => - useQuery(query, { - notifyOnNetworkStatusChange: true, - }), - { - wrapper: ({ children }) => ( - - {children} - - ), - } + const ProfiledHook = profileHook(() => + useQuery(query, { + notifyOnNetworkStatusChange: true, + }) ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ hello: "world 1" }); + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); - result.current.refetch(); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ hello: "world 1" }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ hello: "world 1" }); + } - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.data).toEqual({ hello: "world 1" }); + ProfiledHook.getCurrentSnapshot().refetch(); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ hello: "world 1" }); + } - result.current.refetch(); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ hello: "world 1" }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.error).toBeInstanceOf(ApolloError); + expect(result.data).toEqual({ hello: "world 1" }); + } - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ hello: "world 2" }); + ProfiledHook.getCurrentSnapshot().refetch(); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ hello: "world 1" }); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ hello: "world 2" }); + } }); describe("refetchWritePolicy", () => { @@ -5603,73 +5550,67 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the {children} ); - - const { result } = renderHook( - () => - useQuery(query, { - variables: { min: 0, max: 12 }, - notifyOnNetworkStatusChange: true, - // Intentionally not passing refetchWritePolicy. - }), - { wrapper } + const ProfiledHook = profileHook(() => + useQuery(query, { + variables: { min: 0, max: 12 }, + notifyOnNetworkStatusChange: true, + // Intentionally not passing refetchWritePolicy. + }) ); - expect(result.current.loading).toBe(true); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toBe(undefined); - expect(typeof result.current.refetch).toBe("function"); + render(, { wrapper }); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.error).toBeUndefined(); - expect(result.current.data).toEqual({ primes: [2, 3, 5, 7, 11] }); - expect(mergeParams).toEqual([[void 0, [2, 3, 5, 7, 11]]]); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.error).toBe(undefined); + expect(result.data).toBe(undefined); + expect(typeof result.refetch).toBe("function"); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.error).toBeUndefined(); + expect(result.data).toEqual({ primes: [2, 3, 5, 7, 11] }); + expect(mergeParams.shift()).toEqual([void 0, [2, 3, 5, 7, 11]]); + } const thenFn = jest.fn(); - result.current.refetch({ min: 12, max: 30 }).then(thenFn); + ProfiledHook.getCurrentSnapshot() + .refetch({ min: 12, max: 30 }) + .then(thenFn); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ - // We get the stale data because we configured keyArgs: false. - primes: [2, 3, 5, 7, 11], - }); - - // This networkStatus is setVariables instead of refetch because we - // called refetch with new variables. - expect(result.current.networkStatus).toBe(NetworkStatus.setVariables); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ + // We get the stale data because we configured keyArgs: false. + primes: [2, 3, 5, 7, 11], + }); + // This networkStatus is setVariables instead of refetch because we + // called refetch with new variables. + expect(result.networkStatus).toBe(NetworkStatus.setVariables); + } - expect(result.current.error).toBe(undefined); - expect(result.current.data).toEqual({ primes: [13, 17, 19, 23, 29] }); - expect(mergeParams).toEqual([ - [undefined, [2, 3, 5, 7, 11]], - // Without refetchWritePolicy: "overwrite", this array will be - // all 10 primes (2 through 29) together. - [undefined, [13, 17, 19, 23, 29]], - ]); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.error).toBe(undefined); + expect(result.data).toEqual({ primes: [13, 17, 19, 23, 29] }); + expect(mergeParams.shift()).toEqual( + // Without refetchWritePolicy: "overwrite", this array will be + // all 10 primes (2 through 29) together. + [undefined, [13, 17, 19, 23, 29]] + ); - expect(thenFn).toHaveBeenCalledTimes(1); - expect(thenFn).toHaveBeenCalledWith({ - loading: false, - networkStatus: NetworkStatus.ready, - data: { primes: [13, 17, 19, 23, 29] }, - }); + expect(thenFn).toHaveBeenCalledTimes(1); + expect(thenFn).toHaveBeenCalledWith({ + loading: false, + networkStatus: NetworkStatus.ready, + data: { primes: [13, 17, 19, 23, 29] }, + }); + } }); }); @@ -6095,63 +6036,72 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the { request: { query }, result: { data: { hello: "world 1" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 2" } }, + delay: 3, }, { request: { query }, result: { data: { hello: "world 3" } }, + delay: 3, }, ]; const cache = new InMemoryCache(); const onCompleted = jest.fn(); - const { result } = renderHook( - () => - useQuery(query, { - onCompleted, - notifyOnNetworkStatusChange: true, - pollInterval: 10, - }), - { - wrapper: ({ children }) => ( - - {children} - - ), - } - ); - - expect(result.current.loading).toBe(true); - - await waitFor( - () => { - expect(result.current.data).toEqual({ hello: "world 1" }); - }, - { interval: 1 } - ); - expect(result.current.loading).toBe(false); - expect(onCompleted).toHaveBeenCalledTimes(1); - - await waitFor( - () => { - expect(result.current.data).toEqual({ hello: "world 2" }); - }, - { interval: 1 } + const ProfiledHook = profileHook(() => + useQuery(query, { + onCompleted, + notifyOnNetworkStatusChange: true, + pollInterval: 110, + }) ); - expect(result.current.loading).toBe(false); - expect(onCompleted).toHaveBeenCalledTimes(2); + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); - await waitFor( - () => { - expect(result.current.data).toEqual({ hello: "world 3" }); - }, - { interval: 1 } - ); - expect(result.current.loading).toBe(false); - expect(onCompleted).toHaveBeenCalledTimes(3); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual(undefined); + expect(result.loading).toBe(true); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual({ hello: "world 1" }); + expect(result.loading).toBe(false); + expect(onCompleted).toHaveBeenCalledTimes(1); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual({ hello: "world 1" }); + expect(result.loading).toBe(true); + expect(onCompleted).toHaveBeenCalledTimes(1); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual({ hello: "world 2" }); + expect(result.loading).toBe(false); + expect(onCompleted).toHaveBeenCalledTimes(2); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual({ hello: "world 2" }); + expect(result.loading).toBe(true); + expect(onCompleted).toHaveBeenCalledTimes(2); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.data).toEqual({ hello: "world 3" }); + expect(result.loading).toBe(false); + expect(onCompleted).toHaveBeenCalledTimes(3); + } }); // This test was added for issue https://github.com/apollographql/apollo-client/issues/9794 @@ -6396,83 +6346,92 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); const onError = jest.fn(); - const { result } = renderHook( - () => ({ - mutation: useMutation(mutation, { - optimisticResponse: { addCar: carData }, - update(cache, { data }) { - cache.modify({ - fields: { - cars(existing, { readField }) { - const newCarRef = cache.writeFragment({ - data: data!.addCar, - fragment: gql` - fragment NewCar on Car { - id - make - model - } - `, - }); - - if ( - existing.some( - (ref: Reference) => - readField("id", ref) === data!.addCar.id - ) - ) { - return existing; - } - - return [...existing, newCarRef]; - }, + const ProfiledHook = profileHook(() => ({ + mutation: useMutation(mutation, { + optimisticResponse: { addCar: carData }, + update(cache, { data }) { + cache.modify({ + fields: { + cars(existing, { readField }) { + const newCarRef = cache.writeFragment({ + data: data!.addCar, + fragment: gql` + fragment NewCar on Car { + id + make + model + } + `, + }); + + if ( + existing.some( + (ref: Reference) => + readField("id", ref) === data!.addCar.id + ) + ) { + return existing; + } + + return [...existing, newCarRef]; }, - }); - }, - onError, - }), - query: useQuery(query), + }, + }); + }, + onError, }), - { wrapper } - ); - - expect(result.current.query.loading).toBe(true); - const mutate = result.current.mutation[0]; + query: useQuery(query), + })); + render(, { wrapper }); - await waitFor( - () => { - expect(result.current.query.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.query.loading).toBe(false); - expect(result.current.query.data).toEqual(carsData); + { + const { query } = await ProfiledHook.takeSnapshot(); + expect(query.loading).toBe(true); + } + const mutate = ProfiledHook.getCurrentSnapshot().mutation[0]; + { + const { query } = await ProfiledHook.takeSnapshot(); + expect(query.loading).toBe(false); + expect(query.loading).toBe(false); + expect(query.data).toEqual(carsData); + } act(() => void mutate()); - // The mutation ran and is loading the result. The query stays at not - // loading as nothing has changed for the query, but optimistic data is - // rendered. - expect(result.current.mutation[1].loading).toBe(true); - expect(result.current.query.loading).toBe(false); - expect(result.current.query.data).toEqual(allCarsData); + { + // The mutation ran and is loading the result. The query stays at not + // loading as nothing has changed for the query, but optimistic data is + // rendered. + let { query, mutation } = await ProfiledHook.takeSnapshot(); + + while (!mutation[1].loading) { + // useMutation seems to sometimes have an extra render + // before it enters `loading` state - this test doesn't test + // that part of that hook so we just work around it + ({ query, mutation } = await ProfiledHook.takeSnapshot()); + } + expect(mutation[1].loading).toBe(true); + expect(query.loading).toBe(false); + expect(query.data).toEqual(allCarsData); + } expect(onError).toHaveBeenCalledTimes(0); - await tick(); - // The mutation ran and is loading the result. The query stays at - // not loading as nothing has changed for the query. - expect(result.current.mutation[1].loading).toBe(true); - expect(result.current.query.loading).toBe(false); - - await waitFor(() => { - expect(result.current.mutation[1].loading).toBe(false); - }); + { + const { query, mutation } = await ProfiledHook.takeSnapshot(); + // The mutation ran and is loading the result. The query stays at + // not loading as nothing has changed for the query. + expect(mutation[1].loading).toBe(true); + expect(query.loading).toBe(false); + } - // The mutation has completely finished, leaving the query with access to - // the original cache data. - expect(result.current.mutation[1].loading).toBe(false); - expect(result.current.query.loading).toBe(false); - expect(result.current.query.data).toEqual(carsData); + { + const { query, mutation } = await ProfiledHook.takeSnapshot(); + // The mutation has completely finished, leaving the query with access to + // the original cache data. + expect(mutation[1].loading).toBe(false); + expect(query.loading).toBe(false); + expect(query.data).toEqual(carsData); + } expect(onError).toHaveBeenCalledTimes(1); expect(onError.mock.calls[0][0].message).toBe("Oh no!"); @@ -6618,6 +6577,7 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the }); it("should attempt a refetch when data is missing, partialRefetch is true and addTypename is false for the cache", async () => { + using _disabledActWarnings = disableActWarnings(); using consoleSpy = spyOnConsole("error"); const query = gql` { @@ -6648,43 +6608,45 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the {children} ); - const { result } = renderHook( - () => - useQuery(query, { - partialRefetch: true, - notifyOnNetworkStatusChange: true, - }), - { wrapper } + const ProfiledHook = profileHook(() => + useQuery(query, { + partialRefetch: true, + notifyOnNetworkStatusChange: true, + }) ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toBe(undefined); - expect(result.current.networkStatus).toBe(NetworkStatus.loading); + render(, { wrapper }); - await waitFor( - () => { - expect(result.current.networkStatus).toBe(NetworkStatus.refetch); - }, - { interval: 1 } - ); - expect(result.current.loading).toBe(true); - expect(result.current.error).toBe(undefined); - expect(result.current.data).toBe(undefined); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.data).toBe(undefined); + expect(result.error).toBe(undefined); + expect(result.networkStatus).toBe(NetworkStatus.loading); + } - expect(consoleSpy.error).toHaveBeenCalledTimes(1); - expect(consoleSpy.error.mock.calls[0][0]).toMatch("Missing field"); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.networkStatus).toBe(NetworkStatus.refetch); + expect(result.loading).toBe(true); + expect(result.error).toBe(undefined); + expect(result.data).toBe(undefined); + } - await waitFor( - () => { - expect(result.current.networkStatus).toBe(NetworkStatus.ready); - }, - { interval: 1 } - ); + const calls = consoleSpy.error.mock.calls; + if (!IS_REACT_17) { + // React 17 doesn't know `IS_REACT_ACT_ENVIRONMENT` yet, so it will log a warning that we don't care about. + expect(calls.length).toBe(1); + } + expect(calls[0][0]).toMatch("Missing field"); - expect(result.current.loading).toBe(false); - expect(result.current.data).toEqual({ hello: "world" }); - expect(result.current.error).toBe(undefined); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.networkStatus).toBe(NetworkStatus.ready); + expect(result.loading).toBe(false); + expect(result.data).toEqual({ hello: "world" }); + expect(result.error).toBe(undefined); + } }); }); @@ -7948,66 +7910,67 @@ This is pure coincidence though, and the useQuery rewrite that doesn't break the ); - const { result, rerender } = renderHook( - ({ gender }) => - useQuery(query, { - variables: { gender }, - fetchPolicy: "network-only", - }), - { wrapper, initialProps: { gender: "all" } } + const ProfiledHook = profileHook(({ gender }: { gender: string }) => + useQuery(query, { + variables: { gender }, + fetchPolicy: "network-only", + }) ); + const { rerender } = render(, { wrapper }); - expect(result.current.loading).toBe(true); - expect(result.current.networkStatus).toBe(NetworkStatus.loading); - expect(result.current.data).toBe(undefined); - - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.networkStatus).toBe(NetworkStatus.loading); + expect(result.data).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.networkStatus).toBe(NetworkStatus.ready); + expect(result.data).toEqual({ + people: peopleData.map(({ gender, ...person }) => person), + }); + } - expect(result.current.networkStatus).toBe(NetworkStatus.ready); - expect(result.current.data).toEqual({ - people: peopleData.map(({ gender, ...person }) => person), - }); + rerender(); - rerender({ gender: "female" }); - expect(result.current.loading).toBe(true); - expect(result.current.networkStatus).toBe(NetworkStatus.setVariables); - expect(result.current.data).toBe(undefined); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.networkStatus).toBe(NetworkStatus.setVariables); + expect(result.data).toBe(undefined); + } - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.networkStatus).toBe(NetworkStatus.ready); - expect(result.current.data).toEqual({ - people: peopleData - .filter((person) => person.gender === "female") - .map(({ gender, ...person }) => person), - }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.networkStatus).toBe(NetworkStatus.ready); + expect(result.data).toEqual({ + people: peopleData + .filter((person) => person.gender === "female") + .map(({ gender, ...person }) => person), + }); + } - rerender({ gender: "nonbinary" }); - expect(result.current.loading).toBe(true); - expect(result.current.networkStatus).toBe(NetworkStatus.setVariables); - expect(result.current.data).toBe(undefined); + rerender(); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.networkStatus).toBe(NetworkStatus.ready); - expect(result.current.data).toEqual({ - people: peopleData - .filter((person) => person.gender === "nonbinary") - .map(({ gender, ...person }) => person), - }); + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(true); + expect(result.networkStatus).toBe(NetworkStatus.setVariables); + expect(result.data).toBe(undefined); + } + { + const result = await ProfiledHook.takeSnapshot(); + expect(result.loading).toBe(false); + expect(result.networkStatus).toBe(NetworkStatus.ready); + expect(result.data).toEqual({ + people: peopleData + .filter((person) => person.gender === "nonbinary") + .map(({ gender, ...person }) => person), + }); + } }); });