Created
July 20, 2022 15:23
-
-
Save seanmhanson/2a236284bef925f679a73ca96eddde49 to your computer and use it in GitHub Desktop.
RTK Query Testing Utils - Setup API Store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
AnyAction, | |
combineReducers, | |
configureStore, | |
EnhancedStore, | |
Middleware, | |
Reducer, | |
} from '@reduxjs/toolkit'; | |
import { CurriedGetDefaultMiddleware as GetDefaultMiddleware } from '@reduxjs/toolkit/dist/getDefaultMiddleware'; | |
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
interface DefaultApi { | |
reducer: Reducer<any, any>; | |
reducerPath: string; | |
middleware: Middleware; | |
util: { resetApiState(): any }; | |
} | |
interface DefaultReducers { | |
[key: string]: Reducer<any, any>; | |
} | |
interface ApiStore { | |
api: any; | |
store: EnhancedStore; | |
} | |
/* | |
* A utility for creating mock stores used in testing the actions/behaviors of our API, independent | |
* of the UI. | |
* | |
* - this is similar to "redux-mock-store" when testing with redux, but this also includes the API | |
* reducers, whereas redux-mock-store does not us to include reducers | |
* | |
* - Based upon RTK Query's helper function, as per John McDowell | |
* RTK Query fn: https://github.com/reduxjs/redux-toolkit/blob/master/packages/toolkit/src/query/tests/helpers.tsx | |
* McDowell example: https://medium.com/@johnmcdowell0801/testing-rtk-query-with-jest-cdfa5aaf3dc1 | |
*/ | |
export function setupStoreWithApi< | |
Api extends DefaultApi, | |
ExtraReducers extends DefaultReducers = Record<never, never>, | |
>(api: Api, extraReducers?: ExtraReducers): ApiStore { | |
const getStore = (): EnhancedStore => { | |
const reducers = { | |
[api.reducerPath]: api.reducer, | |
...extraReducers, | |
}; | |
const middleware = (getDefaultMiddleware: GetDefaultMiddleware) => | |
getDefaultMiddleware({ | |
serializableCheck: false, | |
immutableChecK: false, | |
}).concat(api.middleware); | |
return configureStore({ reducer: combineReducers(reducers), middleware }); | |
}; | |
/** | |
* Rewritten for clarity from the RTK function, this creates the type that matches the | |
* created store, but as an explicit type (without the generics seen above as "Api" and | |
* "ExtraReducers"), so that the returned store will already be correctly typed | |
*/ | |
type ReducersFromApi = { api: ReturnType<Api['reducer']> }; | |
type ReducersFromOptions = { | |
[K in keyof ExtraReducers]: ReturnType<ExtraReducers[K]>; | |
}; | |
type StoreReducers = ReducersFromApi & ReducersFromOptions; | |
// middleware expected from the store, if any; otherwise, this is omitted entirely | |
type ExpectedStoreType = ReturnType<typeof getStore>; | |
type StoreMiddleware = ExpectedStoreType extends EnhancedStore< | |
any, | |
any, | |
infer M | |
> | |
? M | |
: never; | |
type StoreType = EnhancedStore<StoreReducers, AnyAction, StoreMiddleware>; | |
// initialize the api and store, ensuring that store is correctly cast after initialization | |
const initialStore = getStore() as StoreType; | |
const ref = { api, store: initialStore }; | |
const store = getStore() as StoreType; | |
ref.store = store; | |
return ref; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment