Skip to content

Commit b44b34d

Browse files
committed
Add test for subscription serialization
1 parent 407688d commit b44b34d

File tree

1 file changed

+117
-38
lines changed

1 file changed

+117
-38
lines changed

packages/toolkit/src/query/tests/buildMiddleware.test.tsx

Lines changed: 117 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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
})
3638
const { 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-
100103
it.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

Comments
 (0)