@@ -3,7 +3,7 @@ import { createApi, fetchBaseQuery, QueryStatus } from '@rtk-incubator/rtk-query
33import { act , fireEvent , render , screen , waitFor } from '@testing-library/react' ;
44import userEvent from '@testing-library/user-event' ;
55import { rest } from 'msw' ;
6- import { setupApiStore , waitMs } from './helpers' ;
6+ import { setupApiStore , useRenderCounter , waitMs } from './helpers' ;
77import { server } from './mocks/server' ;
88import { AnyAction } from 'redux' ;
99import { SubscriptionOptions } from '@internal/core/apiState' ;
@@ -54,8 +54,31 @@ afterEach(() => {
5454
5555describe ( 'hooks tests' , ( ) => {
5656 describe ( 'useQuery' , ( ) => {
57+ let getRenderCount : ( ) => number = ( ) => 0 ;
58+
59+ test ( 'useQuery hook basic render count assumptions' , async ( ) => {
60+ function User ( ) {
61+ getRenderCount = useRenderCounter ( ) ;
62+
63+ const { isFetching } = api . endpoints . getUser . useQuery ( 1 ) ;
64+
65+ return (
66+ < div >
67+ < div data-testid = "isFetching" > { String ( isFetching ) } </ div >
68+ </ div >
69+ ) ;
70+ }
71+
72+ render ( < User /> , { wrapper : storeRef . wrapper } ) ;
73+ expect ( getRenderCount ( ) ) . toBe ( 2 ) ; // By the time this runs, the initial render will happen, and the query will start immediately running by the time we can expect this
74+
75+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
76+ expect ( getRenderCount ( ) ) . toBe ( 3 ) ;
77+ } ) ;
78+
5779 test ( 'useQuery hook sets isFetching=true whenever a request is in flight' , async ( ) => {
5880 function User ( ) {
81+ getRenderCount = useRenderCounter ( ) ;
5982 const [ value , setValue ] = React . useState ( 0 ) ;
6083
6184 const { isFetching } = api . endpoints . getUser . useQuery ( 1 , { skip : value < 1 } ) ;
@@ -69,14 +92,18 @@ describe('hooks tests', () => {
6992 }
7093
7194 render ( < User /> , { wrapper : storeRef . wrapper } ) ;
95+ expect ( getRenderCount ( ) ) . toBe ( 1 ) ;
7296
7397 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
74- fireEvent . click ( screen . getByText ( 'Increment value' ) ) ;
98+ fireEvent . click ( screen . getByText ( 'Increment value' ) ) ; // setState = 1, perform request = 2
7599 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ) ;
76100 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
101+ expect ( getRenderCount ( ) ) . toBe ( 4 ) ;
102+
77103 fireEvent . click ( screen . getByText ( 'Increment value' ) ) ;
78104 // Being that nothing has changed in the args, this should never fire.
79105 expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
106+ expect ( getRenderCount ( ) ) . toBe ( 5 ) ; // even though there was no request, the button click updates the state so this is an expected render
80107 } ) ;
81108
82109 test ( 'useQuery hook sets isLoading=true only on initial request' , async ( ) => {
@@ -114,6 +141,7 @@ describe('hooks tests', () => {
114141 let refetchMe : ( ) => void = ( ) => { } ;
115142 function User ( ) {
116143 const [ value , setValue ] = React . useState ( 0 ) ;
144+ getRenderCount = useRenderCounter ( ) ;
117145
118146 const { isLoading, isFetching, refetch } = api . endpoints . getUser . useQuery ( 22 , { skip : value < 1 } ) ;
119147 refetchMe = refetch ;
@@ -127,33 +155,33 @@ describe('hooks tests', () => {
127155 }
128156
129157 render ( < User /> , { wrapper : storeRef . wrapper } ) ;
158+ expect ( getRenderCount ( ) ) . toBe ( 1 ) ;
130159
131- await waitFor ( ( ) => {
132- expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
133- expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
134- } ) ;
135- fireEvent . click ( screen . getByText ( 'Increment value' ) ) ;
160+ expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
161+ expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
162+
163+ fireEvent . click ( screen . getByText ( 'Increment value' ) ) ; // renders: set state = 1, perform request = 2
136164 // Condition is met, should load
137165 await waitFor ( ( ) => {
138166 expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'true' ) ;
139167 expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ;
140168 } ) ;
169+
141170 // Make sure the request is done for sure.
142171 await waitFor ( ( ) => {
143172 expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
144173 expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
145174 } ) ;
175+ expect ( getRenderCount ( ) ) . toBe ( 4 ) ;
176+
146177 fireEvent . click ( screen . getByText ( 'Increment value' ) ) ;
147- // Being that we already have data, isLoading should be false
148- await waitFor ( ( ) => {
149- expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
150- expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
151- } ) ;
152- // Make sure the request is done for sure.
178+ // Being that we already have data and changing the value doesn't trigger a new request, only the button click should impact the render
153179 await waitFor ( ( ) => {
154180 expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
155181 expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
156182 } ) ;
183+ expect ( getRenderCount ( ) ) . toBe ( 5 ) ;
184+
157185 // We call a refetch, should set both to true, then false when complete/errored
158186 act ( ( ) => refetchMe ( ) ) ;
159187 await waitFor ( ( ) => {
@@ -164,6 +192,7 @@ describe('hooks tests', () => {
164192 expect ( screen . getByTestId ( 'isLoading' ) . textContent ) . toBe ( 'false' ) ;
165193 expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ;
166194 } ) ;
195+ expect ( getRenderCount ( ) ) . toBe ( 7 ) ;
167196 } ) ;
168197
169198 test ( 'useQuery hook respects refetchOnMountOrArgChange: true' , async ( ) => {
@@ -372,10 +401,11 @@ describe('hooks tests', () => {
372401 data = undefined ;
373402 } ) ;
374403
404+ let getRenderCount : ( ) => number = ( ) => 0 ;
375405 test ( 'useLazyQuery does not automatically fetch when mounted and has undefined data' , async ( ) => {
376406 function User ( ) {
377407 const [ fetchUser , { data : hookData , isFetching, isUninitialized } ] = api . endpoints . getUser . useLazyQuery ( ) ;
378-
408+ getRenderCount = useRenderCounter ( ) ;
379409 data = hookData ;
380410
381411 return (
@@ -391,17 +421,22 @@ describe('hooks tests', () => {
391421 }
392422
393423 render ( < User /> , { wrapper : storeRef . wrapper } ) ;
424+ expect ( getRenderCount ( ) ) . toBe ( 1 ) ;
394425
395426 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isUninitialized' ) . textContent ) . toBe ( 'true' ) ) ;
396427 await waitFor ( ( ) => expect ( data ) . toBeUndefined ( ) ) ;
397428
398429 fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
430+ expect ( getRenderCount ( ) ) . toBe ( 2 ) ;
399431
400- await waitFor ( ( ) => {
401- expect ( screen . getByTestId ( 'isUninitialized' ) . textContent ) . toBe ( 'false' ) ;
402- expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ;
403- } ) ;
432+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'isUninitialized' ) . textContent ) . toBe ( 'false' ) ) ;
404433 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
434+ expect ( getRenderCount ( ) ) . toBe ( 3 ) ;
435+
436+ fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
437+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ) ;
438+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
439+ expect ( getRenderCount ( ) ) . toBe ( 5 ) ;
405440 } ) ;
406441
407442 test ( 'useLazyQuery accepts updated subscription options and only dispatches updateSubscriptionOptions when values are updated' , async ( ) => {
@@ -411,6 +446,7 @@ describe('hooks tests', () => {
411446 const [ fetchUser , { data : hookData , isFetching, isUninitialized } ] = api . endpoints . getUser . useLazyQuery (
412447 options
413448 ) ;
449+ getRenderCount = useRenderCounter ( ) ;
414450
415451 data = hookData ;
416452
@@ -437,30 +473,35 @@ describe('hooks tests', () => {
437473 }
438474
439475 render ( < User /> , { wrapper : storeRef . wrapper } ) ;
476+ expect ( getRenderCount ( ) ) . toBe ( 1 ) ; // hook mount
440477
441478 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isUninitialized' ) . textContent ) . toBe ( 'true' ) ) ;
442479 await waitFor ( ( ) => expect ( data ) . toBeUndefined ( ) ) ;
443480
444481 fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
482+ expect ( getRenderCount ( ) ) . toBe ( 2 ) ;
445483
446484 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ) ;
447485 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
486+ expect ( getRenderCount ( ) ) . toBe ( 3 ) ;
448487
449- fireEvent . click ( screen . getByTestId ( 'updateOptions' ) ) ;
450- fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
488+ fireEvent . click ( screen . getByTestId ( 'updateOptions' ) ) ; // setState = 1
489+ expect ( getRenderCount ( ) ) . toBe ( 4 ) ;
451490
491+ fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ; // perform new request = 2
452492 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ) ;
453493 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
494+ expect ( getRenderCount ( ) ) . toBe ( 6 ) ;
454495
455496 interval = 1000 ;
456497
457- fireEvent . click ( screen . getByTestId ( 'updateOptions' ) ) ;
458- fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
498+ fireEvent . click ( screen . getByTestId ( 'updateOptions' ) ) ; // setState = 1
499+ expect ( getRenderCount ( ) ) . toBe ( 7 ) ;
459500
460- await waitFor ( ( ) => {
461- expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ;
462- } ) ;
501+ fireEvent . click ( screen . getByTestId ( 'fetchButton' ) ) ;
502+ await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'true' ) ) ;
463503 await waitFor ( ( ) => expect ( screen . getByTestId ( 'isFetching' ) . textContent ) . toBe ( 'false' ) ) ;
504+ expect ( getRenderCount ( ) ) . toBe ( 9 ) ;
464505
465506 expect (
466507 storeRef . store . getState ( ) . actions . filter ( api . internalActions . updateSubscriptionOptions . match )
@@ -534,7 +575,7 @@ describe('hooks tests', () => {
534575 } ) ;
535576
536577 describe ( 'useMutation' , ( ) => {
537- test ( 'useMutation hook sets and unsets the ` isLoading` flag when running' , async ( ) => {
578+ test ( 'useMutation hook sets and unsets the isLoading flag when running' , async ( ) => {
538579 function User ( ) {
539580 const [ updateUser , { isLoading } ] = api . endpoints . updateUser . useMutation ( ) ;
540581
@@ -1043,6 +1084,7 @@ describe('hooks with createApi defaults set', () => {
10431084 </ div > ,
10441085 { wrapper : storeRef . wrapper }
10451086 ) ;
1087+
10461088 expect ( screen . getByTestId ( 'renderCount' ) . textContent ) . toBe ( '1' ) ;
10471089
10481090 const addBtn = screen . getByTestId ( 'addPost' ) ;
0 commit comments