Build NapCat Artifacts / Windows Loader Test (push) Canceled after 0s
Build NapCat Artifacts / Type Check & Test (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Framework (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Shell (push) Canceled after 0s
372 lines
11 KiB
Plaintext
372 lines
11 KiB
Plaintext
import * as DevTools from '@internal/devtoolsExtension'
|
|
import type { Middleware, StoreEnhancer } from '@reduxjs/toolkit'
|
|
import { Tuple } from '@reduxjs/toolkit'
|
|
import type * as Redux from 'redux'
|
|
import { vi } from 'vitest'
|
|
|
|
vi.doMock('redux', async (importOriginal) => {
|
|
const redux = await importOriginal<typeof import('redux')>()
|
|
|
|
vi.spyOn(redux, 'applyMiddleware')
|
|
vi.spyOn(redux, 'combineReducers')
|
|
vi.spyOn(redux, 'compose')
|
|
vi.spyOn(redux, 'createStore')
|
|
|
|
return redux
|
|
})
|
|
|
|
describe('configureStore', async () => {
|
|
const composeWithDevToolsSpy = vi.spyOn(DevTools, 'composeWithDevTools')
|
|
|
|
const redux = await import('redux')
|
|
|
|
const { configureStore } = await import('@reduxjs/toolkit')
|
|
|
|
const reducer: Redux.Reducer = (state = {}, _action) => state
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('given a function reducer', () => {
|
|
it('calls createStore with the reducer', () => {
|
|
configureStore({ reducer })
|
|
expect(configureStore({ reducer })).toBeInstanceOf(Object)
|
|
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledTimes(2)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('given an object of reducers', () => {
|
|
it('calls createStore with the combined reducers', () => {
|
|
const reducer = {
|
|
reducer() {
|
|
return true
|
|
},
|
|
}
|
|
expect(configureStore({ reducer })).toBeInstanceOf(Object)
|
|
expect(redux.combineReducers).toHaveBeenCalledWith(reducer)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
expect.any(Function),
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given no reducer', () => {
|
|
it('throws', () => {
|
|
expect(configureStore).toThrow(
|
|
'`reducer` is a required argument, and must be a function or an object of functions that can be passed to combineReducers',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given no middleware', () => {
|
|
it('calls createStore without any middleware', () => {
|
|
expect(
|
|
configureStore({ middleware: () => new Tuple(), reducer }),
|
|
).toBeInstanceOf(Object)
|
|
expect(redux.applyMiddleware).toHaveBeenCalledWith()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given an array of middleware', () => {
|
|
it('throws an error requiring a callback', () => {
|
|
// @ts-expect-error
|
|
expect(() => configureStore({ middleware: [], reducer })).toThrow(
|
|
'`middleware` field must be a callback',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given undefined middleware', () => {
|
|
it('calls createStore with default middleware', () => {
|
|
expect(configureStore({ middleware: undefined, reducer })).toBeInstanceOf(
|
|
Object,
|
|
)
|
|
expect(redux.applyMiddleware).toHaveBeenCalledWith(
|
|
expect.any(Function), // immutableCheck
|
|
expect.any(Function), // thunk
|
|
expect.any(Function), // serializableCheck
|
|
expect.any(Function), // actionCreatorCheck
|
|
)
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given any middleware', () => {
|
|
const exampleMiddleware: Middleware<any, any> = () => (next) => (action) =>
|
|
next(action)
|
|
it('throws an error by default if there are duplicate middleware', () => {
|
|
const makeStore = () => {
|
|
return configureStore({
|
|
reducer,
|
|
middleware: (gDM) =>
|
|
gDM().concat(exampleMiddleware, exampleMiddleware),
|
|
})
|
|
}
|
|
|
|
expect(makeStore).toThrowError(
|
|
'Duplicate middleware references found when creating the store. Ensure that each middleware is only included once.',
|
|
)
|
|
})
|
|
|
|
it('does not throw a duplicate middleware error if duplicateMiddlewareCheck is disabled', () => {
|
|
const makeStore = () => {
|
|
return configureStore({
|
|
reducer,
|
|
middleware: (gDM) =>
|
|
gDM().concat(exampleMiddleware, exampleMiddleware),
|
|
duplicateMiddlewareCheck: false,
|
|
})
|
|
}
|
|
|
|
expect(makeStore).not.toThrowError()
|
|
})
|
|
})
|
|
|
|
describe('given a middleware creation function that returns undefined', () => {
|
|
it('throws an error', () => {
|
|
const invalidBuilder = vi.fn((getDefaultMiddleware) => undefined as any)
|
|
expect(() =>
|
|
configureStore({ middleware: invalidBuilder, reducer }),
|
|
).toThrow(
|
|
'when using a middleware builder function, an array of middleware must be returned',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given a middleware creation function that returns an array with non-functions', () => {
|
|
it('throws an error', () => {
|
|
const invalidBuilder = vi.fn((getDefaultMiddleware) => [true] as any)
|
|
expect(() =>
|
|
configureStore({ middleware: invalidBuilder, reducer }),
|
|
).toThrow('each middleware provided to configureStore must be a function')
|
|
})
|
|
})
|
|
|
|
describe('given custom middleware', () => {
|
|
it('calls createStore with custom middleware and without default middleware', () => {
|
|
const thank: Redux.Middleware = (_store) => (next) => (action) =>
|
|
next(action)
|
|
expect(
|
|
configureStore({ middleware: () => new Tuple(thank), reducer }),
|
|
).toBeInstanceOf(Object)
|
|
expect(redux.applyMiddleware).toHaveBeenCalledWith(thank)
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('middleware builder notation', () => {
|
|
it('calls builder, passes getDefaultMiddleware and uses returned middlewares', () => {
|
|
const thank = vi.fn(
|
|
((_store) => (next) => (action) => 'foobar') as Redux.Middleware,
|
|
)
|
|
|
|
const builder = vi.fn((getDefaultMiddleware) => {
|
|
expect(getDefaultMiddleware).toEqual(expect.any(Function))
|
|
expect(getDefaultMiddleware()).toEqual(expect.any(Array))
|
|
|
|
return new Tuple(thank)
|
|
})
|
|
|
|
const store = configureStore({ middleware: builder, reducer })
|
|
|
|
expect(builder).toHaveBeenCalled()
|
|
|
|
expect(store.dispatch({ type: 'test' })).toBe('foobar')
|
|
})
|
|
})
|
|
|
|
describe('with devTools disabled', () => {
|
|
it('calls createStore without devTools enhancer', () => {
|
|
expect(configureStore({ devTools: false, reducer })).toBeInstanceOf(
|
|
Object,
|
|
)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
expect(redux.compose).toHaveBeenCalled()
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('with devTools options', () => {
|
|
it('calls createStore with devTools enhancer and option', () => {
|
|
const options = {
|
|
name: 'myApp',
|
|
trace: true,
|
|
}
|
|
expect(configureStore({ devTools: options, reducer })).toBeInstanceOf(
|
|
Object,
|
|
)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
|
|
expect(composeWithDevToolsSpy).toHaveBeenLastCalledWith(options)
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given preloadedState', () => {
|
|
it('calls createStore with preloadedState', () => {
|
|
expect(configureStore({ reducer })).toBeInstanceOf(Object)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('given enhancers', () => {
|
|
let dummyEnhancerCalled = false
|
|
|
|
const dummyEnhancer: StoreEnhancer =
|
|
(createStore) => (reducer, preloadedState) => {
|
|
dummyEnhancerCalled = true
|
|
|
|
return createStore(reducer, preloadedState)
|
|
}
|
|
|
|
beforeEach(() => {
|
|
dummyEnhancerCalled = false
|
|
})
|
|
|
|
it('calls createStore with enhancers', () => {
|
|
expect(
|
|
configureStore({
|
|
enhancers: (gDE) => gDE().concat(dummyEnhancer),
|
|
reducer,
|
|
}),
|
|
).toBeInstanceOf(Object)
|
|
expect(redux.applyMiddleware).toHaveBeenCalled()
|
|
if (process.env.TEST_DIST) {
|
|
expect(composeWithDevToolsSpy).not.toHaveBeenCalled()
|
|
} else {
|
|
expect(composeWithDevToolsSpy).toHaveBeenCalledOnce()
|
|
}
|
|
expect(redux.createStore).toHaveBeenCalledWith(
|
|
reducer,
|
|
undefined,
|
|
expect.any(Function),
|
|
)
|
|
|
|
expect(dummyEnhancerCalled).toBe(true)
|
|
})
|
|
|
|
describe('invalid arguments', () => {
|
|
test('enhancers is not a callback', () => {
|
|
expect(() => configureStore({ reducer, enhancers: [] as any })).toThrow(
|
|
'`enhancers` field must be a callback',
|
|
)
|
|
})
|
|
|
|
test('callback fails to return array', () => {
|
|
expect(() =>
|
|
configureStore({ reducer, enhancers: (() => {}) as any }),
|
|
).toThrow('`enhancers` callback must return an array')
|
|
})
|
|
|
|
test('array contains non-function', () => {
|
|
expect(() =>
|
|
configureStore({ reducer, enhancers: (() => ['']) as any }),
|
|
).toThrow('each enhancer provided to configureStore must be a function')
|
|
})
|
|
})
|
|
|
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
beforeEach(() => {
|
|
consoleSpy.mockClear()
|
|
})
|
|
afterAll(() => {
|
|
consoleSpy.mockRestore()
|
|
})
|
|
|
|
it('warns if middleware enhancer is excluded from final array when middlewares are provided', () => {
|
|
const store = configureStore({
|
|
reducer,
|
|
enhancers: () => new Tuple(dummyEnhancer),
|
|
})
|
|
|
|
expect(dummyEnhancerCalled).toBe(true)
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
'middlewares were provided, but middleware enhancer was not included in final enhancers - make sure to call `getDefaultEnhancers`',
|
|
)
|
|
})
|
|
it("doesn't warn when middleware enhancer is excluded if no middlewares provided", () => {
|
|
const store = configureStore({
|
|
reducer,
|
|
middleware: () => new Tuple(),
|
|
enhancers: () => new Tuple(dummyEnhancer),
|
|
})
|
|
|
|
expect(dummyEnhancerCalled).toBe(true)
|
|
|
|
expect(consoleSpy).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|