source: node_modules/@reduxjs/toolkit/src/query/tests/raceConditions.test.ts@ a762898

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

Added visualizations

  • Property mode set to 100644
File size: 3.4 KB
Line 
1import { createApi, QueryStatus } from '@reduxjs/toolkit/query'
2import { delay } from 'msw'
3import { actionsReducer, setupApiStore } from '../../tests/utils/helpers'
4
5// We need to be able to control when which query resolves to simulate race
6// conditions properly, that's the purpose of this factory.
7const createPromiseFactory = () => {
8 const resolveQueue: (() => void)[] = []
9 const createPromise = () =>
10 new Promise<void>((resolve) => {
11 resolveQueue.push(resolve)
12 })
13 const resolveOldest = () => {
14 resolveQueue.shift()?.()
15 }
16 return { createPromise, resolveOldest }
17}
18
19const getEatenBananaPromises = createPromiseFactory()
20const eatBananaPromises = createPromiseFactory()
21
22let eatenBananas = 0
23const api = createApi({
24 invalidationBehavior: 'delayed',
25 baseQuery: () => undefined as any,
26 tagTypes: ['Banana'],
27 endpoints: (build) => ({
28 // Eat a banana.
29 eatBanana: build.mutation<unknown, void>({
30 queryFn: async () => {
31 await eatBananaPromises.createPromise()
32 eatenBananas += 1
33 return { data: null, meta: {} }
34 },
35 invalidatesTags: ['Banana'],
36 }),
37
38 // Get the number of eaten bananas.
39 getEatenBananas: build.query<number, void>({
40 queryFn: async (arg, arg1, arg2, arg3) => {
41 const result = eatenBananas
42 await getEatenBananaPromises.createPromise()
43 return { data: result }
44 },
45 providesTags: ['Banana'],
46 }),
47 }),
48})
49const { getEatenBananas, eatBanana } = api.endpoints
50
51const storeRef = setupApiStore(api, {
52 ...actionsReducer,
53})
54
55it('invalidates a query after a corresponding mutation', async () => {
56 eatenBananas = 0
57
58 const query = storeRef.store.dispatch(getEatenBananas.initiate())
59 const getQueryState = () =>
60 storeRef.store.getState().api.queries[query.queryCacheKey]
61 getEatenBananaPromises.resolveOldest()
62 await delay(2)
63
64 expect(getQueryState()?.data).toBe(0)
65 expect(getQueryState()?.status).toBe(QueryStatus.fulfilled)
66
67 const mutation = storeRef.store.dispatch(eatBanana.initiate())
68 const getMutationState = () =>
69 storeRef.store.getState().api.mutations[mutation.requestId]
70 eatBananaPromises.resolveOldest()
71 await delay(2)
72
73 expect(getMutationState()?.status).toBe(QueryStatus.fulfilled)
74 expect(getQueryState()?.data).toBe(0)
75 expect(getQueryState()?.status).toBe(QueryStatus.pending)
76
77 getEatenBananaPromises.resolveOldest()
78 await delay(2)
79
80 expect(getQueryState()?.data).toBe(1)
81 expect(getQueryState()?.status).toBe(QueryStatus.fulfilled)
82})
83
84it('invalidates a query whose corresponding mutation finished while the query was in flight', async () => {
85 eatenBananas = 0
86
87 const query = storeRef.store.dispatch(getEatenBananas.initiate())
88 const getQueryState = () =>
89 storeRef.store.getState().api.queries[query.queryCacheKey]
90
91 const mutation = storeRef.store.dispatch(eatBanana.initiate())
92 const getMutationState = () =>
93 storeRef.store.getState().api.mutations[mutation.requestId]
94 eatBananaPromises.resolveOldest()
95 await delay(2)
96 expect(getMutationState()?.status).toBe(QueryStatus.fulfilled)
97
98 getEatenBananaPromises.resolveOldest()
99 await delay(2)
100 expect(getQueryState()?.data).toBe(0)
101 expect(getQueryState()?.status).toBe(QueryStatus.pending)
102
103 // should already be refetching
104 getEatenBananaPromises.resolveOldest()
105 await delay(2)
106
107 expect(getQueryState()?.status).toBe(QueryStatus.fulfilled)
108 expect(getQueryState()?.data).toBe(1)
109})
Note: See TracBrowser for help on using the repository browser.