Skip to content

Commit db5916b

Browse files
committed
Test prefetch warnings
1 parent 23e328e commit db5916b

File tree

3 files changed

+132
-5
lines changed

3 files changed

+132
-5
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { renderHook } from "@testing-library/react"
2+
import { useQuery } from "@tanstack/react-query"
3+
import { usePrefetchWarnings } from "./usePrefetchWarnings"
4+
import { setupReactQueryTest } from "../hooks/test-utils"
5+
import { urls, factories, setMockResponse } from "../test-utils"
6+
import {
7+
learningResourcesKeyFactory,
8+
useLearningResourcesDetail,
9+
} from "../hooks/learningResources"
10+
11+
jest.mock("./usePrefetchWarnings", () => {
12+
const originalModule = jest.requireActual("./usePrefetchWarnings")
13+
return {
14+
...originalModule,
15+
logQueries: jest.fn(),
16+
}
17+
})
18+
19+
describe("SSR prefetch warnings", () => {
20+
beforeEach(() => {
21+
jest.spyOn(console, "info").mockImplementation(() => {})
22+
jest.spyOn(console, "table").mockImplementation(() => {})
23+
})
24+
25+
afterEach(() => {
26+
jest.clearAllMocks()
27+
})
28+
29+
it("Warns if a query is requested on the client that has not been prefetched", async () => {
30+
const { wrapper, queryClient } = setupReactQueryTest()
31+
32+
const data = factories.learningResources.resource()
33+
setMockResponse.get(urls.learningResources.details({ id: 1 }), data)
34+
35+
renderHook(() => useLearningResourcesDetail(1), { wrapper })
36+
37+
renderHook(usePrefetchWarnings, {
38+
wrapper,
39+
initialProps: { queryClient },
40+
})
41+
42+
expect(console.info).toHaveBeenCalledWith(
43+
"The following queries were requested in first render but not prefetched.",
44+
"If these queries are user-specific, they cannot be prefetched - responses are cached on public CDN.",
45+
"Otherwise, consider fetching on the server with prefetch:",
46+
)
47+
expect(console.table).toHaveBeenCalledWith(
48+
[
49+
{
50+
disabled: false,
51+
hash: '["learningResources","detail",1]',
52+
initialStatus: "loading",
53+
key: ["learningResources", "detail", 1],
54+
observerCount: 1,
55+
status: "loading",
56+
},
57+
],
58+
["hash", "initialStatus", "status", "observerCount", "disabled"],
59+
)
60+
})
61+
62+
it("Ignores exempted queries requested on the client that have not been prefetched", async () => {
63+
const { wrapper, queryClient } = setupReactQueryTest()
64+
65+
const data = factories.learningResources.resource()
66+
setMockResponse.get(urls.learningResources.details({ id: 1 }), data)
67+
68+
renderHook(() => useLearningResourcesDetail(1), { wrapper })
69+
70+
renderHook(usePrefetchWarnings, {
71+
wrapper,
72+
initialProps: {
73+
queryClient,
74+
exemptions: [["learningResources", "detail", 1]],
75+
},
76+
})
77+
78+
expect(console.info).not.toHaveBeenCalled()
79+
expect(console.table).not.toHaveBeenCalled()
80+
})
81+
82+
it("Warns for queries prefetched on the server but not requested on the client", async () => {
83+
const { wrapper, queryClient } = setupReactQueryTest()
84+
85+
const data = factories.learningResources.resource()
86+
setMockResponse.get(urls.learningResources.details({ id: 1 }), data)
87+
88+
// Emulate server prefetch
89+
const { unmount } = renderHook(
90+
() =>
91+
useQuery({
92+
...learningResourcesKeyFactory.detail(1),
93+
initialData: data,
94+
}),
95+
{ wrapper },
96+
)
97+
98+
// Removes observer
99+
unmount()
100+
101+
renderHook(usePrefetchWarnings, {
102+
wrapper,
103+
initialProps: { queryClient },
104+
})
105+
106+
expect(console.info).toHaveBeenCalledWith(
107+
"The following queries were prefetched on the server but not accessed during initial render.",
108+
"If these queries are no longer in use they should removed from prefetch:",
109+
)
110+
expect(console.table).toHaveBeenCalledWith(
111+
[
112+
{
113+
disabled: false,
114+
hash: '["learningResources","detail",1]',
115+
initialStatus: "success",
116+
key: ["learningResources", "detail", 1],
117+
observerCount: 0,
118+
status: "success",
119+
},
120+
],
121+
["hash", "initialStatus", "status", "observerCount", "disabled"],
122+
)
123+
})
124+
})

frontends/api/src/ssr/usePrefetchWarnings.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ const PREFETCH_EXEMPT_QUERIES = [["userMe"]]
2323
* Call this as high as possible in render tree to detect query usage on
2424
* first render.
2525
*/
26-
export const usePrefetchWarnings = (
27-
queryClient: QueryClient,
26+
export const usePrefetchWarnings = ({
27+
queryClient,
28+
exemptions = [],
29+
}: {
30+
queryClient: QueryClient
2831
/**
2932
* A list of query keys that should be exempted.
3033
*
@@ -34,8 +37,8 @@ export const usePrefetchWarnings = (
3437
* - ["a", { x: 1, y: 2 }]
3538
* - ["a", { x: 1, y: 2 }, ...any_other_entries]
3639
*/
37-
exemptions: QueryKey[] = [],
38-
) => {
40+
exemptions?: QueryKey[]
41+
}) => {
3942
/**
4043
* NOTE: React renders components top-down, but effects run bottom-up, so
4144
* this effect will run after all child effects.

frontends/main/src/app/providers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { usePrefetchWarnings } from "api/ssr/usePrefetchWarnings"
1111
export default function Providers({ children }: { children: React.ReactNode }) {
1212
const queryClient = getQueryClient()
1313

14-
usePrefetchWarnings(queryClient)
14+
usePrefetchWarnings({ queryClient })
1515

1616
return (
1717
<QueryClientProvider client={queryClient}>

0 commit comments

Comments
 (0)