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

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

Added visualizations

  • Property mode set to 100644
File size: 6.5 KB
Line 
1import { createSlice, createAction } from '@reduxjs/toolkit'
2import type { CombinedState } from '@reduxjs/toolkit/query'
3import { createApi } from '@reduxjs/toolkit/query'
4import { delay } from 'msw'
5import { setupApiStore } from '../../tests/utils/helpers'
6
7let shouldApiResponseSuccess = true
8
9const rehydrateAction = createAction<{ api: CombinedState<any, any, any> }>(
10 'persist/REHYDRATE',
11)
12
13const baseQuery = (args?: any) => ({ data: args })
14const api = createApi({
15 baseQuery,
16 tagTypes: ['SUCCEED', 'FAILED'],
17 endpoints: (build) => ({
18 getUser: build.query<{ url: string; success: boolean }, number>({
19 query(id) {
20 return { url: `user/${id}`, success: shouldApiResponseSuccess }
21 },
22 providesTags: (result) => (result?.success ? ['SUCCEED'] : ['FAILED']),
23 }),
24 }),
25 extractRehydrationInfo(action, { reducerPath }) {
26 if (rehydrateAction.match(action)) {
27 return action.payload?.[reducerPath]
28 }
29 return undefined
30 },
31})
32const { getUser } = api.endpoints
33
34const authSlice = createSlice({
35 name: 'auth',
36 initialState: {
37 token: '1234',
38 },
39 reducers: {
40 setToken(state, action) {
41 state.token = action.payload
42 },
43 },
44})
45
46const storeRef = setupApiStore(api, { auth: authSlice.reducer })
47
48describe('buildSlice', () => {
49 beforeEach(() => {
50 shouldApiResponseSuccess = true
51 })
52
53 it('only resets the api state when resetApiState is dispatched', async () => {
54 storeRef.store.dispatch({ type: 'unrelated' }) // trigger "registered middleware" into place
55 const initialState = storeRef.store.getState()
56
57 await storeRef.store.dispatch(
58 getUser.initiate(1, { subscriptionOptions: { pollingInterval: 10 } }),
59 )
60
61 const initialQueryState = {
62 api: {
63 config: {
64 focused: true,
65 invalidationBehavior: 'delayed',
66 keepUnusedDataFor: 60,
67 middlewareRegistered: true,
68 online: true,
69 reducerPath: 'api',
70 refetchOnFocus: false,
71 refetchOnMountOrArgChange: false,
72 refetchOnReconnect: false,
73 },
74 mutations: {},
75 provided: expect.any(Object),
76 queries: {
77 'getUser(1)': {
78 data: {
79 success: true,
80 url: 'user/1',
81 },
82 endpointName: 'getUser',
83 fulfilledTimeStamp: expect.any(Number),
84 originalArgs: 1,
85 requestId: expect.any(String),
86 startedTimeStamp: expect.any(Number),
87 status: 'fulfilled',
88 },
89 },
90 // Filled some time later
91 subscriptions: {},
92 },
93 auth: {
94 token: '1234',
95 },
96 }
97
98 expect(storeRef.store.getState()).toEqual(initialQueryState)
99
100 storeRef.store.dispatch(api.util.resetApiState())
101
102 expect(storeRef.store.getState()).toEqual(initialState)
103 })
104
105 it('replaces previous tags with new provided tags', async () => {
106 await storeRef.store.dispatch(getUser.initiate(1))
107
108 expect(
109 api.util.selectInvalidatedBy(storeRef.store.getState(), ['SUCCEED']),
110 ).toHaveLength(1)
111 expect(
112 api.util.selectInvalidatedBy(storeRef.store.getState(), ['FAILED']),
113 ).toHaveLength(0)
114
115 shouldApiResponseSuccess = false
116
117 storeRef.store.dispatch(getUser.initiate(1)).refetch()
118
119 await delay(10)
120
121 expect(
122 api.util.selectInvalidatedBy(storeRef.store.getState(), ['SUCCEED']),
123 ).toHaveLength(0)
124 expect(
125 api.util.selectInvalidatedBy(storeRef.store.getState(), ['FAILED']),
126 ).toHaveLength(1)
127 })
128
129 it('handles extractRehydrationInfo correctly', async () => {
130 await storeRef.store.dispatch(getUser.initiate(1))
131 await storeRef.store.dispatch(getUser.initiate(2))
132
133 const stateWithUser = storeRef.store.getState()
134
135 storeRef.store.dispatch(api.util.resetApiState())
136
137 storeRef.store.dispatch(rehydrateAction({ api: stateWithUser.api }))
138
139 const rehydratedState = storeRef.store.getState()
140 expect(rehydratedState).toEqual(stateWithUser)
141 })
142})
143
144describe('`merge` callback', () => {
145 const baseQuery = (args?: any) => ({ data: args })
146
147 interface Todo {
148 id: string
149 text: string
150 }
151
152 it('Calls `merge` once there is existing data, and allows mutations of cache state', async () => {
153 let mergeCalled = false
154 let queryFnCalls = 0
155 const todoTexts = ['A', 'B', 'C', 'D']
156
157 const api = createApi({
158 baseQuery,
159 endpoints: (build) => ({
160 getTodos: build.query<Todo[], void>({
161 async queryFn() {
162 const text = todoTexts[queryFnCalls]
163 return { data: [{ id: `${queryFnCalls++}`, text }] }
164 },
165 merge(currentCacheValue, responseData) {
166 mergeCalled = true
167 currentCacheValue.push(...responseData)
168 },
169 }),
170 }),
171 })
172
173 const storeRef = setupApiStore(api, undefined, {
174 withoutTestLifecycles: true,
175 })
176
177 const selectTodoEntry = api.endpoints.getTodos.select()
178
179 const res = storeRef.store.dispatch(api.endpoints.getTodos.initiate())
180 await res
181 expect(mergeCalled).toBe(false)
182 const todoEntry1 = selectTodoEntry(storeRef.store.getState())
183 expect(todoEntry1.data).toEqual([{ id: '0', text: 'A' }])
184
185 res.refetch()
186
187 await delay(10)
188
189 expect(mergeCalled).toBe(true)
190 const todoEntry2 = selectTodoEntry(storeRef.store.getState())
191
192 expect(todoEntry2.data).toEqual([
193 { id: '0', text: 'A' },
194 { id: '1', text: 'B' },
195 ])
196 })
197
198 it('Allows returning a different value from `merge`', async () => {
199 let firstQueryFnCall = true
200
201 const api = createApi({
202 baseQuery,
203 endpoints: (build) => ({
204 getTodos: build.query<Todo[], void>({
205 async queryFn() {
206 const item = firstQueryFnCall
207 ? { id: '0', text: 'A' }
208 : { id: '1', text: 'B' }
209 firstQueryFnCall = false
210 return { data: [item] }
211 },
212 merge(currentCacheValue, responseData) {
213 return responseData
214 },
215 }),
216 }),
217 })
218
219 const storeRef = setupApiStore(api, undefined, {
220 withoutTestLifecycles: true,
221 })
222
223 const selectTodoEntry = api.endpoints.getTodos.select()
224
225 const res = storeRef.store.dispatch(api.endpoints.getTodos.initiate())
226 await res
227
228 const todoEntry1 = selectTodoEntry(storeRef.store.getState())
229 expect(todoEntry1.data).toEqual([{ id: '0', text: 'A' }])
230
231 res.refetch()
232
233 await delay(10)
234
235 const todoEntry2 = selectTodoEntry(storeRef.store.getState())
236
237 expect(todoEntry2.data).toEqual([{ id: '1', text: 'B' }])
238 })
239})
Note: See TracBrowser for help on using the repository browser.