@tanstack/query-core 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/build/cjs/focusManager.js +101 -0
  2. package/build/cjs/focusManager.js.map +1 -0
  3. package/build/cjs/hydration.js +112 -0
  4. package/build/cjs/hydration.js.map +1 -0
  5. package/build/cjs/index.js +51 -0
  6. package/build/cjs/index.js.map +1 -0
  7. package/build/cjs/infiniteQueryBehavior.js +160 -0
  8. package/build/cjs/infiniteQueryBehavior.js.map +1 -0
  9. package/build/cjs/infiniteQueryObserver.js +92 -0
  10. package/build/cjs/infiniteQueryObserver.js.map +1 -0
  11. package/build/cjs/logger.js +18 -0
  12. package/build/cjs/logger.js.map +1 -0
  13. package/build/cjs/mutation.js +258 -0
  14. package/build/cjs/mutation.js.map +1 -0
  15. package/build/cjs/mutationCache.js +99 -0
  16. package/build/cjs/mutationCache.js.map +1 -0
  17. package/build/cjs/mutationObserver.js +130 -0
  18. package/build/cjs/mutationObserver.js.map +1 -0
  19. package/build/cjs/notifyManager.js +114 -0
  20. package/build/cjs/notifyManager.js.map +1 -0
  21. package/build/cjs/onlineManager.js +100 -0
  22. package/build/cjs/onlineManager.js.map +1 -0
  23. package/build/cjs/queriesObserver.js +170 -0
  24. package/build/cjs/queriesObserver.js.map +1 -0
  25. package/build/cjs/query.js +474 -0
  26. package/build/cjs/query.js.map +1 -0
  27. package/build/cjs/queryCache.js +140 -0
  28. package/build/cjs/queryCache.js.map +1 -0
  29. package/build/cjs/queryClient.js +357 -0
  30. package/build/cjs/queryClient.js.map +1 -0
  31. package/build/cjs/queryObserver.js +521 -0
  32. package/build/cjs/queryObserver.js.map +1 -0
  33. package/build/cjs/removable.js +47 -0
  34. package/build/cjs/removable.js.map +1 -0
  35. package/build/cjs/retryer.js +177 -0
  36. package/build/cjs/retryer.js.map +1 -0
  37. package/build/cjs/subscribable.js +43 -0
  38. package/build/cjs/subscribable.js.map +1 -0
  39. package/build/cjs/utils.js +356 -0
  40. package/build/cjs/utils.js.map +1 -0
  41. package/build/esm/index.js +3077 -0
  42. package/build/esm/index.js.map +1 -0
  43. package/build/stats-html.html +2689 -0
  44. package/build/umd/index.development.js +3106 -0
  45. package/build/umd/index.development.js.map +1 -0
  46. package/build/umd/index.production.js +12 -0
  47. package/build/umd/index.production.js.map +1 -0
  48. package/package.json +25 -0
  49. package/src/focusManager.ts +89 -0
  50. package/src/hydration.ts +164 -0
  51. package/src/index.ts +35 -0
  52. package/src/infiniteQueryBehavior.ts +214 -0
  53. package/src/infiniteQueryObserver.ts +159 -0
  54. package/src/logger.native.ts +11 -0
  55. package/src/logger.ts +9 -0
  56. package/src/mutation.ts +349 -0
  57. package/src/mutationCache.ts +157 -0
  58. package/src/mutationObserver.ts +195 -0
  59. package/src/notifyManager.ts +96 -0
  60. package/src/onlineManager.ts +89 -0
  61. package/src/queriesObserver.ts +211 -0
  62. package/src/query.ts +612 -0
  63. package/src/queryCache.ts +206 -0
  64. package/src/queryClient.ts +716 -0
  65. package/src/queryObserver.ts +748 -0
  66. package/src/removable.ts +37 -0
  67. package/src/retryer.ts +215 -0
  68. package/src/subscribable.ts +33 -0
  69. package/src/tests/focusManager.test.tsx +155 -0
  70. package/src/tests/hydration.test.tsx +429 -0
  71. package/src/tests/infiniteQueryBehavior.test.tsx +124 -0
  72. package/src/tests/infiniteQueryObserver.test.tsx +64 -0
  73. package/src/tests/mutationCache.test.tsx +260 -0
  74. package/src/tests/mutationObserver.test.tsx +75 -0
  75. package/src/tests/mutations.test.tsx +363 -0
  76. package/src/tests/notifyManager.test.tsx +51 -0
  77. package/src/tests/onlineManager.test.tsx +148 -0
  78. package/src/tests/queriesObserver.test.tsx +330 -0
  79. package/src/tests/query.test.tsx +888 -0
  80. package/src/tests/queryCache.test.tsx +236 -0
  81. package/src/tests/queryClient.test.tsx +1435 -0
  82. package/src/tests/queryObserver.test.tsx +802 -0
  83. package/src/tests/utils.test.tsx +360 -0
  84. package/src/types.ts +705 -0
  85. package/src/utils.ts +435 -0
@@ -0,0 +1,429 @@
1
+ import {
2
+ createQueryClient,
3
+ executeMutation,
4
+ mockNavigatorOnLine,
5
+ sleep,
6
+ } from '../../../../tests/utils'
7
+ import { QueryCache } from '../queryCache'
8
+ import { dehydrate, hydrate } from '../hydration'
9
+
10
+ async function fetchData<TData>(value: TData, ms?: number): Promise<TData> {
11
+ await sleep(ms || 0)
12
+ return value
13
+ }
14
+
15
+ describe('dehydration and rehydration', () => {
16
+ test('should work with serializeable values', async () => {
17
+ const queryCache = new QueryCache()
18
+ const queryClient = createQueryClient({ queryCache })
19
+ await queryClient.prefetchQuery(['string'], () => fetchData('string'))
20
+ await queryClient.prefetchQuery(['number'], () => fetchData(1))
21
+ await queryClient.prefetchQuery(['boolean'], () => fetchData(true))
22
+ await queryClient.prefetchQuery(['null'], () => fetchData(null))
23
+ await queryClient.prefetchQuery(['array'], () => fetchData(['string', 0]))
24
+ await queryClient.prefetchQuery(['nested'], () =>
25
+ fetchData({ key: [{ nestedKey: 1 }] }),
26
+ )
27
+ const dehydrated = dehydrate(queryClient)
28
+ const stringified = JSON.stringify(dehydrated)
29
+
30
+ // ---
31
+
32
+ const parsed = JSON.parse(stringified)
33
+ const hydrationCache = new QueryCache()
34
+ const hydrationClient = createQueryClient({
35
+ queryCache: hydrationCache,
36
+ })
37
+ hydrate(hydrationClient, parsed)
38
+ expect(hydrationCache.find(['string'])?.state.data).toBe('string')
39
+ expect(hydrationCache.find(['number'])?.state.data).toBe(1)
40
+ expect(hydrationCache.find(['boolean'])?.state.data).toBe(true)
41
+ expect(hydrationCache.find(['null'])?.state.data).toBe(null)
42
+ expect(hydrationCache.find(['array'])?.state.data).toEqual(['string', 0])
43
+ expect(hydrationCache.find(['nested'])?.state.data).toEqual({
44
+ key: [{ nestedKey: 1 }],
45
+ })
46
+
47
+ const fetchDataAfterHydration = jest.fn<unknown, unknown[]>()
48
+ await hydrationClient.prefetchQuery(['string'], fetchDataAfterHydration, {
49
+ staleTime: 1000,
50
+ })
51
+ await hydrationClient.prefetchQuery(['number'], fetchDataAfterHydration, {
52
+ staleTime: 1000,
53
+ })
54
+ await hydrationClient.prefetchQuery(['boolean'], fetchDataAfterHydration, {
55
+ staleTime: 1000,
56
+ })
57
+ await hydrationClient.prefetchQuery(['null'], fetchDataAfterHydration, {
58
+ staleTime: 1000,
59
+ })
60
+ await hydrationClient.prefetchQuery(['array'], fetchDataAfterHydration, {
61
+ staleTime: 1000,
62
+ })
63
+ await hydrationClient.prefetchQuery(['nested'], fetchDataAfterHydration, {
64
+ staleTime: 1000,
65
+ })
66
+ expect(fetchDataAfterHydration).toHaveBeenCalledTimes(0)
67
+
68
+ queryClient.clear()
69
+ hydrationClient.clear()
70
+ })
71
+
72
+ test('should not dehydrate queries if dehydrateQueries is set to false', async () => {
73
+ const queryCache = new QueryCache()
74
+ const queryClient = createQueryClient({ queryCache })
75
+ await queryClient.prefetchQuery(['string'], () => fetchData('string'))
76
+
77
+ const dehydrated = dehydrate(queryClient, { dehydrateQueries: false })
78
+
79
+ expect(dehydrated.queries.length).toBe(0)
80
+
81
+ queryClient.clear()
82
+ })
83
+
84
+ test('should use the cache time from the client', async () => {
85
+ const queryCache = new QueryCache()
86
+ const queryClient = createQueryClient({ queryCache })
87
+ await queryClient.prefetchQuery(['string'], () => fetchData('string'), {
88
+ cacheTime: 50,
89
+ })
90
+ const dehydrated = dehydrate(queryClient)
91
+ const stringified = JSON.stringify(dehydrated)
92
+
93
+ await sleep(20)
94
+
95
+ // ---
96
+
97
+ const parsed = JSON.parse(stringified)
98
+ const hydrationCache = new QueryCache()
99
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
100
+ hydrate(hydrationClient, parsed)
101
+ expect(hydrationCache.find(['string'])?.state.data).toBe('string')
102
+ await sleep(100)
103
+ expect(hydrationCache.find(['string'])).toBeTruthy()
104
+
105
+ queryClient.clear()
106
+ hydrationClient.clear()
107
+ })
108
+
109
+ test('should be able to provide default options for the hydrated queries', async () => {
110
+ const queryCache = new QueryCache()
111
+ const queryClient = createQueryClient({ queryCache })
112
+ await queryClient.prefetchQuery(['string'], () => fetchData('string'))
113
+ const dehydrated = dehydrate(queryClient)
114
+ const stringified = JSON.stringify(dehydrated)
115
+ const parsed = JSON.parse(stringified)
116
+ const hydrationCache = new QueryCache()
117
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
118
+ hydrate(hydrationClient, parsed, {
119
+ defaultOptions: { queries: { retry: 10 } },
120
+ })
121
+ expect(hydrationCache.find(['string'])?.options.retry).toBe(10)
122
+ queryClient.clear()
123
+ hydrationClient.clear()
124
+ })
125
+
126
+ test('should work with complex keys', async () => {
127
+ const queryCache = new QueryCache()
128
+ const queryClient = createQueryClient({ queryCache })
129
+ await queryClient.prefetchQuery(
130
+ ['string', { key: ['string'], key2: 0 }],
131
+ () => fetchData('string'),
132
+ )
133
+ const dehydrated = dehydrate(queryClient)
134
+ const stringified = JSON.stringify(dehydrated)
135
+
136
+ // ---
137
+
138
+ const parsed = JSON.parse(stringified)
139
+ const hydrationCache = new QueryCache()
140
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
141
+ hydrate(hydrationClient, parsed)
142
+ expect(
143
+ hydrationCache.find(['string', { key: ['string'], key2: 0 }])?.state.data,
144
+ ).toBe('string')
145
+
146
+ const fetchDataAfterHydration = jest.fn<unknown, unknown[]>()
147
+ await hydrationClient.prefetchQuery(
148
+ ['string', { key: ['string'], key2: 0 }],
149
+ fetchDataAfterHydration,
150
+ { staleTime: 100 },
151
+ )
152
+ expect(fetchDataAfterHydration).toHaveBeenCalledTimes(0)
153
+
154
+ queryClient.clear()
155
+ hydrationClient.clear()
156
+ })
157
+
158
+ test('should only hydrate successful queries by default', async () => {
159
+ const consoleMock = jest.spyOn(console, 'error')
160
+ consoleMock.mockImplementation(() => undefined)
161
+
162
+ const queryCache = new QueryCache()
163
+ const queryClient = createQueryClient({ queryCache })
164
+ await queryClient.prefetchQuery(['success'], () => fetchData('success'))
165
+ queryClient.prefetchQuery(['loading'], () => fetchData('loading', 10000))
166
+ await queryClient.prefetchQuery(['error'], () => {
167
+ throw new Error()
168
+ })
169
+ const dehydrated = dehydrate(queryClient)
170
+ const stringified = JSON.stringify(dehydrated)
171
+
172
+ // ---
173
+
174
+ const parsed = JSON.parse(stringified)
175
+ const hydrationCache = new QueryCache()
176
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
177
+ hydrate(hydrationClient, parsed)
178
+
179
+ expect(hydrationCache.find(['success'])).toBeTruthy()
180
+ expect(hydrationCache.find(['loading'])).toBeFalsy()
181
+ expect(hydrationCache.find(['error'])).toBeFalsy()
182
+
183
+ queryClient.clear()
184
+ hydrationClient.clear()
185
+ consoleMock.mockRestore()
186
+ })
187
+
188
+ test('should filter queries via shouldDehydrateQuery', async () => {
189
+ const queryCache = new QueryCache()
190
+ const queryClient = createQueryClient({ queryCache })
191
+ await queryClient.prefetchQuery(['string'], () => fetchData('string'))
192
+ await queryClient.prefetchQuery(['number'], () => fetchData(1))
193
+ const dehydrated = dehydrate(queryClient, {
194
+ shouldDehydrateQuery: (query) => query.queryKey[0] !== 'string',
195
+ })
196
+
197
+ // This is testing implementation details that can change and are not
198
+ // part of the public API, but is important for keeping the payload small
199
+ const dehydratedQuery = dehydrated.queries.find(
200
+ (query) => query.queryKey[0] === 'string',
201
+ )
202
+ expect(dehydratedQuery).toBeUndefined()
203
+
204
+ const stringified = JSON.stringify(dehydrated)
205
+
206
+ // ---
207
+
208
+ const parsed = JSON.parse(stringified)
209
+ const hydrationCache = new QueryCache()
210
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
211
+ hydrate(hydrationClient, parsed)
212
+ expect(hydrationCache.find(['string'])).toBeUndefined()
213
+ expect(hydrationCache.find(['number'])?.state.data).toBe(1)
214
+
215
+ queryClient.clear()
216
+ hydrationClient.clear()
217
+ })
218
+
219
+ test('should not overwrite query in cache if hydrated query is older', async () => {
220
+ const queryCache = new QueryCache()
221
+ const queryClient = createQueryClient({ queryCache })
222
+ await queryClient.prefetchQuery(['string'], () =>
223
+ fetchData('string-older', 5),
224
+ )
225
+ const dehydrated = dehydrate(queryClient)
226
+ const stringified = JSON.stringify(dehydrated)
227
+
228
+ // ---
229
+
230
+ const parsed = JSON.parse(stringified)
231
+ const hydrationCache = new QueryCache()
232
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
233
+ await hydrationClient.prefetchQuery(['string'], () =>
234
+ fetchData('string-newer', 5),
235
+ )
236
+
237
+ hydrate(hydrationClient, parsed)
238
+ expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer')
239
+
240
+ queryClient.clear()
241
+ hydrationClient.clear()
242
+ })
243
+
244
+ test('should overwrite query in cache if hydrated query is newer', async () => {
245
+ const hydrationCache = new QueryCache()
246
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
247
+ await hydrationClient.prefetchQuery(['string'], () =>
248
+ fetchData('string-older', 5),
249
+ )
250
+
251
+ // ---
252
+
253
+ const queryCache = new QueryCache()
254
+ const queryClient = createQueryClient({ queryCache })
255
+ await queryClient.prefetchQuery(['string'], () =>
256
+ fetchData('string-newer', 5),
257
+ )
258
+ const dehydrated = dehydrate(queryClient)
259
+ const stringified = JSON.stringify(dehydrated)
260
+
261
+ // ---
262
+
263
+ const parsed = JSON.parse(stringified)
264
+ hydrate(hydrationClient, parsed)
265
+ expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer')
266
+
267
+ queryClient.clear()
268
+ hydrationClient.clear()
269
+ })
270
+
271
+ test('should be able to dehydrate mutations and continue on hydration', async () => {
272
+ const consoleMock = jest.spyOn(console, 'error')
273
+ consoleMock.mockImplementation(() => undefined)
274
+ const onlineMock = mockNavigatorOnLine(false)
275
+
276
+ const serverAddTodo = jest
277
+ .fn()
278
+ .mockImplementation(() => Promise.reject('offline'))
279
+ const serverOnMutate = jest.fn().mockImplementation((variables) => {
280
+ const optimisticTodo = { id: 1, text: variables.text }
281
+ return { optimisticTodo }
282
+ })
283
+ const serverOnSuccess = jest.fn()
284
+
285
+ const serverClient = createQueryClient()
286
+
287
+ serverClient.setMutationDefaults(['addTodo'], {
288
+ mutationFn: serverAddTodo,
289
+ onMutate: serverOnMutate,
290
+ onSuccess: serverOnSuccess,
291
+ retry: 3,
292
+ retryDelay: 10,
293
+ })
294
+
295
+ executeMutation(serverClient, {
296
+ mutationKey: ['addTodo'],
297
+ variables: { text: 'text' },
298
+ }).catch(() => undefined)
299
+
300
+ await sleep(50)
301
+
302
+ const dehydrated = dehydrate(serverClient)
303
+ const stringified = JSON.stringify(dehydrated)
304
+
305
+ serverClient.clear()
306
+
307
+ // ---
308
+
309
+ onlineMock.mockReturnValue(true)
310
+
311
+ const parsed = JSON.parse(stringified)
312
+ const client = createQueryClient()
313
+
314
+ const clientAddTodo = jest.fn().mockImplementation((variables) => {
315
+ return { id: 2, text: variables.text }
316
+ })
317
+ const clientOnMutate = jest.fn().mockImplementation((variables) => {
318
+ const optimisticTodo = { id: 1, text: variables.text }
319
+ return { optimisticTodo }
320
+ })
321
+ const clientOnSuccess = jest.fn()
322
+
323
+ client.setMutationDefaults(['addTodo'], {
324
+ mutationFn: clientAddTodo,
325
+ onMutate: clientOnMutate,
326
+ onSuccess: clientOnSuccess,
327
+ retry: 3,
328
+ retryDelay: 10,
329
+ })
330
+
331
+ hydrate(client, parsed)
332
+
333
+ await client.resumePausedMutations()
334
+
335
+ expect(clientAddTodo).toHaveBeenCalledTimes(1)
336
+ expect(clientOnMutate).not.toHaveBeenCalled()
337
+ expect(clientOnSuccess).toHaveBeenCalledTimes(1)
338
+ expect(clientOnSuccess).toHaveBeenCalledWith(
339
+ { id: 2, text: 'text' },
340
+ { text: 'text' },
341
+ { optimisticTodo: { id: 1, text: 'text' } },
342
+ )
343
+
344
+ client.clear()
345
+ consoleMock.mockRestore()
346
+ onlineMock.mockRestore()
347
+ })
348
+
349
+ test('should not dehydrate mutations if dehydrateMutations is set to false', async () => {
350
+ const consoleMock = jest.spyOn(console, 'error')
351
+ consoleMock.mockImplementation(() => undefined)
352
+
353
+ const serverAddTodo = jest
354
+ .fn()
355
+ .mockImplementation(() => Promise.reject('offline'))
356
+
357
+ const queryClient = createQueryClient()
358
+
359
+ queryClient.setMutationDefaults(['addTodo'], {
360
+ mutationFn: serverAddTodo,
361
+ retry: false,
362
+ })
363
+
364
+ executeMutation(queryClient, {
365
+ mutationKey: ['addTodo'],
366
+ variables: { text: 'text' },
367
+ }).catch(() => undefined)
368
+
369
+ await sleep(1)
370
+ const dehydrated = dehydrate(queryClient, { dehydrateMutations: false })
371
+
372
+ expect(dehydrated.mutations.length).toBe(0)
373
+
374
+ queryClient.clear()
375
+ consoleMock.mockRestore()
376
+ })
377
+
378
+ test('should not dehydrate mutation if mutation state is set to pause', async () => {
379
+ const consoleMock = jest.spyOn(console, 'error')
380
+ consoleMock.mockImplementation(() => undefined)
381
+
382
+ const serverAddTodo = jest
383
+ .fn()
384
+ .mockImplementation(() => Promise.reject('offline'))
385
+
386
+ const queryClient = createQueryClient()
387
+
388
+ queryClient.setMutationDefaults(['addTodo'], {
389
+ mutationFn: serverAddTodo,
390
+ retry: 1,
391
+ retryDelay: 20,
392
+ })
393
+
394
+ executeMutation(queryClient, {
395
+ mutationKey: ['addTodo'],
396
+ variables: { text: 'text' },
397
+ }).catch(() => undefined)
398
+
399
+ // Dehydrate mutation between retries
400
+ await sleep(1)
401
+ const dehydrated = dehydrate(queryClient)
402
+
403
+ expect(dehydrated.mutations.length).toBe(0)
404
+
405
+ await sleep(30)
406
+ queryClient.clear()
407
+ consoleMock.mockRestore()
408
+ })
409
+
410
+ test('should not hydrate if the hydratedState is null or is not an object', async () => {
411
+ const queryCache = new QueryCache()
412
+ const queryClient = createQueryClient({ queryCache })
413
+
414
+ expect(() => hydrate(queryClient, null)).not.toThrow()
415
+ expect(() => hydrate(queryClient, 'invalid')).not.toThrow()
416
+
417
+ queryClient.clear()
418
+ })
419
+
420
+ test('should support hydratedState with undefined queries and mutations', async () => {
421
+ const queryCache = new QueryCache()
422
+ const queryClient = createQueryClient({ queryCache })
423
+
424
+ expect(() => hydrate(queryClient, {})).not.toThrow()
425
+ expect(() => hydrate(queryClient, {})).not.toThrow()
426
+
427
+ queryClient.clear()
428
+ })
429
+ })
@@ -0,0 +1,124 @@
1
+ import { waitFor } from '@testing-library/react'
2
+ import {
3
+ QueryClient,
4
+ InfiniteQueryObserver,
5
+ InfiniteQueryObserverResult,
6
+ } from '@tanstack/query-core'
7
+ import { createQueryClient, queryKey } from '../../../../tests/utils'
8
+
9
+ describe('InfiniteQueryBehavior', () => {
10
+ let queryClient: QueryClient
11
+
12
+ beforeEach(() => {
13
+ queryClient = createQueryClient()
14
+ queryClient.mount()
15
+ })
16
+
17
+ afterEach(() => {
18
+ queryClient.clear()
19
+ })
20
+
21
+ test('InfiniteQueryBehavior should throw an error if the queryFn is not defined', async () => {
22
+ const key = queryKey()
23
+
24
+ const observer = new InfiniteQueryObserver(queryClient, {
25
+ queryKey: key,
26
+ retry: false,
27
+ })
28
+
29
+ let observerResult:
30
+ | InfiniteQueryObserverResult<unknown, unknown>
31
+ | undefined
32
+
33
+ const unsubscribe = observer.subscribe((result) => {
34
+ observerResult = result
35
+ })
36
+
37
+ await waitFor(() => {
38
+ return expect(observerResult).toMatchObject({
39
+ isError: true,
40
+ error: 'Missing queryFn',
41
+ })
42
+ })
43
+
44
+ unsubscribe()
45
+ })
46
+
47
+ test('InfiniteQueryBehavior should not refetch the first page if another page refetched', async () => {
48
+ const key = queryKey()
49
+ let abortSignal: AbortSignal | null = null
50
+
51
+ const queryFnSpy = jest
52
+ .fn()
53
+ .mockImplementation(({ pageParam = 1, signal }) => {
54
+ abortSignal = signal
55
+ return pageParam
56
+ })
57
+
58
+ const observer = new InfiniteQueryObserver<number>(queryClient, {
59
+ queryKey: key,
60
+ queryFn: queryFnSpy,
61
+ getNextPageParam: (lastPage) => lastPage + 1,
62
+ })
63
+
64
+ let observerResult:
65
+ | InfiniteQueryObserverResult<unknown, unknown>
66
+ | undefined
67
+
68
+ const unsubscribe = observer.subscribe((result) => {
69
+ observerResult = result
70
+ })
71
+
72
+ // Wait for the first page to be fetched
73
+ await waitFor(() =>
74
+ expect(observerResult).toMatchObject({
75
+ isFetching: false,
76
+ data: { pages: [1] },
77
+ }),
78
+ )
79
+
80
+ expect(queryFnSpy).toHaveBeenNthCalledWith(1, {
81
+ queryKey: key,
82
+ pageParam: undefined,
83
+ meta: undefined,
84
+ signal: abortSignal,
85
+ })
86
+
87
+ queryFnSpy.mockClear()
88
+
89
+ // Fetch the second page
90
+ await observer.fetchNextPage()
91
+
92
+ expect(queryFnSpy).toHaveBeenNthCalledWith(1, {
93
+ queryKey: key,
94
+ pageParam: 2,
95
+ meta: undefined,
96
+ signal: abortSignal,
97
+ })
98
+
99
+ expect(observerResult).toMatchObject({
100
+ isFetching: false,
101
+ data: { pages: [1, 2] },
102
+ })
103
+
104
+ queryFnSpy.mockClear()
105
+
106
+ // Refetch the second page
107
+ await queryClient.refetchQueries({
108
+ refetchPage: (_page, index) => index === 1,
109
+ })
110
+
111
+ expect(queryFnSpy).toHaveBeenNthCalledWith(1, {
112
+ queryKey: key,
113
+ pageParam: 2,
114
+ meta: undefined,
115
+ signal: abortSignal,
116
+ })
117
+
118
+ expect(observerResult).toMatchObject({
119
+ data: { pages: [1, 2] },
120
+ })
121
+
122
+ unsubscribe()
123
+ })
124
+ })
@@ -0,0 +1,64 @@
1
+ import { createQueryClient, queryKey, sleep } from '../../../../tests/utils'
2
+ import { QueryClient, InfiniteQueryObserver } from '..'
3
+
4
+ describe('InfiniteQueryObserver', () => {
5
+ let queryClient: QueryClient
6
+
7
+ beforeEach(() => {
8
+ queryClient = createQueryClient()
9
+ queryClient.mount()
10
+ })
11
+
12
+ afterEach(() => {
13
+ queryClient.clear()
14
+ })
15
+
16
+ test('InfiniteQueryObserver should be able to fetch an infinite query with selector', async () => {
17
+ const key = queryKey()
18
+ const observer = new InfiniteQueryObserver(queryClient, {
19
+ queryKey: key,
20
+ queryFn: () => 1,
21
+ select: (data) => ({
22
+ pages: data.pages.map((x) => `${x}`),
23
+ pageParams: data.pageParams,
24
+ }),
25
+ })
26
+ let observerResult
27
+ const unsubscribe = observer.subscribe((result) => {
28
+ observerResult = result
29
+ })
30
+ await sleep(1)
31
+ unsubscribe()
32
+ expect(observerResult).toMatchObject({
33
+ data: { pages: ['1'], pageParams: [undefined] },
34
+ })
35
+ })
36
+
37
+ test('InfiniteQueryObserver should pass the meta option to the queryFn', async () => {
38
+ const meta = {
39
+ it: 'works',
40
+ }
41
+
42
+ const key = queryKey()
43
+ const queryFn = jest.fn(() => 1)
44
+ const observer = new InfiniteQueryObserver(queryClient, {
45
+ meta,
46
+ queryKey: key,
47
+ queryFn,
48
+ select: (data) => ({
49
+ pages: data.pages.map((x) => `${x}`),
50
+ pageParams: data.pageParams,
51
+ }),
52
+ })
53
+ let observerResult
54
+ const unsubscribe = observer.subscribe((result) => {
55
+ observerResult = result
56
+ })
57
+ await sleep(1)
58
+ unsubscribe()
59
+ expect(observerResult).toMatchObject({
60
+ data: { pages: ['1'], pageParams: [undefined] },
61
+ })
62
+ expect(queryFn).toBeCalledWith(expect.objectContaining({ meta }))
63
+ })
64
+ })