@@ -26,11 +26,13 @@ const api = createApi({
2626 providesTags : [ 'Bread' ] ,
2727 } ) ,
2828 invalidateFruit : build . mutation ( {
29- query : ( fruit ?: 'Banana' | 'Bread' | null ) => ( { url : `invalidate/fruit/${ fruit || '' } ` } ) ,
29+ query : ( fruit ?: 'Banana' | 'Bread' | null ) => ( {
30+ url : `invalidate/fruit/${ fruit || '' } ` ,
31+ } ) ,
3032 invalidatesTags ( result , error , arg ) {
3133 return [ arg ]
32- }
33- } )
34+ } ,
35+ } ) ,
3436 } ) ,
3537} )
3638const { getBanana, getBread, invalidateFruit } = api . endpoints
@@ -77,9 +79,11 @@ it('invalidates the specified tags', async () => {
7779 )
7880} )
7981
80- it ( 'invalidates tags correctly when null or undefined are provided as tags' , async ( ) => {
82+ it ( 'invalidates tags correctly when null or undefined are provided as tags' , async ( ) => {
8183 await storeRef . store . dispatch ( getBanana . initiate ( 1 ) )
82- await storeRef . store . dispatch ( api . util . invalidateTags ( [ undefined , null , 'Banana' ] ) )
84+ await storeRef . store . dispatch (
85+ api . util . invalidateTags ( [ undefined , null , 'Banana' ] ) ,
86+ )
8387
8488 // Slight pause to let the middleware run and such
8589 await delay ( 20 )
@@ -96,41 +100,116 @@ it('invalidates tags correctly when null or undefined are provided as tags', asy
96100 expect ( storeRef . store . getState ( ) . actions ) . toMatchSequence ( ...apiActions )
97101} )
98102
99-
100103it . each ( [
101- { tags : [ undefined , null , 'Bread' ] as Parameters < typeof api . util . invalidateTags > [ '0' ] } ,
102- { tags : [ undefined , null ] , } , { tags : [ ] } ]
103- ) ( 'does not invalidate with tags=$tags if no query matches' , async ( { tags } ) => {
104- await storeRef . store . dispatch ( getBanana . initiate ( 1 ) )
105- await storeRef . store . dispatch ( api . util . invalidateTags ( tags ) )
104+ {
105+ tags : [ undefined , null , 'Bread' ] as Parameters <
106+ typeof api . util . invalidateTags
107+ > [ '0' ] ,
108+ } ,
109+ { tags : [ undefined , null ] } ,
110+ { tags : [ ] } ,
111+ ] ) (
112+ 'does not invalidate with tags=$tags if no query matches' ,
113+ async ( { tags } ) => {
114+ await storeRef . store . dispatch ( getBanana . initiate ( 1 ) )
115+ await storeRef . store . dispatch ( api . util . invalidateTags ( tags ) )
116+
117+ // Slight pause to let the middleware run and such
118+ await delay ( 20 )
119+
120+ const apiActions = [
121+ api . internalActions . middlewareRegistered . match ,
122+ getBanana . matchPending ,
123+ getBanana . matchFulfilled ,
124+ api . util . invalidateTags . match ,
125+ ]
126+
127+ expect ( storeRef . store . getState ( ) . actions ) . toMatchSequence ( ...apiActions )
128+ } ,
129+ )
106130
107- // Slight pause to let the middleware run and such
108- await delay ( 20 )
131+ it . each ( [
132+ { mutationArg : 'Bread' as 'Bread' | null | undefined } ,
133+ { mutationArg : undefined } ,
134+ { mutationArg : null } ,
135+ ] ) (
136+ 'does not invalidate queries when a mutation with tags=[$mutationArg] runs and does not match anything' ,
137+ async ( { mutationArg } ) => {
138+ await storeRef . store . dispatch ( getBanana . initiate ( 1 ) )
139+ await storeRef . store . dispatch ( invalidateFruit . initiate ( mutationArg ) )
140+
141+ // Slight pause to let the middleware run and such
142+ await delay ( 20 )
143+
144+ const apiActions = [
145+ api . internalActions . middlewareRegistered . match ,
146+ getBanana . matchPending ,
147+ getBanana . matchFulfilled ,
148+ invalidateFruit . matchPending ,
149+ invalidateFruit . matchFulfilled ,
150+ ]
151+
152+ expect ( storeRef . store . getState ( ) . actions ) . toMatchSequence ( ...apiActions )
153+ } ,
154+ )
155+
156+ it ( 'correctly stringifies subscription state and dispatches subscriptionsUpdated' , async ( ) => {
157+ // Create a fresh store for this test to avoid interference
158+ const testStoreRef = setupApiStore (
159+ api ,
160+ {
161+ ...actionsReducer ,
162+ } ,
163+ { withoutListeners : true } ,
164+ )
109165
110- const apiActions = [
111- api . internalActions . middlewareRegistered . match ,
112- getBanana . matchPending ,
113- getBanana . matchFulfilled ,
114- api . util . invalidateTags . match ,
115- ]
166+ // Start multiple subscriptions
167+ const subscription1 = testStoreRef . store . dispatch (
168+ getBanana . initiate ( 1 , {
169+ subscriptionOptions : { pollingInterval : 1000 } ,
170+ } ) ,
171+ )
172+ const subscription2 = testStoreRef . store . dispatch (
173+ getBanana . initiate ( 2 , {
174+ subscriptionOptions : { refetchOnFocus : true } ,
175+ } ) ,
176+ )
177+ const subscription3 = testStoreRef . store . dispatch (
178+ api . endpoints . getBananas . initiate ( ) ,
179+ )
116180
117- expect ( storeRef . store . getState ( ) . actions ) . toMatchSequence ( ...apiActions )
181+ // Wait for the subscriptions to be established
182+ await Promise . all ( [ subscription1 , subscription2 , subscription3 ] )
183+
184+ // Wait for the subscription sync timer (500ms + buffer)
185+ await delay ( 600 )
186+
187+ // Check the final subscription state in the store
188+ const finalState = testStoreRef . store . getState ( )
189+ const subscriptionState = finalState [ api . reducerPath ] . subscriptions
190+
191+ // Should have subscriptions for getBanana(1), getBanana(2), and getBananas()
192+ expect ( subscriptionState ) . toMatchObject ( {
193+ 'getBanana(1)' : {
194+ [ subscription1 . requestId ] : { pollingInterval : 1000 } ,
195+ } ,
196+ 'getBanana(2)' : {
197+ [ subscription2 . requestId ] : { refetchOnFocus : true } ,
198+ } ,
199+ 'getBananas(undefined)' : {
200+ [ subscription3 . requestId ] : { } ,
201+ } ,
202+ } )
203+
204+ // Verify the subscription entries have the expected structure
205+ expect ( Object . keys ( subscriptionState ) ) . toHaveLength ( 3 )
206+ expect ( subscriptionState [ 'getBanana(1)' ] ?. [ subscription1 . requestId ] ) . toEqual ( {
207+ pollingInterval : 1000 ,
208+ } )
209+ expect ( subscriptionState [ 'getBanana(2)' ] ?. [ subscription2 . requestId ] ) . toEqual ( {
210+ refetchOnFocus : true ,
211+ } )
212+ expect (
213+ subscriptionState [ 'getBananas(undefined)' ] ?. [ subscription3 . requestId ] ,
214+ ) . toEqual ( { } )
118215} )
119-
120- it . each ( [ { mutationArg : 'Bread' as "Bread" | null | undefined } , { mutationArg : undefined } , { mutationArg : null } ] ) ( 'does not invalidate queries when a mutation with tags=[$mutationArg] runs and does not match anything' , async ( { mutationArg } ) => {
121- await storeRef . store . dispatch ( getBanana . initiate ( 1 ) )
122- await storeRef . store . dispatch ( invalidateFruit . initiate ( mutationArg ) )
123-
124- // Slight pause to let the middleware run and such
125- await delay ( 20 )
126-
127- const apiActions = [
128- api . internalActions . middlewareRegistered . match ,
129- getBanana . matchPending ,
130- getBanana . matchFulfilled ,
131- invalidateFruit . matchPending ,
132- invalidateFruit . matchFulfilled ,
133- ]
134-
135- expect ( storeRef . store . getState ( ) . actions ) . toMatchSequence ( ...apiActions )
136- } )
0 commit comments