@tanstack/solid-query 5.59.17 → 5.59.20

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.
@@ -1,956 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest'
2
- import { fireEvent, render, waitFor } from '@solidjs/testing-library'
3
-
4
- import {
5
- ErrorBoundary,
6
- Show,
7
- Suspense,
8
- createRenderEffect,
9
- createSignal,
10
- on,
11
- } from 'solid-js'
12
- import {
13
- QueryCache,
14
- QueryClientProvider,
15
- createInfiniteQuery,
16
- createQuery,
17
- } from '..'
18
- import { createQueryClient, queryKey, sleep } from './utils'
19
- import type {
20
- CreateInfiniteQueryResult,
21
- CreateQueryResult,
22
- InfiniteData,
23
- } from '..'
24
-
25
- describe("useQuery's in Suspense mode", () => {
26
- const queryCache = new QueryCache()
27
- const queryClient = createQueryClient({ queryCache })
28
-
29
- it('should render the correct amount of times in Suspense mode', async () => {
30
- const key = queryKey()
31
- const states: Array<CreateQueryResult<number>> = []
32
-
33
- let count = 0
34
- let renders = 0
35
-
36
- function Page() {
37
- const [stateKey, setStateKey] = createSignal(key)
38
-
39
- const state = createQuery(() => ({
40
- queryKey: stateKey(),
41
- queryFn: async () => {
42
- count++
43
- await sleep(10)
44
- return count
45
- },
46
- }))
47
-
48
- createRenderEffect(() => {
49
- states.push({ ...state })
50
- })
51
-
52
- createRenderEffect(
53
- on([() => ({ ...state }), () => key], () => {
54
- renders++
55
- }),
56
- )
57
-
58
- return (
59
- <div>
60
- <button aria-label="toggle" onClick={() => setStateKey(queryKey())} />
61
- data: {String(state.data)}
62
- </div>
63
- )
64
- }
65
-
66
- const rendered = render(() => (
67
- <QueryClientProvider client={queryClient}>
68
- <Suspense fallback="loading">
69
- <Page />
70
- </Suspense>
71
- </QueryClientProvider>
72
- ))
73
-
74
- await waitFor(() => rendered.getByText('data: 1'))
75
- fireEvent.click(rendered.getByLabelText('toggle'))
76
-
77
- await waitFor(() => rendered.getByText('data: 2'))
78
-
79
- expect(renders).toBe(4)
80
- expect(states.length).toBe(4)
81
- expect(states[1]).toMatchObject({ data: 1, status: 'success' })
82
- expect(states[3]).toMatchObject({ data: 2, status: 'success' })
83
- })
84
-
85
- it('should return the correct states for a successful infinite query', async () => {
86
- const key = queryKey()
87
- const states: Array<CreateInfiniteQueryResult<InfiniteData<number>>> = []
88
-
89
- function Page() {
90
- const [multiplier, setMultiplier] = createSignal(1)
91
- const state = createInfiniteQuery(() => ({
92
- queryKey: [`${key}_${multiplier()}`],
93
- queryFn: async ({ pageParam }) => {
94
- await sleep(10)
95
- return Number(pageParam * multiplier())
96
- },
97
- initialPageParam: 1,
98
- suspense: true,
99
- getNextPageParam: (lastPage) => lastPage + 1,
100
- }))
101
-
102
- createRenderEffect(() => {
103
- states.push({ ...state })
104
- })
105
-
106
- return (
107
- <div>
108
- <button onClick={() => setMultiplier(2)}>next</button>
109
- data: {state.data?.pages.join(',')}
110
- </div>
111
- )
112
- }
113
-
114
- const rendered = render(() => (
115
- <QueryClientProvider client={queryClient}>
116
- <Suspense fallback="loading">
117
- <Page />
118
- </Suspense>
119
- </QueryClientProvider>
120
- ))
121
-
122
- await waitFor(() => rendered.getByText('data: 1'))
123
-
124
- // eslint-disable-next-line cspell/spellchecker
125
- // TODO(lukemurray): in react this is 1 in solid this is 2 because suspense
126
- // occurs on read.
127
- expect(states.length).toBe(2)
128
- expect(states[1]).toMatchObject({
129
- data: { pages: [1], pageParams: [1] },
130
- status: 'success',
131
- })
132
-
133
- fireEvent.click(rendered.getByText('next'))
134
- await waitFor(() => rendered.getByText('data: 2'))
135
-
136
- // eslint-disable-next-line cspell/spellchecker
137
- // TODO(lukemurray): in react this is 2 and in solid it is 4
138
- expect(states.length).toBe(4)
139
- expect(states[3]).toMatchObject({
140
- data: { pages: [2], pageParams: [1] },
141
- status: 'success',
142
- })
143
- })
144
-
145
- it('should not call the queryFn twice when used in Suspense mode', async () => {
146
- const key = queryKey()
147
-
148
- const queryFn = vi.fn<(...args: Array<unknown>) => string>()
149
- queryFn.mockImplementation(() => {
150
- sleep(10)
151
- return 'data'
152
- })
153
-
154
- function Page() {
155
- createQuery(() => ({ queryKey: [key], queryFn, suspense: true }))
156
-
157
- return <>rendered</>
158
- }
159
-
160
- const rendered = render(() => (
161
- <QueryClientProvider client={queryClient}>
162
- <Suspense fallback="loading">
163
- <Page />
164
- </Suspense>
165
- </QueryClientProvider>
166
- ))
167
-
168
- await waitFor(() => rendered.getByText('rendered'))
169
-
170
- expect(queryFn).toHaveBeenCalledTimes(1)
171
- })
172
-
173
- it('should remove query instance when component unmounted', async () => {
174
- const key = queryKey()
175
-
176
- function Page() {
177
- createQuery(() => ({
178
- queryKey: key,
179
- queryFn: () => {
180
- sleep(10)
181
- return 'data'
182
- },
183
- }))
184
-
185
- return <>rendered</>
186
- }
187
-
188
- function App() {
189
- const [show, setShow] = createSignal(false)
190
-
191
- return (
192
- <>
193
- <Suspense fallback="loading">{show() && <Page />}</Suspense>
194
- <button
195
- aria-label="toggle"
196
- onClick={() => setShow((prev) => !prev)}
197
- />
198
- </>
199
- )
200
- }
201
-
202
- const rendered = render(() => (
203
- <QueryClientProvider client={queryClient}>
204
- <App />
205
- </QueryClientProvider>
206
- ))
207
-
208
- expect(rendered.queryByText('rendered')).toBeNull()
209
- expect(queryCache.find({ queryKey: key })).toBeFalsy()
210
-
211
- fireEvent.click(rendered.getByLabelText('toggle'))
212
- await waitFor(() => rendered.getByText('rendered'))
213
-
214
- expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(1)
215
-
216
- fireEvent.click(rendered.getByLabelText('toggle'))
217
-
218
- expect(rendered.queryByText('rendered')).toBeNull()
219
- expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(0)
220
- })
221
-
222
- // https://github.com/tannerlinsley/react-query/issues/468
223
- it('should reset error state if new component instances are mounted', async () => {
224
- const key = queryKey()
225
-
226
- let succeed = false
227
-
228
- function Page() {
229
- const state = createQuery(() => ({
230
- queryKey: key,
231
- queryFn: async () => {
232
- await sleep(10)
233
- if (!succeed) {
234
- throw new Error('Suspense Error Bingo')
235
- } else {
236
- return 'data'
237
- }
238
- },
239
- retryDelay: 10,
240
- suspense: true,
241
- }))
242
-
243
- // Suspense only triggers if used in JSX
244
- return (
245
- <Show when={state.data}>
246
- <div>rendered</div>
247
- </Show>
248
- )
249
- }
250
-
251
- const rendered = render(() => (
252
- <QueryClientProvider client={queryClient}>
253
- <ErrorBoundary
254
- fallback={(_err, resetSolid) => (
255
- <div>
256
- <div>error boundary</div>
257
- <button
258
- onClick={() => {
259
- succeed = true
260
- resetSolid()
261
- }}
262
- >
263
- retry
264
- </button>
265
- </div>
266
- )}
267
- >
268
- <Suspense fallback={'Loading...'}>
269
- <Page />
270
- </Suspense>
271
- </ErrorBoundary>
272
- </QueryClientProvider>
273
- ))
274
-
275
- await waitFor(() => rendered.getByText('Loading...'))
276
-
277
- await waitFor(() => rendered.getByText('error boundary'))
278
-
279
- await waitFor(() => rendered.getByText('retry'))
280
-
281
- fireEvent.click(rendered.getByText('retry'))
282
-
283
- await waitFor(() => rendered.getByText('rendered'))
284
- })
285
-
286
- it('should retry fetch if the reset error boundary has been reset', async () => {
287
- const key = queryKey()
288
-
289
- let succeed = false
290
-
291
- function Page() {
292
- const state = createQuery(() => ({
293
- queryKey: key,
294
- queryFn: async () => {
295
- await sleep(10)
296
- if (!succeed) {
297
- throw new Error('Suspense Error Bingo')
298
- } else {
299
- return 'data'
300
- }
301
- },
302
- retry: false,
303
- suspense: true,
304
- }))
305
-
306
- // Suspense only triggers if used in JSX
307
- return (
308
- <Show when={state.data}>
309
- <div>rendered</div>
310
- </Show>
311
- )
312
- }
313
-
314
- const rendered = render(() => (
315
- <QueryClientProvider client={queryClient}>
316
- <ErrorBoundary
317
- fallback={(_err, resetSolid) => (
318
- <div>
319
- <div>error boundary</div>
320
- <button
321
- onClick={() => {
322
- resetSolid()
323
- }}
324
- >
325
- retry
326
- </button>
327
- </div>
328
- )}
329
- >
330
- <Suspense fallback="Loading...">
331
- <Page />
332
- </Suspense>
333
- </ErrorBoundary>
334
- </QueryClientProvider>
335
- ))
336
-
337
- await waitFor(() => rendered.getByText('Loading...'))
338
- await waitFor(() => rendered.getByText('error boundary'))
339
- await waitFor(() => rendered.getByText('retry'))
340
- fireEvent.click(rendered.getByText('retry'))
341
- await waitFor(() => rendered.getByText('error boundary'))
342
- await waitFor(() => rendered.getByText('retry'))
343
- succeed = true
344
- fireEvent.click(rendered.getByText('retry'))
345
- await waitFor(() => rendered.getByText('rendered'))
346
- })
347
-
348
- it('should refetch when re-mounting', async () => {
349
- const key = queryKey()
350
- let count = 0
351
-
352
- function Component() {
353
- const result = createQuery(() => ({
354
- queryKey: key,
355
- queryFn: async () => {
356
- await sleep(100)
357
- count++
358
- return count
359
- },
360
-
361
- retry: false,
362
- suspense: true,
363
- staleTime: 0,
364
- }))
365
- return (
366
- <div>
367
- <span>data: {result.data}</span>
368
- <span>fetching: {result.isFetching ? 'true' : 'false'}</span>
369
- </div>
370
- )
371
- }
372
-
373
- function Page() {
374
- const [show, setShow] = createSignal(true)
375
- return (
376
- <div>
377
- <button
378
- onClick={() => {
379
- setShow(!show())
380
- }}
381
- >
382
- {show() ? 'hide' : 'show'}
383
- </button>
384
- <Suspense fallback="Loading...">{show() && <Component />}</Suspense>
385
- </div>
386
- )
387
- }
388
-
389
- const rendered = render(() => (
390
- <QueryClientProvider client={queryClient}>
391
- <Page />
392
- </QueryClientProvider>
393
- ))
394
-
395
- await waitFor(() => rendered.getByText('Loading...'))
396
- await waitFor(() => rendered.getByText('data: 1'))
397
- await waitFor(() => rendered.getByText('fetching: false'))
398
- await waitFor(() => rendered.getByText('hide'))
399
- fireEvent.click(rendered.getByText('hide'))
400
- await waitFor(() => rendered.getByText('show'))
401
- fireEvent.click(rendered.getByText('show'))
402
- await waitFor(() => rendered.getByText('fetching: true'))
403
- await waitFor(() => rendered.getByText('data: 2'))
404
- await waitFor(() => rendered.getByText('fetching: false'))
405
- })
406
-
407
- it('should suspend when switching to a new query', async () => {
408
- const key1 = queryKey()
409
- const key2 = queryKey()
410
-
411
- function Component(props: { queryKey: Array<string> }) {
412
- const result = createQuery(() => ({
413
- queryKey: props.queryKey,
414
- queryFn: async () => {
415
- await sleep(100)
416
- return props.queryKey
417
- },
418
- retry: false,
419
- suspense: true,
420
- }))
421
- return <div>data: {result.data}</div>
422
- }
423
-
424
- function Page() {
425
- const [key, setKey] = createSignal(key1)
426
- return (
427
- <div>
428
- <button
429
- onClick={() => {
430
- setKey(key2)
431
- }}
432
- >
433
- switch
434
- </button>
435
- <Suspense fallback="Loading...">
436
- <Component queryKey={key()} />
437
- </Suspense>
438
- </div>
439
- )
440
- }
441
-
442
- const rendered = render(() => (
443
- <QueryClientProvider client={queryClient}>
444
- <Page />
445
- </QueryClientProvider>
446
- ))
447
-
448
- await waitFor(() => rendered.getByText('Loading...'))
449
- await waitFor(() => rendered.getByText(`data: ${key1}`))
450
- fireEvent.click(rendered.getByText('switch'))
451
- await waitFor(() => rendered.getByText('Loading...'))
452
- await waitFor(() => rendered.getByText(`data: ${key2}`))
453
- })
454
-
455
- it('should throw errors to the error boundary by default', async () => {
456
- const key = queryKey()
457
-
458
- const consoleMock = vi
459
- .spyOn(console, 'error')
460
- .mockImplementation(() => undefined)
461
-
462
- function Page() {
463
- const state = createQuery(() => ({
464
- queryKey: key,
465
- queryFn: async (): Promise<unknown> => {
466
- await sleep(10)
467
- throw new Error('Suspense Error a1x')
468
- },
469
- retry: false,
470
- suspense: true,
471
- }))
472
-
473
- // read state.data to trigger suspense.
474
- createRenderEffect(() => {
475
- state.data
476
- })
477
-
478
- return <div>rendered</div>
479
- }
480
-
481
- function App() {
482
- return (
483
- <ErrorBoundary
484
- fallback={() => (
485
- <div>
486
- <div>error boundary</div>
487
- </div>
488
- )}
489
- >
490
- <Suspense fallback="Loading...">
491
- <Page />
492
- </Suspense>
493
- </ErrorBoundary>
494
- )
495
- }
496
-
497
- const rendered = render(() => (
498
- <QueryClientProvider client={queryClient}>
499
- <App />
500
- </QueryClientProvider>
501
- ))
502
-
503
- await waitFor(() => rendered.getByText('Loading...'))
504
- await waitFor(() => rendered.getByText('error boundary'))
505
-
506
- consoleMock.mockRestore()
507
- })
508
-
509
- it('should not throw errors to the error boundary when throwOnError: false', async () => {
510
- const key = queryKey()
511
-
512
- function Page() {
513
- const state = createQuery(() => ({
514
- queryKey: key,
515
- queryFn: async (): Promise<unknown> => {
516
- await sleep(10)
517
- throw new Error('Suspense Error a2x')
518
- },
519
- retry: false,
520
- throwOnError: false,
521
- }))
522
-
523
- // read state.data to trigger suspense.
524
- createRenderEffect(() => {
525
- state.data
526
- })
527
-
528
- return <div>rendered</div>
529
- }
530
-
531
- function App() {
532
- return (
533
- <ErrorBoundary
534
- fallback={() => (
535
- <div>
536
- <div>error boundary</div>
537
- </div>
538
- )}
539
- >
540
- <Suspense fallback="Loading...">
541
- <Page />
542
- </Suspense>
543
- </ErrorBoundary>
544
- )
545
- }
546
-
547
- const rendered = render(() => (
548
- <QueryClientProvider client={queryClient}>
549
- <App />
550
- </QueryClientProvider>
551
- ))
552
-
553
- await waitFor(() => rendered.getByText('Loading...'))
554
- await waitFor(() => rendered.getByText('rendered'))
555
- })
556
-
557
- it('should throw errors to the error boundary when a throwOnError function returns true', async () => {
558
- const key = queryKey()
559
-
560
- const consoleMock = vi
561
- .spyOn(console, 'error')
562
- .mockImplementation(() => undefined)
563
-
564
- function Page() {
565
- const state = createQuery(() => ({
566
- queryKey: key,
567
- queryFn: async (): Promise<unknown> => {
568
- await sleep(10)
569
- return Promise.reject(new Error('Remote Error'))
570
- },
571
- retry: false,
572
- throwOnError: (err) => err.message !== 'Local Error',
573
- }))
574
-
575
- // read state.data to trigger suspense.
576
- createRenderEffect(() => {
577
- state.data
578
- })
579
-
580
- return <div>rendered</div>
581
- }
582
-
583
- function App() {
584
- return (
585
- <ErrorBoundary
586
- fallback={() => (
587
- <div>
588
- <div>error boundary</div>
589
- </div>
590
- )}
591
- >
592
- <Suspense fallback="Loading...">
593
- <Page />
594
- </Suspense>
595
- </ErrorBoundary>
596
- )
597
- }
598
-
599
- const rendered = render(() => (
600
- <QueryClientProvider client={queryClient}>
601
- <App />
602
- </QueryClientProvider>
603
- ))
604
-
605
- await waitFor(() => rendered.getByText('Loading...'))
606
- await waitFor(() => rendered.getByText('error boundary'))
607
-
608
- consoleMock.mockRestore()
609
- })
610
-
611
- it('should not throw errors to the error boundary when a throwOnError function returns false', async () => {
612
- const key = queryKey()
613
-
614
- function Page() {
615
- const state = createQuery(() => ({
616
- queryKey: key,
617
- queryFn: async (): Promise<unknown> => {
618
- await sleep(10)
619
- return Promise.reject(new Error('Local Error'))
620
- },
621
-
622
- retry: false,
623
- suspense: true,
624
- throwOnError: (err) => err.message !== 'Local Error',
625
- }))
626
-
627
- // read state.data to trigger suspense.
628
- createRenderEffect(() => {
629
- state.data
630
- })
631
-
632
- return <div>rendered</div>
633
- }
634
-
635
- function App() {
636
- return (
637
- <ErrorBoundary
638
- fallback={() => (
639
- <div>
640
- <div>error boundary</div>
641
- </div>
642
- )}
643
- >
644
- <Suspense fallback="Loading...">
645
- <Page />
646
- </Suspense>
647
- </ErrorBoundary>
648
- )
649
- }
650
-
651
- const rendered = render(() => (
652
- <QueryClientProvider client={queryClient}>
653
- <App />
654
- </QueryClientProvider>
655
- ))
656
-
657
- await waitFor(() => rendered.getByText('Loading...'))
658
- await waitFor(() => rendered.getByText('rendered'))
659
- })
660
-
661
- it('should not call the queryFn when not enabled', async () => {
662
- const key = queryKey()
663
-
664
- const queryFn = vi.fn<(...args: Array<unknown>) => Promise<string>>()
665
- queryFn.mockImplementation(async () => {
666
- await sleep(10)
667
- return '23'
668
- })
669
-
670
- function Page() {
671
- const [enabled, setEnabled] = createSignal(false)
672
- const result = createQuery(() => ({
673
- queryKey: [key],
674
- queryFn,
675
- suspense: true,
676
- enabled: enabled(),
677
- }))
678
-
679
- return (
680
- <div>
681
- <button onClick={() => setEnabled(true)}>fire</button>
682
- <h1>{result.data}</h1>
683
- </div>
684
- )
685
- }
686
-
687
- const rendered = render(() => (
688
- <QueryClientProvider client={queryClient}>
689
- <Suspense fallback="loading">
690
- <Page />
691
- </Suspense>
692
- </QueryClientProvider>
693
- ))
694
-
695
- expect(queryFn).toHaveBeenCalledTimes(0)
696
- await sleep(5)
697
- fireEvent.click(rendered.getByRole('button', { name: /fire/i }))
698
-
699
- await waitFor(() => {
700
- expect(rendered.getByRole('heading').textContent).toBe('23')
701
- })
702
-
703
- expect(queryFn).toHaveBeenCalledTimes(1)
704
- })
705
-
706
- it('should error caught in error boundary without infinite loop', async () => {
707
- const key = queryKey()
708
-
709
- const consoleMock = vi
710
- .spyOn(console, 'error')
711
- .mockImplementation(() => undefined)
712
-
713
- let succeed = true
714
-
715
- function Page() {
716
- const [nonce] = createSignal(0)
717
- const queryKeys = [`${key}-${succeed}`]
718
- const result = createQuery(() => ({
719
- queryKey: queryKeys,
720
- queryFn: async () => {
721
- await sleep(10)
722
- if (!succeed) {
723
- throw new Error('Suspense Error Bingo')
724
- } else {
725
- return nonce()
726
- }
727
- },
728
- retry: false,
729
- suspense: true,
730
- }))
731
- return (
732
- <div>
733
- <span>rendered</span> <span>{result.data}</span>
734
- <button
735
- aria-label="fail"
736
- onClick={async () => {
737
- await queryClient.resetQueries()
738
- }}
739
- >
740
- fail
741
- </button>
742
- </div>
743
- )
744
- }
745
-
746
- function App() {
747
- return (
748
- <ErrorBoundary fallback={() => <div>error boundary</div>}>
749
- <Suspense fallback="Loading...">
750
- <Page />
751
- </Suspense>
752
- </ErrorBoundary>
753
- )
754
- }
755
-
756
- const rendered = render(() => (
757
- <QueryClientProvider client={queryClient}>
758
- <App />
759
- </QueryClientProvider>
760
- ))
761
-
762
- // render suspense fallback (Loading...)
763
- await waitFor(() => rendered.getByText('Loading...'))
764
- // resolve promise -> render Page (rendered)
765
- await waitFor(() => rendered.getByText('rendered'))
766
-
767
- // change query key
768
- succeed = false
769
- // reset query -> and throw error
770
- fireEvent.click(rendered.getByLabelText('fail'))
771
- // render error boundary fallback (error boundary)
772
- await waitFor(() => rendered.getByText('error boundary'))
773
-
774
- consoleMock.mockRestore()
775
- })
776
-
777
- it('should error caught in error boundary without infinite loop when query keys changed', async () => {
778
- let succeed = true
779
-
780
- const consoleMock = vi
781
- .spyOn(console, 'error')
782
- .mockImplementation(() => undefined)
783
-
784
- function Page() {
785
- const [key, setKey] = createSignal(0)
786
-
787
- const result = createQuery(() => ({
788
- queryKey: [`${key()}-${succeed}`],
789
- queryFn: async () => {
790
- await sleep(10)
791
- if (!succeed) {
792
- throw new Error('Suspense Error Bingo')
793
- } else {
794
- return 'data'
795
- }
796
- },
797
- retry: false,
798
- suspense: true,
799
- }))
800
- return (
801
- <div>
802
- <span>rendered</span> <span>{result.data}</span>
803
- <button aria-label="fail" onClick={() => setKey((k) => k + 1)}>
804
- fail
805
- </button>
806
- </div>
807
- )
808
- }
809
-
810
- function App() {
811
- return (
812
- <ErrorBoundary fallback={() => <div>error boundary</div>}>
813
- <Suspense fallback="Loading...">
814
- <Page />
815
- </Suspense>
816
- </ErrorBoundary>
817
- )
818
- }
819
-
820
- const rendered = render(() => (
821
- <QueryClientProvider client={queryClient}>
822
- <App />
823
- </QueryClientProvider>
824
- ))
825
-
826
- // render suspense fallback (Loading...)
827
- await waitFor(() => rendered.getByText('Loading...'))
828
- // resolve promise -> render Page (rendered)
829
- await waitFor(() => rendered.getByText('rendered'))
830
-
831
- // change promise result to error
832
- succeed = false
833
- // change query key
834
- fireEvent.click(rendered.getByLabelText('fail'))
835
- // render error boundary fallback (error boundary)
836
- await waitFor(() => rendered.getByText('error boundary'))
837
-
838
- consoleMock.mockRestore()
839
- })
840
-
841
- it('should error caught in error boundary without infinite loop when enabled changed', async () => {
842
- const consoleMock = vi
843
- .spyOn(console, 'error')
844
- .mockImplementation(() => undefined)
845
-
846
- function Page() {
847
- const queryKeys = '1'
848
- const [enabled, setEnabled] = createSignal(false)
849
-
850
- const result = createQuery<string>(() => ({
851
- queryKey: [queryKeys],
852
- queryFn: async () => {
853
- await sleep(10)
854
- throw new Error('Suspense Error Bingo')
855
- },
856
-
857
- retry: false,
858
- suspense: true,
859
- enabled: enabled(),
860
- }))
861
- return (
862
- <div>
863
- <span>rendered</span> <span>{result.data}</span>
864
- <button
865
- aria-label="fail"
866
- onClick={() => {
867
- setEnabled(true)
868
- }}
869
- >
870
- fail
871
- </button>
872
- </div>
873
- )
874
- }
875
-
876
- function App() {
877
- return (
878
- <ErrorBoundary fallback={() => <div>error boundary</div>}>
879
- <Suspense fallback="Loading...">
880
- <Page />
881
- </Suspense>
882
- </ErrorBoundary>
883
- )
884
- }
885
-
886
- const rendered = render(() => (
887
- <QueryClientProvider client={queryClient}>
888
- <App />
889
- </QueryClientProvider>
890
- ))
891
-
892
- // render empty data with 'rendered' when enabled is false
893
- await waitFor(() => rendered.getByText('rendered'))
894
-
895
- // change enabled to true
896
- fireEvent.click(rendered.getByLabelText('fail'))
897
-
898
- // render pending fallback
899
- await waitFor(() => rendered.getByText('Loading...'))
900
-
901
- // render error boundary fallback (error boundary)
902
- await waitFor(() => rendered.getByText('error boundary'))
903
-
904
- consoleMock.mockRestore()
905
- })
906
-
907
- it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => {
908
- const key = queryKey()
909
- let state: CreateQueryResult<number> | null = null
910
-
911
- let count = 0
912
- let renders = 0
913
-
914
- function Page() {
915
- state = createQuery(() => ({
916
- queryKey: key,
917
- queryFn: async () => {
918
- count++
919
- await sleep(10)
920
- return count
921
- },
922
- gcTime: 0,
923
- }))
924
-
925
- createRenderEffect(
926
- on([() => ({ ...state })], () => {
927
- renders++
928
- }),
929
- )
930
-
931
- return (
932
- <div>
933
- <span>rendered</span>
934
- </div>
935
- )
936
- }
937
-
938
- const rendered = render(() => (
939
- <QueryClientProvider client={queryClient}>
940
- <Suspense fallback="loading">
941
- <Page />
942
- </Suspense>
943
- </QueryClientProvider>
944
- ))
945
-
946
- await waitFor(() =>
947
- expect(state).toMatchObject({
948
- data: 1,
949
- status: 'success',
950
- }),
951
- )
952
-
953
- expect(renders).toBe(2)
954
- expect(rendered.queryByText('rendered')).not.toBeNull()
955
- })
956
- })