Skip to content

Commit 2acf617

Browse files
committed
test(react-query): use fake timer for HydrationBoundary.test.tsx
1 parent 0511c82 commit 2acf617

File tree

1 file changed

+82
-74
lines changed

1 file changed

+82
-74
lines changed

packages/react-query/src/__tests__/HydrationBoundary.test.tsx

+82-74
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { beforeAll, describe, expect, test, vi } from 'vitest'
1+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
22
import * as React from 'react'
33
import { render } from '@testing-library/react'
44

55
import * as coreModule from '@tanstack/query-core'
66
import {
77
HydrationBoundary,
8-
QueryCache,
98
QueryClient,
109
QueryClientProvider,
1110
dehydrate,
@@ -14,33 +13,32 @@ import {
1413
import { createQueryClient, sleep } from './utils'
1514

1615
describe('React hydration', () => {
17-
const fetchData: (value: string) => Promise<string> = (value) =>
18-
new Promise((res) => setTimeout(() => res(value), 10))
19-
const dataQuery: (key: [string]) => Promise<string> = (key) =>
20-
fetchData(key[0])
2116
let stringifiedState: string
2217

23-
beforeAll(async () => {
24-
const queryCache = new QueryCache()
25-
const queryClient = createQueryClient({ queryCache })
26-
await queryClient.prefetchQuery({
18+
beforeEach(async () => {
19+
vi.useFakeTimers()
20+
const queryClient = createQueryClient()
21+
queryClient.prefetchQuery({
2722
queryKey: ['string'],
28-
queryFn: () => dataQuery(['stringCached']),
23+
queryFn: () => sleep(10).then(() => ['stringCached']),
2924
})
25+
await vi.advanceTimersByTimeAsync(10)
3026
const dehydrated = dehydrate(queryClient)
3127
stringifiedState = JSON.stringify(dehydrated)
3228
queryClient.clear()
3329
})
30+
afterEach(() => {
31+
vi.useRealTimers()
32+
})
3433

3534
test('should hydrate queries to the cache on context', async () => {
3635
const dehydratedState = JSON.parse(stringifiedState)
37-
const queryCache = new QueryCache()
38-
const queryClient = createQueryClient({ queryCache })
36+
const queryClient = createQueryClient()
3937

4038
function Page() {
4139
const { data } = useQuery({
4240
queryKey: ['string'],
43-
queryFn: () => dataQuery(['string']),
41+
queryFn: () => sleep(20).then(() => ['string']),
4442
})
4543
return (
4644
<div>
@@ -56,25 +54,23 @@ describe('React hydration', () => {
5654
</HydrationBoundary>
5755
</QueryClientProvider>,
5856
)
59-
60-
await rendered.findByText('stringCached')
61-
await rendered.findByText('string')
57+
await vi.advanceTimersByTimeAsync(1)
58+
expect(rendered.getByText('stringCached')).toBeInTheDocument()
59+
await vi.advanceTimersByTimeAsync(20)
60+
expect(rendered.getByText('string')).toBeInTheDocument()
6261
queryClient.clear()
6362
})
6463

6564
test('should hydrate queries to the cache on custom context', async () => {
66-
const queryCacheOuter = new QueryCache()
67-
const queryCacheInner = new QueryCache()
68-
69-
const queryClientInner = new QueryClient({ queryCache: queryCacheInner })
70-
const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter })
65+
const queryClientInner = new QueryClient()
66+
const queryClientOuter = new QueryClient()
7167

7268
const dehydratedState = JSON.parse(stringifiedState)
7369

7470
function Page() {
7571
const { data } = useQuery({
7672
queryKey: ['string'],
77-
queryFn: () => dataQuery(['string']),
73+
queryFn: () => sleep(20).then(() => ['string']),
7874
})
7975
return (
8076
<div>
@@ -93,8 +89,10 @@ describe('React hydration', () => {
9389
</QueryClientProvider>,
9490
)
9591

96-
await rendered.findByText('stringCached')
97-
await rendered.findByText('string')
92+
await vi.advanceTimersByTimeAsync(1)
93+
expect(rendered.getByText('stringCached')).toBeInTheDocument()
94+
await vi.advanceTimersByTimeAsync(20)
95+
expect(rendered.getByText('string')).toBeInTheDocument()
9896

9997
queryClientInner.clear()
10098
queryClientOuter.clear()
@@ -103,13 +101,12 @@ describe('React hydration', () => {
103101
describe('ReactQueryCacheProvider with hydration support', () => {
104102
test('should hydrate new queries if queries change', async () => {
105103
const dehydratedState = JSON.parse(stringifiedState)
106-
const queryCache = new QueryCache()
107-
const queryClient = createQueryClient({ queryCache })
104+
const queryClient = createQueryClient()
108105

109106
function Page({ queryKey }: { queryKey: [string] }) {
110107
const { data } = useQuery({
111108
queryKey,
112-
queryFn: () => dataQuery(queryKey),
109+
queryFn: () => sleep(20).then(() => queryKey),
113110
})
114111
return (
115112
<div>
@@ -126,20 +123,23 @@ describe('React hydration', () => {
126123
</QueryClientProvider>,
127124
)
128125

129-
await rendered.findByText('string')
126+
await vi.advanceTimersByTimeAsync(1)
127+
expect(rendered.getByText('stringCached')).toBeInTheDocument()
128+
await vi.advanceTimersByTimeAsync(20)
129+
expect(rendered.getByText('string')).toBeInTheDocument()
130130

131-
const intermediateCache = new QueryCache()
132-
const intermediateClient = createQueryClient({
133-
queryCache: intermediateCache,
134-
})
135-
await intermediateClient.prefetchQuery({
131+
const intermediateClient = createQueryClient()
132+
133+
intermediateClient.prefetchQuery({
136134
queryKey: ['string'],
137-
queryFn: () => dataQuery(['should change']),
135+
queryFn: () => sleep(20).then(() => ['should change']),
138136
})
139-
await intermediateClient.prefetchQuery({
137+
await vi.advanceTimersByTimeAsync(20)
138+
intermediateClient.prefetchQuery({
140139
queryKey: ['added'],
141-
queryFn: () => dataQuery(['added']),
140+
queryFn: () => sleep(20).then(() => ['added']),
142141
})
142+
await vi.advanceTimersByTimeAsync(20)
143143
const dehydrated = dehydrate(intermediateClient)
144144
intermediateClient.clear()
145145

@@ -154,14 +154,14 @@ describe('React hydration', () => {
154154

155155
// Existing observer should not have updated at this point,
156156
// as that would indicate a side effect in the render phase
157-
rendered.getByText('string')
157+
expect(rendered.getByText('string')).toBeInTheDocument()
158158
// New query data should be available immediately
159-
rendered.getByText('added')
159+
expect(rendered.getByText('added')).toBeInTheDocument()
160160

161-
await sleep(10)
161+
await vi.advanceTimersByTimeAsync(20)
162162
// After effects phase has had time to run, the observer should have updated
163163
expect(rendered.queryByText('string')).toBeNull()
164-
rendered.getByText('should change')
164+
expect(rendered.getByText('should change')).toBeInTheDocument()
165165

166166
queryClient.clear()
167167
})
@@ -174,13 +174,12 @@ describe('React hydration', () => {
174174
// since they don't have any observers on the current page that would update.
175175
test('should hydrate new but not existing queries if transition is aborted', async () => {
176176
const initialDehydratedState = JSON.parse(stringifiedState)
177-
const queryCache = new QueryCache()
178-
const queryClient = createQueryClient({ queryCache })
177+
const queryClient = createQueryClient()
179178

180179
function Page({ queryKey }: { queryKey: [string] }) {
181180
const { data } = useQuery({
182181
queryKey,
183-
queryFn: () => dataQuery(queryKey),
182+
queryFn: () => sleep(20).then(() => queryKey),
184183
})
185184
return (
186185
<div>
@@ -197,20 +196,23 @@ describe('React hydration', () => {
197196
</QueryClientProvider>,
198197
)
199198

200-
await rendered.findByText('string')
199+
await vi.advanceTimersByTimeAsync(1)
200+
expect(rendered.getByText('stringCached')).toBeInTheDocument()
201+
await vi.advanceTimersByTimeAsync(20)
202+
expect(rendered.getByText('string')).toBeInTheDocument()
201203

202-
const intermediateCache = new QueryCache()
203-
const intermediateClient = createQueryClient({
204-
queryCache: intermediateCache,
205-
})
206-
await intermediateClient.prefetchQuery({
204+
const intermediateClient = createQueryClient()
205+
intermediateClient.prefetchQuery({
207206
queryKey: ['string'],
208-
queryFn: () => dataQuery(['should not change']),
207+
queryFn: () => sleep(20).then(() => ['should not change']),
209208
})
210-
await intermediateClient.prefetchQuery({
209+
await vi.advanceTimersByTimeAsync(20)
210+
intermediateClient.prefetchQuery({
211211
queryKey: ['added'],
212-
queryFn: () => dataQuery(['added']),
212+
queryFn: () => sleep(20).then(() => ['added']),
213213
})
214+
await vi.advanceTimersByTimeAsync(20)
215+
214216
const newDehydratedState = dehydrate(intermediateClient)
215217
intermediateClient.clear()
216218

@@ -250,31 +252,30 @@ describe('React hydration', () => {
250252
)
251253

252254
// This query existed before the transition so it should stay the same
253-
rendered.getByText('string')
255+
expect(rendered.getByText('string')).toBeInTheDocument()
254256
expect(rendered.queryByText('should not change')).toBeNull()
255257
// New query data should be available immediately because it was
256258
// hydrated in the previous transition, even though the new dehydrated
257259
// state did not contain it
258-
rendered.getByText('added')
260+
expect(rendered.getByText('added')).toBeInTheDocument()
259261
})
260262

261-
await sleep(10)
263+
await vi.advanceTimersByTimeAsync(20)
262264
// It should stay the same even after effects have had a chance to run
263-
rendered.getByText('string')
265+
expect(rendered.getByText('string')).toBeInTheDocument()
264266
expect(rendered.queryByText('should not change')).toBeNull()
265267

266268
queryClient.clear()
267269
})
268270

269271
test('should hydrate queries to new cache if cache changes', async () => {
270272
const dehydratedState = JSON.parse(stringifiedState)
271-
const queryCache = new QueryCache()
272-
const queryClient = createQueryClient({ queryCache })
273+
const queryClient = createQueryClient()
273274

274275
function Page() {
275276
const { data } = useQuery({
276277
queryKey: ['string'],
277-
queryFn: () => dataQuery(['string']),
278+
queryFn: () => sleep(20).then(() => ['string']),
278279
})
279280
return (
280281
<div>
@@ -291,12 +292,11 @@ describe('React hydration', () => {
291292
</QueryClientProvider>,
292293
)
293294

294-
await rendered.findByText('string')
295-
296-
const newClientQueryCache = new QueryCache()
297-
const newClientQueryClient = createQueryClient({
298-
queryCache: newClientQueryCache,
299-
})
295+
await vi.advanceTimersByTimeAsync(1)
296+
expect(rendered.getByText('stringCached')).toBeInTheDocument()
297+
await vi.advanceTimersByTimeAsync(20)
298+
expect(rendered.getByText('string')).toBeInTheDocument()
299+
const newClientQueryClient = createQueryClient()
300300

301301
rendered.rerender(
302302
<QueryClientProvider client={newClientQueryClient}>
@@ -306,17 +306,16 @@ describe('React hydration', () => {
306306
</QueryClientProvider>,
307307
)
308308

309-
await sleep(10)
310-
rendered.getByText('string')
309+
await vi.advanceTimersByTimeAsync(20)
310+
expect(rendered.getByText('string')).toBeInTheDocument()
311311

312312
queryClient.clear()
313313
newClientQueryClient.clear()
314314
})
315315
})
316316

317317
test('should not hydrate queries if state is null', async () => {
318-
const queryCache = new QueryCache()
319-
const queryClient = createQueryClient({ queryCache })
318+
const queryClient = createQueryClient()
320319

321320
const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
322321

@@ -332,15 +331,19 @@ describe('React hydration', () => {
332331
</QueryClientProvider>,
333332
)
334333

335-
expect(hydrateSpy).toHaveBeenCalledTimes(0)
334+
await Promise.all(
335+
Array.from({ length: 1000 }).map(async (_, index) => {
336+
await vi.advanceTimersByTimeAsync(index)
337+
expect(hydrateSpy).toHaveBeenCalledTimes(0)
338+
}),
339+
)
336340

337341
hydrateSpy.mockRestore()
338342
queryClient.clear()
339343
})
340344

341345
test('should not hydrate queries if state is undefined', async () => {
342-
const queryCache = new QueryCache()
343-
const queryClient = createQueryClient({ queryCache })
346+
const queryClient = createQueryClient()
344347

345348
const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
346349

@@ -356,7 +359,12 @@ describe('React hydration', () => {
356359
</QueryClientProvider>,
357360
)
358361

359-
expect(hydrateSpy).toHaveBeenCalledTimes(0)
362+
await Promise.all(
363+
Array.from({ length: 1000 }).map(async (_, index) => {
364+
await vi.advanceTimersByTimeAsync(index)
365+
expect(hydrateSpy).toHaveBeenCalledTimes(0)
366+
}),
367+
)
360368

361369
hydrateSpy.mockRestore()
362370
queryClient.clear()

0 commit comments

Comments
 (0)