source: node_modules/@reduxjs/toolkit/src/tests/utils/helpers.tsx@ a762898

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

Added visualizations

  • Property mode set to 100644
File size: 4.8 KB
Line 
1import type {
2 EnhancedStore,
3 Middleware,
4 Reducer,
5 Store,
6 UnknownAction,
7} from '@reduxjs/toolkit'
8import { configureStore } from '@reduxjs/toolkit'
9import { setupListeners } from '@reduxjs/toolkit/query'
10import { useCallback, useEffect, useRef } from 'react'
11
12import { Provider } from 'react-redux'
13
14import { act, cleanup } from '@testing-library/react'
15
16export const ANY = 0 as any
17
18export const DEFAULT_DELAY_MS = 150
19
20export const getSerializedHeaders = (headers: Headers = new Headers()) => {
21 const result: Record<string, string> = {}
22 headers.forEach((val, key) => {
23 result[key] = val
24 })
25 return result
26}
27
28export async function waitMs(time = DEFAULT_DELAY_MS) {
29 const now = Date.now()
30 while (Date.now() < now + time) {
31 await new Promise((res) => process.nextTick(res))
32 }
33}
34
35export function waitForFakeTimer(time = DEFAULT_DELAY_MS) {
36 return new Promise((resolve) => setTimeout(resolve, time))
37}
38
39export function withProvider(store: Store<any>) {
40 return function Wrapper({ children }: any) {
41 return <Provider store={store}>{children}</Provider>
42 }
43}
44
45export const hookWaitFor = async (cb: () => void, time = 2000) => {
46 const startedAt = Date.now()
47
48 while (true) {
49 try {
50 cb()
51 return true
52 } catch (e) {
53 if (Date.now() > startedAt + time) {
54 throw e
55 }
56 await act(async () => {
57 await waitMs(2)
58 })
59 }
60 }
61}
62export const fakeTimerWaitFor = async (cb: () => void, time = 2000) => {
63 const startedAt = Date.now()
64
65 while (true) {
66 try {
67 cb()
68 return true
69 } catch (e) {
70 if (Date.now() > startedAt + time) {
71 throw e
72 }
73 await act(async () => {
74 await vi.advanceTimersByTimeAsync(2)
75 })
76 }
77 }
78}
79
80export const useRenderCounter = () => {
81 const countRef = useRef(0)
82
83 useEffect(() => {
84 countRef.current += 1
85 })
86
87 useEffect(() => {
88 return () => {
89 countRef.current = 0
90 }
91 }, [])
92
93 return useCallback(() => countRef.current, [])
94}
95
96expect.extend({
97 toMatchSequence(
98 _actions: UnknownAction[],
99 ...matchers: Array<(arg: any) => boolean>
100 ) {
101 const actions = _actions.concat()
102 actions.shift() // remove INIT
103
104 for (let i = 0; i < matchers.length; i++) {
105 if (!matchers[i](actions[i])) {
106 return {
107 message: () =>
108 `Action ${actions[i].type} does not match sequence at position ${i}.
109All actions:
110${actions.map((a) => a.type).join('\n')}`,
111 pass: false,
112 }
113 }
114 }
115 return {
116 message: () => `All actions match the sequence.`,
117 pass: true,
118 }
119 },
120})
121
122export const actionsReducer = {
123 actions: (state: UnknownAction[] = [], action: UnknownAction) => {
124 // As of 2.0-beta.4, we are going to ignore all `subscriptionsUpdated` actions in tests
125 if (action.type.includes('subscriptionsUpdated')) {
126 return state
127 }
128
129 return [...state, action]
130 },
131}
132
133export function setupApiStore<
134 A extends {
135 reducerPath: 'api'
136 reducer: Reducer<any, any>
137 middleware: Middleware
138 util: { resetApiState(): any }
139 },
140 R extends Record<string, Reducer<any, any>> = Record<never, never>,
141>(
142 api: A,
143 extraReducers?: R,
144 options: {
145 withoutListeners?: boolean
146 withoutTestLifecycles?: boolean
147 middleware?: {
148 prepend?: Middleware[]
149 concat?: Middleware[]
150 }
151 } = {},
152) {
153 const { middleware } = options
154 const getStore = () =>
155 configureStore({
156 reducer: { api: api.reducer, ...extraReducers },
157 middleware: (gdm) => {
158 const tempMiddleware = gdm({
159 serializableCheck: false,
160 immutableCheck: false,
161 }).concat(api.middleware)
162
163 return tempMiddleware
164 .concat(middleware?.concat ?? [])
165 .prepend(middleware?.prepend ?? []) as typeof tempMiddleware
166 },
167 enhancers: (gde) =>
168 gde({
169 autoBatch: false,
170 }),
171 })
172
173 type State = {
174 api: ReturnType<A['reducer']>
175 } & {
176 [K in keyof R]: ReturnType<R[K]>
177 }
178 type StoreType = EnhancedStore<
179 {
180 api: ReturnType<A['reducer']>
181 } & {
182 [K in keyof R]: ReturnType<R[K]>
183 },
184 UnknownAction,
185 ReturnType<typeof getStore> extends EnhancedStore<any, any, infer M>
186 ? M
187 : never
188 >
189
190 const initialStore = getStore() as StoreType
191 const refObj = {
192 api,
193 store: initialStore,
194 wrapper: withProvider(initialStore),
195 }
196 let cleanupListeners: () => void
197
198 if (!options.withoutTestLifecycles) {
199 beforeEach(() => {
200 const store = getStore() as StoreType
201 refObj.store = store
202 refObj.wrapper = withProvider(store)
203 if (!options.withoutListeners) {
204 cleanupListeners = setupListeners(store.dispatch)
205 }
206 })
207 afterEach(() => {
208 cleanup()
209 if (!options.withoutListeners) {
210 cleanupListeners()
211 }
212 refObj.store.dispatch(api.util.resetApiState())
213 })
214 }
215
216 return refObj
217}
Note: See TracBrowser for help on using the repository browser.