source: node_modules/@reduxjs/toolkit/src/tests/configureStore.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: 11.4 KB
Line 
1import * as DevTools from '@internal/devtoolsExtension'
2import type { Middleware, StoreEnhancer } from '@reduxjs/toolkit'
3import { Tuple } from '@reduxjs/toolkit'
4import type * as Redux from 'redux'
5import { vi } from 'vitest'
6
7vi.doMock('redux', async (importOriginal) => {
8 const redux = await importOriginal<typeof import('redux')>()
9
10 vi.spyOn(redux, 'applyMiddleware')
11 vi.spyOn(redux, 'combineReducers')
12 vi.spyOn(redux, 'compose')
13 vi.spyOn(redux, 'createStore')
14
15 return redux
16})
17
18describe('configureStore', async () => {
19 const composeWithDevToolsSpy = vi.spyOn(DevTools, 'composeWithDevTools')
20
21 const redux = await import('redux')
22
23 const { configureStore } = await import('@reduxjs/toolkit')
24
25 const reducer: Redux.Reducer = (state = {}, _action) => state
26
27 beforeEach(() => {
28 vi.clearAllMocks()
29 })
30
31 describe('given a function reducer', () => {
32 it('calls createStore with the reducer', () => {
33 configureStore({ reducer })
34 expect(configureStore({ reducer })).toBeInstanceOf(Object)
35
36 expect(redux.createStore).toHaveBeenCalledWith(
37 reducer,
38 undefined,
39 expect.any(Function),
40 )
41 expect(redux.applyMiddleware).toHaveBeenCalled()
42 if (process.env.TEST_DIST) {
43 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
44 } else {
45 expect(composeWithDevToolsSpy).toHaveBeenCalledTimes(2)
46 }
47 })
48 })
49
50 describe('given an object of reducers', () => {
51 it('calls createStore with the combined reducers', () => {
52 const reducer = {
53 reducer() {
54 return true
55 },
56 }
57 expect(configureStore({ reducer })).toBeInstanceOf(Object)
58 expect(redux.combineReducers).toHaveBeenCalledWith(reducer)
59 expect(redux.applyMiddleware).toHaveBeenCalled()
60 if (process.env.TEST_DIST) {
61 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
62 } else {
63 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
64 }
65 expect(redux.createStore).toHaveBeenCalledWith(
66 expect.any(Function),
67 undefined,
68 expect.any(Function),
69 )
70 })
71 })
72
73 describe('given no reducer', () => {
74 it('throws', () => {
75 expect(configureStore).toThrow(
76 '`reducer` is a required argument, and must be a function or an object of functions that can be passed to combineReducers',
77 )
78 })
79 })
80
81 describe('given no middleware', () => {
82 it('calls createStore without any middleware', () => {
83 expect(
84 configureStore({ middleware: () => new Tuple(), reducer }),
85 ).toBeInstanceOf(Object)
86 expect(redux.applyMiddleware).toHaveBeenCalledWith()
87 if (process.env.TEST_DIST) {
88 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
89 } else {
90 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
91 }
92 expect(redux.createStore).toHaveBeenCalledWith(
93 reducer,
94 undefined,
95 expect.any(Function),
96 )
97 })
98 })
99
100 describe('given an array of middleware', () => {
101 it('throws an error requiring a callback', () => {
102 // @ts-expect-error
103 expect(() => configureStore({ middleware: [], reducer })).toThrow(
104 '`middleware` field must be a callback',
105 )
106 })
107 })
108
109 describe('given undefined middleware', () => {
110 it('calls createStore with default middleware', () => {
111 expect(configureStore({ middleware: undefined, reducer })).toBeInstanceOf(
112 Object,
113 )
114 expect(redux.applyMiddleware).toHaveBeenCalledWith(
115 expect.any(Function), // immutableCheck
116 expect.any(Function), // thunk
117 expect.any(Function), // serializableCheck
118 expect.any(Function), // actionCreatorCheck
119 )
120 if (process.env.TEST_DIST) {
121 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
122 } else {
123 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
124 }
125 expect(redux.createStore).toHaveBeenCalledWith(
126 reducer,
127 undefined,
128 expect.any(Function),
129 )
130 })
131 })
132
133 describe('given any middleware', () => {
134 const exampleMiddleware: Middleware<any, any> = () => (next) => (action) =>
135 next(action)
136 it('throws an error by default if there are duplicate middleware', () => {
137 const makeStore = () => {
138 return configureStore({
139 reducer,
140 middleware: (gDM) =>
141 gDM().concat(exampleMiddleware, exampleMiddleware),
142 })
143 }
144
145 expect(makeStore).toThrowError(
146 'Duplicate middleware references found when creating the store. Ensure that each middleware is only included once.',
147 )
148 })
149
150 it('does not throw a duplicate middleware error if duplicateMiddlewareCheck is disabled', () => {
151 const makeStore = () => {
152 return configureStore({
153 reducer,
154 middleware: (gDM) =>
155 gDM().concat(exampleMiddleware, exampleMiddleware),
156 duplicateMiddlewareCheck: false,
157 })
158 }
159
160 expect(makeStore).not.toThrowError()
161 })
162 })
163
164 describe('given a middleware creation function that returns undefined', () => {
165 it('throws an error', () => {
166 const invalidBuilder = vi.fn((getDefaultMiddleware) => undefined as any)
167 expect(() =>
168 configureStore({ middleware: invalidBuilder, reducer }),
169 ).toThrow(
170 'when using a middleware builder function, an array of middleware must be returned',
171 )
172 })
173 })
174
175 describe('given a middleware creation function that returns an array with non-functions', () => {
176 it('throws an error', () => {
177 const invalidBuilder = vi.fn((getDefaultMiddleware) => [true] as any)
178 expect(() =>
179 configureStore({ middleware: invalidBuilder, reducer }),
180 ).toThrow('each middleware provided to configureStore must be a function')
181 })
182 })
183
184 describe('given custom middleware', () => {
185 it('calls createStore with custom middleware and without default middleware', () => {
186 const thank: Redux.Middleware = (_store) => (next) => (action) =>
187 next(action)
188 expect(
189 configureStore({ middleware: () => new Tuple(thank), reducer }),
190 ).toBeInstanceOf(Object)
191 expect(redux.applyMiddleware).toHaveBeenCalledWith(thank)
192 if (process.env.TEST_DIST) {
193 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
194 } else {
195 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
196 }
197 expect(redux.createStore).toHaveBeenCalledWith(
198 reducer,
199 undefined,
200 expect.any(Function),
201 )
202 })
203 })
204
205 describe('middleware builder notation', () => {
206 it('calls builder, passes getDefaultMiddleware and uses returned middlewares', () => {
207 const thank = vi.fn(
208 ((_store) => (next) => (action) => 'foobar') as Redux.Middleware,
209 )
210
211 const builder = vi.fn((getDefaultMiddleware) => {
212 expect(getDefaultMiddleware).toEqual(expect.any(Function))
213 expect(getDefaultMiddleware()).toEqual(expect.any(Array))
214
215 return new Tuple(thank)
216 })
217
218 const store = configureStore({ middleware: builder, reducer })
219
220 expect(builder).toHaveBeenCalled()
221
222 expect(store.dispatch({ type: 'test' })).toBe('foobar')
223 })
224 })
225
226 describe('with devTools disabled', () => {
227 it('calls createStore without devTools enhancer', () => {
228 expect(configureStore({ devTools: false, reducer })).toBeInstanceOf(
229 Object,
230 )
231 expect(redux.applyMiddleware).toHaveBeenCalled()
232 expect(redux.compose).toHaveBeenCalled()
233 expect(redux.createStore).toHaveBeenCalledWith(
234 reducer,
235 undefined,
236 expect.any(Function),
237 )
238 })
239 })
240
241 describe('with devTools options', () => {
242 it('calls createStore with devTools enhancer and option', () => {
243 const options = {
244 name: 'myApp',
245 trace: true,
246 }
247 expect(configureStore({ devTools: options, reducer })).toBeInstanceOf(
248 Object,
249 )
250 expect(redux.applyMiddleware).toHaveBeenCalled()
251 if (process.env.TEST_DIST) {
252 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
253 } else {
254 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
255
256 expect(composeWithDevToolsSpy).toHaveBeenLastCalledWith(options)
257 }
258 expect(redux.createStore).toHaveBeenCalledWith(
259 reducer,
260 undefined,
261 expect.any(Function),
262 )
263 })
264 })
265
266 describe('given preloadedState', () => {
267 it('calls createStore with preloadedState', () => {
268 expect(configureStore({ reducer })).toBeInstanceOf(Object)
269 expect(redux.applyMiddleware).toHaveBeenCalled()
270 if (process.env.TEST_DIST) {
271 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
272 } else {
273 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
274 }
275 expect(redux.createStore).toHaveBeenCalledWith(
276 reducer,
277 undefined,
278 expect.any(Function),
279 )
280 })
281 })
282
283 describe('given enhancers', () => {
284 let dummyEnhancerCalled = false
285
286 const dummyEnhancer: StoreEnhancer =
287 (createStore) => (reducer, preloadedState) => {
288 dummyEnhancerCalled = true
289
290 return createStore(reducer, preloadedState)
291 }
292
293 beforeEach(() => {
294 dummyEnhancerCalled = false
295 })
296
297 it('calls createStore with enhancers', () => {
298 expect(
299 configureStore({
300 enhancers: (gDE) => gDE().concat(dummyEnhancer),
301 reducer,
302 }),
303 ).toBeInstanceOf(Object)
304 expect(redux.applyMiddleware).toHaveBeenCalled()
305 if (process.env.TEST_DIST) {
306 expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
307 } else {
308 expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
309 }
310 expect(redux.createStore).toHaveBeenCalledWith(
311 reducer,
312 undefined,
313 expect.any(Function),
314 )
315
316 expect(dummyEnhancerCalled).toBe(true)
317 })
318
319 describe('invalid arguments', () => {
320 test('enhancers is not a callback', () => {
321 expect(() => configureStore({ reducer, enhancers: [] as any })).toThrow(
322 '`enhancers` field must be a callback',
323 )
324 })
325
326 test('callback fails to return array', () => {
327 expect(() =>
328 configureStore({ reducer, enhancers: (() => {}) as any }),
329 ).toThrow('`enhancers` callback must return an array')
330 })
331
332 test('array contains non-function', () => {
333 expect(() =>
334 configureStore({ reducer, enhancers: (() => ['']) as any }),
335 ).toThrow('each enhancer provided to configureStore must be a function')
336 })
337 })
338
339 const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
340 beforeEach(() => {
341 consoleSpy.mockClear()
342 })
343 afterAll(() => {
344 consoleSpy.mockRestore()
345 })
346
347 it('warns if middleware enhancer is excluded from final array when middlewares are provided', () => {
348 const store = configureStore({
349 reducer,
350 enhancers: () => new Tuple(dummyEnhancer),
351 })
352
353 expect(dummyEnhancerCalled).toBe(true)
354
355 expect(consoleSpy).toHaveBeenCalledWith(
356 'middlewares were provided, but middleware enhancer was not included in final enhancers - make sure to call `getDefaultEnhancers`',
357 )
358 })
359 it("doesn't warn when middleware enhancer is excluded if no middlewares provided", () => {
360 const store = configureStore({
361 reducer,
362 middleware: () => new Tuple(),
363 enhancers: () => new Tuple(dummyEnhancer),
364 })
365
366 expect(dummyEnhancerCalled).toBe(true)
367
368 expect(consoleSpy).not.toHaveBeenCalled()
369 })
370 })
371})
Note: See TracBrowser for help on using the repository browser.