Skip to content

Commit

Permalink
Restructure configureStore test to allow valid mocking
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Jan 19, 2023
1 parent a2fe2e3 commit ec58e10
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/toolkit/src/query/tests/cleanup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ test('data stays in store when one component requiring the data stays in the sto
expect(getSubStateB()).toEqual(statusB)
})

test.only('Minimizes the number of subscription dispatches when multiple components ask for the same data', async () => {
test('Minimizes the number of subscription dispatches when multiple components ask for the same data', async () => {
const listenerMiddleware = createListenerMiddleware()
const storeRef = setupApiStore(api, undefined, {
middleware: {
Expand Down
83 changes: 65 additions & 18 deletions packages/toolkit/src/tests/configureStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,64 @@
import { vi } from 'vitest'
import type { StoreEnhancer, StoreEnhancerStoreCreator } from '@reduxjs/toolkit'
import { configureStore } from '@reduxjs/toolkit'
import * as RTK from '@reduxjs/toolkit'
import * as redux from 'redux'
import * as devtools from '@internal/devtoolsExtension'
import type * as Redux from 'redux'
import type * as DevTools from '@internal/devtoolsExtension'

vi.doMock('redux', async () => {
const redux: any = await vi.importActual('redux')

describe('configureStore', () => {
vi.spyOn(redux, 'applyMiddleware')
vi.spyOn(redux, 'combineReducers')
vi.spyOn(redux, 'compose')
vi.spyOn(redux, 'createStore')

return redux
})

vi.doMock('@internal/devtoolsExtension', async () => {
const devtools: typeof DevTools = await vi.importActual(
'@internal/devtoolsExtension'
)
vi.spyOn(devtools, 'composeWithDevTools') // @remap-prod-remove-line
return devtools
})

function originalReduxCompose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}

if (funcs.length === 1) {
return funcs[0]
}

return funcs.reduce(
(a, b) =>
(...args: any) =>
a(b(...args))
)
}

const reducer: redux.Reducer = (state = {}, _action) => state
function originalComposeWithDevtools() {
if (arguments.length === 0) return undefined
if (typeof arguments[0] === 'object') return originalReduxCompose
return originalReduxCompose.apply(null, arguments as any as Function[])
}

describe('configureStore', async () => {
// RTK's internal `composeWithDevtools` function isn't publicly exported,
// so we can't mock it. However, it _does_ try to access the global extension method
// attached to `window`. So, if we mock _that_, we'll know if the enhancer ran.
const mockDevtoolsCompose = vi
.fn()
.mockImplementation(originalComposeWithDevtools)
;(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = mockDevtoolsCompose

const redux = await import('redux')

const { configureStore } = await import('@reduxjs/toolkit')

const reducer: Redux.Reducer = (state = {}, _action) => state

beforeEach(() => {
vi.clearAllMocks()
Expand All @@ -22,13 +68,14 @@ describe('configureStore', () => {
it('calls createStore with the reducer', () => {
configureStore({ reducer })
expect(configureStore({ reducer })).toBeInstanceOf(Object)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line

expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
expect.any(Function)
)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line
})
})

Expand All @@ -42,7 +89,7 @@ describe('configureStore', () => {
expect(configureStore({ reducer })).toBeInstanceOf(Object)
expect(redux.combineReducers).toHaveBeenCalledWith(reducer)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(redux.createStore).toHaveBeenCalledWith(
expect.any(Function),
undefined,
Expand All @@ -63,7 +110,7 @@ describe('configureStore', () => {
it('calls createStore without any middleware', () => {
expect(configureStore({ middleware: [], reducer })).toBeInstanceOf(Object)
expect(redux.applyMiddleware).toHaveBeenCalledWith()
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand All @@ -82,7 +129,7 @@ describe('configureStore', () => {
expect.any(Function), // immutableCheck
expect.any(Function) // serializableCheck
)
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand Down Expand Up @@ -121,13 +168,13 @@ describe('configureStore', () => {

describe('given custom middleware', () => {
it('calls createStore with custom middleware and without default middleware', () => {
const thank: redux.Middleware = (_store) => (next) => (action) =>
const thank: Redux.Middleware = (_store) => (next) => (action) =>
next(action)
expect(configureStore({ middleware: [thank], reducer })).toBeInstanceOf(
Object
)
expect(redux.applyMiddleware).toHaveBeenCalledWith(thank)
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand All @@ -139,7 +186,7 @@ describe('configureStore', () => {
describe('middleware builder notation', () => {
it('calls builder, passes getDefaultMiddleware and uses returned middlewares', () => {
const thank = vi.fn(
((_store) => (next) => (action) => 'foobar') as redux.Middleware
((_store) => (next) => (action) => 'foobar') as Redux.Middleware
)

const builder = vi.fn((getDefaultMiddleware) => {
Expand Down Expand Up @@ -182,7 +229,7 @@ describe('configureStore', () => {
Object
)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(devtools.composeWithDevTools).toHaveBeenCalledWith(options) // @remap-prod-remove-line
expect(mockDevtoolsCompose).toHaveBeenCalledWith(options) // @remap-prod-remove-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand All @@ -195,7 +242,7 @@ describe('configureStore', () => {
it('calls createStore with preloadedState', () => {
expect(configureStore({ reducer })).toBeInstanceOf(Object)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand All @@ -206,12 +253,12 @@ describe('configureStore', () => {

describe('given enhancers', () => {
it('calls createStore with enhancers', () => {
const enhancer: redux.StoreEnhancer = (next) => next
const enhancer: Redux.StoreEnhancer = (next) => next
expect(configureStore({ enhancers: [enhancer], reducer })).toBeInstanceOf(
Object
)
expect(redux.applyMiddleware).toHaveBeenCalled()
expect(devtools.composeWithDevTools).toHaveBeenCalled() // @remap-prod-remove-line
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line
expect(redux.createStore).toHaveBeenCalledWith(
reducer,
undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/toolkit/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineConfig({
},
deps: {
interopDefault: true,
inline: ['redux', '@reduxjs/toolkit'],
},
},
})

0 comments on commit ec58e10

Please sign in to comment.