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