@@ -3,8 +3,8 @@ import type {
3
3
QueryClient ,
4
4
InfiniteQueryObserverResult ,
5
5
} from '@tanstack/query-core'
6
- import { InfiniteQueryObserver } from '@tanstack/query-core'
7
- import { createQueryClient , queryKey } from './utils'
6
+ import { InfiniteQueryObserver , CancelledError } from '@tanstack/query-core'
7
+ import { createQueryClient , queryKey , sleep } from './utils'
8
8
9
9
describe ( 'InfiniteQueryBehavior' , ( ) => {
10
10
let queryClient : QueryClient
@@ -179,4 +179,290 @@ describe('InfiniteQueryBehavior', () => {
179
179
180
180
unsubscribe ( )
181
181
} )
182
+
183
+ test ( 'InfiniteQueryBehavior should apply pageParam' , async ( ) => {
184
+ const key = queryKey ( )
185
+ let abortSignal : AbortSignal | null = null
186
+
187
+ const queryFnSpy = jest . fn ( ) . mockImplementation ( ( { pageParam, signal } ) => {
188
+ abortSignal = signal
189
+ return pageParam ?? 1
190
+ } )
191
+
192
+ const observer = new InfiniteQueryObserver < number > ( queryClient , {
193
+ queryKey : key ,
194
+ queryFn : queryFnSpy ,
195
+ } )
196
+
197
+ let observerResult :
198
+ | InfiniteQueryObserverResult < unknown , unknown >
199
+ | undefined
200
+
201
+ const unsubscribe = observer . subscribe ( ( result ) => {
202
+ observerResult = result
203
+ } )
204
+
205
+ // Wait for the first page to be fetched
206
+ await waitFor ( ( ) =>
207
+ expect ( observerResult ) . toMatchObject ( {
208
+ isFetching : false ,
209
+ data : { pages : [ 1 ] , pageParams : [ undefined ] } ,
210
+ } ) ,
211
+ )
212
+
213
+ queryFnSpy . mockClear ( )
214
+
215
+ // Fetch the next page using pageParam
216
+ await observer . fetchNextPage ( { pageParam : 2 } )
217
+
218
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
219
+ queryKey : key ,
220
+ pageParam : 2 ,
221
+ meta : undefined ,
222
+ signal : abortSignal ,
223
+ } )
224
+
225
+ expect ( observerResult ) . toMatchObject ( {
226
+ isFetching : false ,
227
+ data : { pages : [ 1 , 2 ] , pageParams : [ undefined , 2 ] } ,
228
+ } )
229
+
230
+ queryFnSpy . mockClear ( )
231
+
232
+ // Fetch the previous page using pageParam
233
+ await observer . fetchPreviousPage ( { pageParam : 0 } )
234
+
235
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
236
+ queryKey : key ,
237
+ pageParam : 0 ,
238
+ meta : undefined ,
239
+ signal : abortSignal ,
240
+ } )
241
+
242
+ expect ( observerResult ) . toMatchObject ( {
243
+ isFetching : false ,
244
+ data : { pages : [ 0 , 1 , 2 ] , pageParams : [ 0 , undefined , 2 ] } ,
245
+ } )
246
+
247
+ queryFnSpy . mockClear ( )
248
+
249
+ // Refetch pages: old manual page params should be used
250
+ await observer . refetch ( )
251
+
252
+ expect ( queryFnSpy ) . toHaveBeenCalledTimes ( 3 )
253
+
254
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
255
+ queryKey : key ,
256
+ pageParam : 0 ,
257
+ meta : undefined ,
258
+ signal : abortSignal ,
259
+ } )
260
+
261
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 2 , {
262
+ queryKey : key ,
263
+ pageParam : undefined ,
264
+ meta : undefined ,
265
+ signal : abortSignal ,
266
+ } )
267
+
268
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 3 , {
269
+ queryKey : key ,
270
+ pageParam : 2 ,
271
+ meta : undefined ,
272
+ signal : abortSignal ,
273
+ } )
274
+
275
+ unsubscribe ( )
276
+ } )
277
+
278
+ test ( 'InfiniteQueryBehavior should support query cancellation' , async ( ) => {
279
+ const key = queryKey ( )
280
+ let abortSignal : AbortSignal | null = null
281
+
282
+ const queryFnSpy = jest
283
+ . fn ( )
284
+ . mockImplementation ( ( { pageParam = 1 , signal } ) => {
285
+ abortSignal = signal
286
+ sleep ( 10 )
287
+ return pageParam
288
+ } )
289
+
290
+ const observer = new InfiniteQueryObserver < number > ( queryClient , {
291
+ queryKey : key ,
292
+ queryFn : queryFnSpy ,
293
+ getNextPageParam : ( lastPage ) => lastPage + 1 ,
294
+ getPreviousPageParam : ( firstPage ) => firstPage - 1 ,
295
+ } )
296
+
297
+ let observerResult :
298
+ | InfiniteQueryObserverResult < unknown , unknown >
299
+ | undefined
300
+
301
+ const unsubscribe = observer . subscribe ( ( result ) => {
302
+ observerResult = result
303
+ } )
304
+
305
+ const query = observer . getCurrentQuery ( )
306
+ query . cancel ( )
307
+
308
+ // Wait for the first page to be cancelled
309
+ await waitFor ( ( ) =>
310
+ expect ( observerResult ) . toMatchObject ( {
311
+ isFetching : false ,
312
+ isError : true ,
313
+ error : new CancelledError ( ) ,
314
+ data : undefined ,
315
+ } ) ,
316
+ )
317
+
318
+ expect ( queryFnSpy ) . toHaveBeenCalledTimes ( 1 )
319
+
320
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
321
+ queryKey : key ,
322
+ pageParam : undefined ,
323
+ meta : undefined ,
324
+ signal : abortSignal ,
325
+ } )
326
+
327
+ unsubscribe ( )
328
+ } )
329
+
330
+ test ( 'InfiniteQueryBehavior should not refetch pages if the query is cancelled' , async ( ) => {
331
+ const key = queryKey ( )
332
+ let abortSignal : AbortSignal | null = null
333
+
334
+ let queryFnSpy = jest
335
+ . fn ( )
336
+ . mockImplementation ( ( { pageParam = 1 , signal } ) => {
337
+ abortSignal = signal
338
+ return pageParam
339
+ } )
340
+
341
+ const observer = new InfiniteQueryObserver < number > ( queryClient , {
342
+ queryKey : key ,
343
+ queryFn : queryFnSpy ,
344
+ getNextPageParam : ( lastPage ) => lastPage + 1 ,
345
+ getPreviousPageParam : ( firstPage ) => firstPage - 1 ,
346
+ } )
347
+
348
+ let observerResult :
349
+ | InfiniteQueryObserverResult < unknown , unknown >
350
+ | undefined
351
+
352
+ const unsubscribe = observer . subscribe ( ( result ) => {
353
+ observerResult = result
354
+ } )
355
+
356
+ // Wait for the first page to be fetched
357
+ await waitFor ( ( ) =>
358
+ expect ( observerResult ) . toMatchObject ( {
359
+ isFetching : false ,
360
+ data : { pages : [ 1 ] , pageParams : [ undefined ] } ,
361
+ } ) ,
362
+ )
363
+
364
+ queryFnSpy . mockClear ( )
365
+
366
+ // Fetch the second page
367
+ await observer . fetchNextPage ( )
368
+
369
+ expect ( observerResult ) . toMatchObject ( {
370
+ isFetching : false ,
371
+ data : { pages : [ 1 , 2 ] , pageParams : [ undefined , 2 ] } ,
372
+ } )
373
+
374
+ expect ( queryFnSpy ) . toHaveBeenCalledTimes ( 1 )
375
+
376
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
377
+ queryKey : key ,
378
+ pageParam : 2 ,
379
+ meta : undefined ,
380
+ signal : abortSignal ,
381
+ } )
382
+
383
+ queryFnSpy = jest . fn ( ) . mockImplementation ( ( { pageParam = 1 , signal } ) => {
384
+ abortSignal = signal
385
+ sleep ( 10 )
386
+ return pageParam
387
+ } )
388
+
389
+ // Refetch the query
390
+ observer . refetch ( )
391
+ expect ( observerResult ) . toMatchObject ( {
392
+ isFetching : true ,
393
+ isError : false ,
394
+ } )
395
+
396
+ // Cancel the query
397
+ const query = observer . getCurrentQuery ( )
398
+ query . cancel ( )
399
+
400
+ expect ( observerResult ) . toMatchObject ( {
401
+ isFetching : false ,
402
+ isError : true ,
403
+ error : new CancelledError ( ) ,
404
+ data : { pages : [ 1 , 2 ] , pageParams : [ undefined , 2 ] } ,
405
+ } )
406
+
407
+ // Pages should not have been fetched
408
+ expect ( queryFnSpy ) . toHaveBeenCalledTimes ( 0 )
409
+
410
+ unsubscribe ( )
411
+ } )
412
+
413
+ test ( 'InfiniteQueryBehavior should return the current pages if no page param can be determined' , async ( ) => {
414
+ const key = queryKey ( )
415
+ let abortSignal : AbortSignal | null = null
416
+
417
+ const queryFnSpy = jest
418
+ . fn ( )
419
+ . mockImplementation ( ( { pageParam = 1 , signal } ) => {
420
+ abortSignal = signal
421
+ return pageParam
422
+ } )
423
+
424
+ const observer = new InfiniteQueryObserver < number > ( queryClient , {
425
+ queryKey : key ,
426
+ queryFn : queryFnSpy ,
427
+ } )
428
+
429
+ let observerResult :
430
+ | InfiniteQueryObserverResult < unknown , unknown >
431
+ | undefined
432
+
433
+ const unsubscribe = observer . subscribe ( ( result ) => {
434
+ observerResult = result
435
+ } )
436
+
437
+ // Wait for the first page to be fetched
438
+ await waitFor ( ( ) =>
439
+ expect ( observerResult ) . toMatchObject ( {
440
+ isFetching : false ,
441
+ data : { pages : [ 1 ] , pageParams : [ undefined ] } ,
442
+ } ) ,
443
+ )
444
+
445
+ expect ( queryFnSpy ) . toHaveBeenNthCalledWith ( 1 , {
446
+ queryKey : key ,
447
+ pageParam : undefined ,
448
+ meta : undefined ,
449
+ signal : abortSignal ,
450
+ } )
451
+
452
+ queryFnSpy . mockClear ( )
453
+
454
+ // Try to fetch a page without page param and getNextPageParam defined
455
+ await observer . fetchNextPage ( )
456
+
457
+ // Current pages should be returned
458
+ expect ( observerResult ) . toMatchObject ( {
459
+ isFetching : false ,
460
+ data : { pages : [ 1 ] , pageParams : [ undefined ] } ,
461
+ } )
462
+
463
+ // queryFnSpy should not be called
464
+ expect ( queryFnSpy ) . toBeCalledTimes ( 0 )
465
+
466
+ unsubscribe ( )
467
+ } )
182
468
} )
0 commit comments