source: node_modules/@reduxjs/toolkit/src/query/tests/buildInitiate.test.tsx

Last change on this file was a762898, checked in by istevanoska <ilinastevanoska@…>, 5 months ago

Added visualizations

  • Property mode set to 100644
File size: 9.6 KB
RevLine 
[a762898]1import { setupApiStore } from '@internal/tests/utils/helpers'
2import { createApi } from '../core'
3import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
4import { fakeBaseQuery } from '../fakeBaseQuery'
5import { delay } from '@internal/utils'
6
7let calls = 0
8const api = createApi({
9 baseQuery: fakeBaseQuery(),
10 endpoints: (build) => ({
11 increment: build.query<number, void>({
12 async queryFn() {
13 const data = calls++
14 await Promise.resolve()
15 return { data }
16 },
17 }),
18 incrementKeep0: build.query<number, void>({
19 async queryFn() {
20 const data = calls++
21 await delay(10)
22 return { data }
23 },
24 keepUnusedDataFor: 0,
25 }),
26 failing: build.query<void, void>({
27 async queryFn() {
28 await Promise.resolve()
29 return { error: { status: 500, data: 'error' } }
30 },
31 }),
32 }),
33})
34
35const storeRef = setupApiStore(api)
36
37let getSubscriptions: SubscriptionSelectors['getSubscriptions']
38let isRequestSubscribed: SubscriptionSelectors['isRequestSubscribed']
39
40beforeEach(() => {
41 ;({ getSubscriptions, isRequestSubscribed } = storeRef.store.dispatch(
42 api.internalActions.internal_getRTKQSubscriptions(),
43 ) as unknown as SubscriptionSelectors)
44})
45
46test('multiple synchonrous initiate calls with pre-existing cache entry', async () => {
47 const { store, api } = storeRef
48 // seed the store
49 const firstValue = await store.dispatch(api.endpoints.increment.initiate())
50
51 expect(firstValue).toMatchObject({ data: 0, status: 'fulfilled' })
52
53 // dispatch another increment
54 const secondValuePromise = store.dispatch(api.endpoints.increment.initiate())
55 // and one with a forced refresh
56 const thirdValuePromise = store.dispatch(
57 api.endpoints.increment.initiate(undefined, { forceRefetch: true }),
58 )
59 // and another increment
60 const fourthValuePromise = store.dispatch(api.endpoints.increment.initiate())
61
62 const secondValue = await secondValuePromise
63 const thirdValue = await thirdValuePromise
64 const fourthValue = await fourthValuePromise
65
66 expect(secondValue).toMatchObject({
67 data: firstValue.data,
68 status: 'fulfilled',
69 requestId: firstValue.requestId,
70 })
71
72 expect(thirdValue).toMatchObject({ data: 1, status: 'fulfilled' })
73 expect(thirdValue.requestId).not.toBe(firstValue.requestId)
74 expect(fourthValue).toMatchObject({
75 data: thirdValue.data,
76 status: 'fulfilled',
77 requestId: thirdValue.requestId,
78 })
79})
80
81describe('calling initiate without a cache entry, with subscribe: false still returns correct values', () => {
82 test('successful query', async () => {
83 const { store, api } = storeRef
84 calls = 0
85 const promise = store.dispatch(
86 api.endpoints.increment.initiate(undefined, { subscribe: false }),
87 )
88 expect(isRequestSubscribed('increment(undefined)', promise.requestId)).toBe(
89 false,
90 )
91
92 await expect(promise).resolves.toMatchObject({
93 data: 0,
94 status: 'fulfilled',
95 })
96 })
97
98 test('successful query with keepUnusedDataFor: 0', async () => {
99 const { store, api } = storeRef
100 calls = 0
101 const promise = store.dispatch(
102 api.endpoints.incrementKeep0.initiate(undefined, { subscribe: false }),
103 )
104 expect(isRequestSubscribed('increment(undefined)', promise.requestId)).toBe(
105 false,
106 )
107
108 await expect(promise.unwrap()).resolves.toBe(0)
109 })
110
111 test('rejected query', async () => {
112 const { store, api } = storeRef
113 calls = 0
114 const promise = store.dispatch(
115 api.endpoints.failing.initiate(undefined, { subscribe: false }),
116 )
117 expect(isRequestSubscribed('failing(undefined)', promise.requestId)).toBe(
118 false,
119 )
120
121 await expect(promise).resolves.toMatchObject({
122 status: 'rejected',
123 })
124 })
125})
126
127describe('calling initiate should have resulting queryCacheKey match baseQuery queryCacheKey', () => {
128 const baseQuery = vi.fn(() => ({ data: 'success' }))
129 function getNewApi() {
130 return createApi({
131 baseQuery,
132 endpoints: (build) => ({
133 query: build.query<void, { arg1: string; arg2: string }>({
134 query: (args) => `queryUrl/${args.arg1}/${args.arg2}`,
135 }),
136 mutation: build.mutation<void, { arg1: string; arg2: string }>({
137 query: () => 'mutationUrl',
138 }),
139 }),
140 })
141 }
142 let api = getNewApi()
143 beforeEach(() => {
144 baseQuery.mockClear()
145 api = getNewApi()
146 })
147
148 test('should be a string and matching on queries', () => {
149 const { store: storeApi } = setupApiStore(api, undefined, {
150 withoutTestLifecycles: true,
151 })
152 const promise = storeApi.dispatch(
153 api.endpoints.query.initiate({ arg2: 'secondArg', arg1: 'firstArg' }),
154 )
155 expect(baseQuery).toHaveBeenCalledWith(
156 expect.any(String),
157 expect.objectContaining({
158 queryCacheKey: promise.queryCacheKey,
159 }),
160 undefined,
161 )
162 })
163
164 test('should be undefined and matching on mutations', () => {
165 const { store: storeApi } = setupApiStore(api, undefined, {
166 withoutTestLifecycles: true,
167 })
168 storeApi.dispatch(
169 api.endpoints.mutation.initiate({ arg2: 'secondArg', arg1: 'firstArg' }),
170 )
171 expect(baseQuery).toHaveBeenCalledWith(
172 expect.any(String),
173 expect.objectContaining({
174 queryCacheKey: undefined,
175 }),
176 undefined,
177 )
178 })
179})
180
181describe('getRunningQueryThunk with multiple stores', () => {
182 test('should isolate running queries between different store instances using the same API', async () => {
183 // Create a shared API instance
184 const sharedApi = createApi({
185 baseQuery: fakeBaseQuery(),
186 endpoints: (build) => ({
187 testQuery: build.query<string, string>({
188 async queryFn(arg) {
189 // Add delay to ensure queries are running when we check
190 await new Promise((resolve) => setTimeout(resolve, 50))
191 return { data: `result-${arg}` }
192 },
193 }),
194 }),
195 })
196
197 // Create two separate stores using the same API instance
198 const store1 = setupApiStore(sharedApi, undefined, {
199 withoutTestLifecycles: true,
200 }).store
201 const store2 = setupApiStore(sharedApi, undefined, {
202 withoutTestLifecycles: true,
203 }).store
204
205 // Start queries on both stores
206 const query1Promise = store1.dispatch(
207 sharedApi.endpoints.testQuery.initiate('arg1'),
208 )
209 const query2Promise = store2.dispatch(
210 sharedApi.endpoints.testQuery.initiate('arg2'),
211 )
212
213 // Verify that getRunningQueryThunk returns the correct query for each store
214 const runningQuery1 = store1.dispatch(
215 sharedApi.util.getRunningQueryThunk('testQuery', 'arg1'),
216 )
217 const runningQuery2 = store2.dispatch(
218 sharedApi.util.getRunningQueryThunk('testQuery', 'arg2'),
219 )
220
221 // Each store should only see its own running query
222 expect(runningQuery1).toBeDefined()
223 expect(runningQuery2).toBeDefined()
224 expect(runningQuery1?.requestId).toBe(query1Promise.requestId)
225 expect(runningQuery2?.requestId).toBe(query2Promise.requestId)
226
227 // Cross-store queries should not be visible
228 const crossQuery1 = store1.dispatch(
229 sharedApi.util.getRunningQueryThunk('testQuery', 'arg2'),
230 )
231 const crossQuery2 = store2.dispatch(
232 sharedApi.util.getRunningQueryThunk('testQuery', 'arg1'),
233 )
234
235 expect(crossQuery1).toBeUndefined()
236 expect(crossQuery2).toBeUndefined()
237
238 // Wait for queries to complete
239 await Promise.all([query1Promise, query2Promise])
240
241 // After completion, getRunningQueryThunk should return undefined for both stores
242 const completedQuery1 = store1.dispatch(
243 sharedApi.util.getRunningQueryThunk('testQuery', 'arg1'),
244 )
245 const completedQuery2 = store2.dispatch(
246 sharedApi.util.getRunningQueryThunk('testQuery', 'arg2'),
247 )
248
249 expect(completedQuery1).toBeUndefined()
250 expect(completedQuery2).toBeUndefined()
251 })
252
253 test('should handle same query args on different stores independently', async () => {
254 // Create a shared API instance
255 const sharedApi = createApi({
256 baseQuery: fakeBaseQuery(),
257 endpoints: (build) => ({
258 sameArgQuery: build.query<string, string>({
259 async queryFn(arg) {
260 await new Promise((resolve) => setTimeout(resolve, 50))
261 return { data: `result-${arg}-${Math.random()}` }
262 },
263 }),
264 }),
265 })
266
267 // Create two separate stores
268 const store1 = setupApiStore(sharedApi, undefined, {
269 withoutTestLifecycles: true,
270 }).store
271 const store2 = setupApiStore(sharedApi, undefined, {
272 withoutTestLifecycles: true,
273 }).store
274
275 // Start the same query on both stores
276 const sameArg = 'shared-arg'
277 const query1Promise = store1.dispatch(
278 sharedApi.endpoints.sameArgQuery.initiate(sameArg),
279 )
280 const query2Promise = store2.dispatch(
281 sharedApi.endpoints.sameArgQuery.initiate(sameArg),
282 )
283
284 // Both stores should see their own running query with the same cache key
285 const runningQuery1 = store1.dispatch(
286 sharedApi.util.getRunningQueryThunk('sameArgQuery', sameArg),
287 )
288 const runningQuery2 = store2.dispatch(
289 sharedApi.util.getRunningQueryThunk('sameArgQuery', sameArg),
290 )
291
292 expect(runningQuery1).toBeDefined()
293 expect(runningQuery2).toBeDefined()
294 expect(runningQuery1?.requestId).toBe(query1Promise.requestId)
295 expect(runningQuery2?.requestId).toBe(query2Promise.requestId)
296
297 // The request IDs should be different even though the cache key is the same
298 expect(runningQuery1?.requestId).not.toBe(runningQuery2?.requestId)
299
300 // But the cache keys should be the same
301 expect(runningQuery1?.queryCacheKey).toBe(runningQuery2?.queryCacheKey)
302
303 // Wait for completion
304 await Promise.all([query1Promise, query2Promise])
305 })
306})
Note: See TracBrowser for help on using the repository browser.