1 | import type { ThunkAction, AnyAction } from '@reduxjs/toolkit'
|
2 | import {
|
3 | isAllOf,
|
4 | isAnyOf,
|
5 | isAsyncThunkAction,
|
6 | isFulfilled,
|
7 | isPending,
|
8 | isRejected,
|
9 | isRejectedWithValue,
|
10 | createAction,
|
11 | createAsyncThunk,
|
12 | createReducer,
|
13 | } from '@reduxjs/toolkit'
|
14 |
|
15 | const thunk: ThunkAction<any, any, any, AnyAction> = () => {}
|
16 |
|
17 | describe('isAnyOf', () => {
|
18 | it('returns true only if any matchers match (match function)', () => {
|
19 | const actionA = createAction<string>('a')
|
20 | const actionB = createAction<number>('b')
|
21 |
|
22 | const trueAction = {
|
23 | type: 'a',
|
24 | payload: 'payload',
|
25 | }
|
26 |
|
27 | expect(isAnyOf(actionA, actionB)(trueAction)).toEqual(true)
|
28 |
|
29 | const falseAction = {
|
30 | type: 'c',
|
31 | payload: 'payload',
|
32 | }
|
33 |
|
34 | expect(isAnyOf(actionA, actionB)(falseAction)).toEqual(false)
|
35 | })
|
36 |
|
37 | it('returns true only if any type guards match', () => {
|
38 | const actionA = createAction<string>('a')
|
39 | const actionB = createAction<number>('b')
|
40 |
|
41 | const isActionA = actionA.match
|
42 | const isActionB = actionB.match
|
43 |
|
44 | const trueAction = {
|
45 | type: 'a',
|
46 | payload: 'payload',
|
47 | }
|
48 |
|
49 | expect(isAnyOf(isActionA, isActionB)(trueAction)).toEqual(true)
|
50 |
|
51 | const falseAction = {
|
52 | type: 'c',
|
53 | payload: 'payload',
|
54 | }
|
55 |
|
56 | expect(isAnyOf(isActionA, isActionB)(falseAction)).toEqual(false)
|
57 | })
|
58 |
|
59 | it('returns true only if any matchers match (thunk action creators)', () => {
|
60 | const thunkA = createAsyncThunk<string>('a', () => {
|
61 | return 'noop'
|
62 | })
|
63 | const thunkB = createAsyncThunk<number>('b', () => {
|
64 | return 0
|
65 | })
|
66 |
|
67 | const action = thunkA.fulfilled('fakeRequestId', 'test')
|
68 |
|
69 | expect(isAnyOf(thunkA.fulfilled, thunkB.fulfilled)(action)).toEqual(true)
|
70 |
|
71 | expect(
|
72 | isAnyOf(thunkA.pending, thunkA.rejected, thunkB.fulfilled)(action)
|
73 | ).toEqual(false)
|
74 | })
|
75 |
|
76 | it('works with reducers', () => {
|
77 | const actionA = createAction<string>('a')
|
78 | const actionB = createAction<number>('b')
|
79 |
|
80 | const trueAction = {
|
81 | type: 'a',
|
82 | payload: 'payload',
|
83 | }
|
84 |
|
85 | const initialState = { value: false }
|
86 |
|
87 | const reducer = createReducer(initialState, (builder) => {
|
88 | builder.addMatcher(isAnyOf(actionA, actionB), (state) => {
|
89 | return { ...state, value: true }
|
90 | })
|
91 | })
|
92 |
|
93 | expect(reducer(initialState, trueAction)).toEqual({ value: true })
|
94 |
|
95 | const falseAction = {
|
96 | type: 'c',
|
97 | payload: 'payload',
|
98 | }
|
99 |
|
100 | expect(reducer(initialState, falseAction)).toEqual(initialState)
|
101 | })
|
102 | })
|
103 |
|
104 | describe('isAllOf', () => {
|
105 | it('returns true only if all matchers match', () => {
|
106 | const actionA = createAction<string>('a')
|
107 |
|
108 | interface SpecialAction {
|
109 | payload: 'SPECIAL'
|
110 | }
|
111 |
|
112 | const isActionSpecial = (action: any): action is SpecialAction => {
|
113 | return action.payload === 'SPECIAL'
|
114 | }
|
115 |
|
116 | const trueAction = {
|
117 | type: 'a',
|
118 | payload: 'SPECIAL',
|
119 | }
|
120 |
|
121 | expect(isAllOf(actionA, isActionSpecial)(trueAction)).toEqual(true)
|
122 |
|
123 | const falseAction = {
|
124 | type: 'a',
|
125 | payload: 'ORDINARY',
|
126 | }
|
127 |
|
128 | expect(isAllOf(actionA, isActionSpecial)(falseAction)).toEqual(false)
|
129 |
|
130 | const thunkA = createAsyncThunk<string>('a', () => 'result')
|
131 |
|
132 | const specialThunkAction = thunkA.fulfilled('SPECIAL', 'fakeRequestId')
|
133 |
|
134 | expect(isAllOf(thunkA.fulfilled, isActionSpecial)(specialThunkAction)).toBe(
|
135 | true
|
136 | )
|
137 |
|
138 | const ordinaryThunkAction = thunkA.fulfilled('ORDINARY', 'fakeRequestId')
|
139 |
|
140 | expect(
|
141 | isAllOf(thunkA.fulfilled, isActionSpecial)(ordinaryThunkAction)
|
142 | ).toBe(false)
|
143 | })
|
144 | })
|
145 |
|
146 | describe('isPending', () => {
|
147 | test('should return false for a regular action', () => {
|
148 | const action = createAction<string>('action/type')('testPayload')
|
149 |
|
150 | expect(isPending()(action)).toBe(false)
|
151 | expect(isPending(action)).toBe(false)
|
152 | expect(isPending(thunk)).toBe(false)
|
153 | })
|
154 |
|
155 | test('should return true only for pending async thunk actions', () => {
|
156 | const thunk = createAsyncThunk<string>('a', () => 'result')
|
157 |
|
158 | const pendingAction = thunk.pending('fakeRequestId')
|
159 | expect(isPending()(pendingAction)).toBe(true)
|
160 | expect(isPending(pendingAction)).toBe(true)
|
161 |
|
162 | const rejectedAction = thunk.rejected(
|
163 | new Error('rejected'),
|
164 | 'fakeRequestId'
|
165 | )
|
166 | expect(isPending()(rejectedAction)).toBe(false)
|
167 |
|
168 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
169 | expect(isPending()(fulfilledAction)).toBe(false)
|
170 | })
|
171 |
|
172 | test('should return true only for thunks provided as arguments', () => {
|
173 | const thunkA = createAsyncThunk<string>('a', () => 'result')
|
174 | const thunkB = createAsyncThunk<string>('b', () => 'result')
|
175 | const thunkC = createAsyncThunk<string>('c', () => 'result')
|
176 |
|
177 | const matchAC = isPending(thunkA, thunkC)
|
178 | const matchB = isPending(thunkB)
|
179 |
|
180 | function testPendingAction(
|
181 | thunk: typeof thunkA | typeof thunkB | typeof thunkC,
|
182 | expected: boolean
|
183 | ) {
|
184 | const pendingAction = thunk.pending('fakeRequestId')
|
185 | expect(matchAC(pendingAction)).toBe(expected)
|
186 | expect(matchB(pendingAction)).toBe(!expected)
|
187 |
|
188 | const rejectedAction = thunk.rejected(
|
189 | new Error('rejected'),
|
190 | 'fakeRequestId'
|
191 | )
|
192 | expect(matchAC(rejectedAction)).toBe(false)
|
193 |
|
194 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
195 | expect(matchAC(fulfilledAction)).toBe(false)
|
196 | }
|
197 |
|
198 | testPendingAction(thunkA, true)
|
199 | testPendingAction(thunkC, true)
|
200 | testPendingAction(thunkB, false)
|
201 | })
|
202 | })
|
203 |
|
204 | describe('isRejected', () => {
|
205 | test('should return false for a regular action', () => {
|
206 | const action = createAction<string>('action/type')('testPayload')
|
207 |
|
208 | expect(isRejected()(action)).toBe(false)
|
209 | expect(isRejected(action)).toBe(false)
|
210 | expect(isRejected(thunk)).toBe(false)
|
211 | })
|
212 |
|
213 | test('should return true only for rejected async thunk actions', () => {
|
214 | const thunk = createAsyncThunk<string>('a', () => 'result')
|
215 |
|
216 | const pendingAction = thunk.pending('fakeRequestId')
|
217 | expect(isRejected()(pendingAction)).toBe(false)
|
218 |
|
219 | const rejectedAction = thunk.rejected(
|
220 | new Error('rejected'),
|
221 | 'fakeRequestId'
|
222 | )
|
223 | expect(isRejected()(rejectedAction)).toBe(true)
|
224 | expect(isRejected(rejectedAction)).toBe(true)
|
225 |
|
226 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
227 | expect(isRejected()(fulfilledAction)).toBe(false)
|
228 | })
|
229 |
|
230 | test('should return true only for thunks provided as arguments', () => {
|
231 | const thunkA = createAsyncThunk<string>('a', () => 'result')
|
232 | const thunkB = createAsyncThunk<string>('b', () => 'result')
|
233 | const thunkC = createAsyncThunk<string>('c', () => 'result')
|
234 |
|
235 | const matchAC = isRejected(thunkA, thunkC)
|
236 | const matchB = isRejected(thunkB)
|
237 |
|
238 | function testRejectedAction(
|
239 | thunk: typeof thunkA | typeof thunkB | typeof thunkC,
|
240 | expected: boolean
|
241 | ) {
|
242 | const pendingAction = thunk.pending('fakeRequestId')
|
243 | expect(matchAC(pendingAction)).toBe(false)
|
244 |
|
245 | const rejectedAction = thunk.rejected(
|
246 | new Error('rejected'),
|
247 | 'fakeRequestId'
|
248 | )
|
249 | expect(matchAC(rejectedAction)).toBe(expected)
|
250 | expect(matchB(rejectedAction)).toBe(!expected)
|
251 |
|
252 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
253 | expect(matchAC(fulfilledAction)).toBe(false)
|
254 | }
|
255 |
|
256 | testRejectedAction(thunkA, true)
|
257 | testRejectedAction(thunkC, true)
|
258 | testRejectedAction(thunkB, false)
|
259 | })
|
260 | })
|
261 |
|
262 | describe('isRejectedWithValue', () => {
|
263 | test('should return false for a regular action', () => {
|
264 | const action = createAction<string>('action/type')('testPayload')
|
265 |
|
266 | expect(isRejectedWithValue()(action)).toBe(false)
|
267 | expect(isRejectedWithValue(action)).toBe(false)
|
268 | expect(isRejectedWithValue(thunk)).toBe(false)
|
269 | })
|
270 |
|
271 | test('should return true only for rejected-with-value async thunk actions', async () => {
|
272 | const thunk = createAsyncThunk<string>('a', (_, { rejectWithValue }) => {
|
273 | return rejectWithValue('rejectWithValue!')
|
274 | })
|
275 |
|
276 | const pendingAction = thunk.pending('fakeRequestId')
|
277 | expect(isRejectedWithValue()(pendingAction)).toBe(false)
|
278 |
|
279 | const rejectedAction = thunk.rejected(
|
280 | new Error('rejected'),
|
281 | 'fakeRequestId'
|
282 | )
|
283 | expect(isRejectedWithValue()(rejectedAction)).toBe(false)
|
284 |
|
285 | const getState = jest.fn(() => ({}))
|
286 | const dispatch = jest.fn((x: any) => x)
|
287 | const extra = {}
|
288 |
|
289 |
|
290 | const rejectedWithValueAction = await thunk()(dispatch, getState, extra)
|
291 |
|
292 | expect(isRejectedWithValue()(rejectedWithValueAction)).toBe(true)
|
293 |
|
294 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
295 | expect(isRejectedWithValue()(fulfilledAction)).toBe(false)
|
296 | })
|
297 |
|
298 | test('should return true only for thunks provided as arguments', async () => {
|
299 | const payloadCreator = (_: any, { rejectWithValue }: any) => {
|
300 | return rejectWithValue('rejectWithValue!')
|
301 | }
|
302 |
|
303 | const thunkA = createAsyncThunk<string>('a', payloadCreator)
|
304 | const thunkB = createAsyncThunk<string>('b', payloadCreator)
|
305 | const thunkC = createAsyncThunk<string>('c', payloadCreator)
|
306 |
|
307 | const matchAC = isRejectedWithValue(thunkA, thunkC)
|
308 | const matchB = isRejectedWithValue(thunkB)
|
309 |
|
310 | async function testRejectedAction(
|
311 | thunk: typeof thunkA | typeof thunkB | typeof thunkC,
|
312 | expected: boolean
|
313 | ) {
|
314 | const pendingAction = thunk.pending('fakeRequestId')
|
315 | expect(matchAC(pendingAction)).toBe(false)
|
316 |
|
317 | const rejectedAction = thunk.rejected(
|
318 | new Error('rejected'),
|
319 | 'fakeRequestId'
|
320 | )
|
321 |
|
322 | expect(matchAC(rejectedAction)).toBe(false)
|
323 |
|
324 | const getState = jest.fn(() => ({}))
|
325 | const dispatch = jest.fn((x: any) => x)
|
326 | const extra = {}
|
327 |
|
328 |
|
329 | const rejectedWithValueAction = await thunk()(dispatch, getState, extra)
|
330 |
|
331 | expect(matchAC(rejectedWithValueAction)).toBe(expected)
|
332 | expect(matchB(rejectedWithValueAction)).toBe(!expected)
|
333 |
|
334 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
335 | expect(matchAC(fulfilledAction)).toBe(false)
|
336 | }
|
337 |
|
338 | await testRejectedAction(thunkA, true)
|
339 | await testRejectedAction(thunkC, true)
|
340 | await testRejectedAction(thunkB, false)
|
341 | })
|
342 | })
|
343 |
|
344 | describe('isFulfilled', () => {
|
345 | test('should return false for a regular action', () => {
|
346 | const action = createAction<string>('action/type')('testPayload')
|
347 |
|
348 | expect(isFulfilled()(action)).toBe(false)
|
349 | expect(isFulfilled(action)).toBe(false)
|
350 | expect(isFulfilled(thunk)).toBe(false)
|
351 | })
|
352 |
|
353 | test('should return true only for fulfilled async thunk actions', () => {
|
354 | const thunk = createAsyncThunk<string>('a', () => 'result')
|
355 |
|
356 | const pendingAction = thunk.pending('fakeRequestId')
|
357 | expect(isFulfilled()(pendingAction)).toBe(false)
|
358 |
|
359 | const rejectedAction = thunk.rejected(
|
360 | new Error('rejected'),
|
361 | 'fakeRequestId'
|
362 | )
|
363 | expect(isFulfilled()(rejectedAction)).toBe(false)
|
364 |
|
365 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
366 | expect(isFulfilled()(fulfilledAction)).toBe(true)
|
367 | expect(isFulfilled(fulfilledAction)).toBe(true)
|
368 | })
|
369 |
|
370 | test('should return true only for thunks provided as arguments', () => {
|
371 | const thunkA = createAsyncThunk<string>('a', () => 'result')
|
372 | const thunkB = createAsyncThunk<string>('b', () => 'result')
|
373 | const thunkC = createAsyncThunk<string>('c', () => 'result')
|
374 |
|
375 | const matchAC = isFulfilled(thunkA, thunkC)
|
376 | const matchB = isFulfilled(thunkB)
|
377 |
|
378 | function testFulfilledAction(
|
379 | thunk: typeof thunkA | typeof thunkB | typeof thunkC,
|
380 | expected: boolean
|
381 | ) {
|
382 | const pendingAction = thunk.pending('fakeRequestId')
|
383 | expect(matchAC(pendingAction)).toBe(false)
|
384 |
|
385 | const rejectedAction = thunk.rejected(
|
386 | new Error('rejected'),
|
387 | 'fakeRequestId'
|
388 | )
|
389 | expect(matchAC(rejectedAction)).toBe(false)
|
390 |
|
391 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
392 | expect(matchAC(fulfilledAction)).toBe(expected)
|
393 | expect(matchB(fulfilledAction)).toBe(!expected)
|
394 | }
|
395 |
|
396 | testFulfilledAction(thunkA, true)
|
397 | testFulfilledAction(thunkC, true)
|
398 | testFulfilledAction(thunkB, false)
|
399 | })
|
400 | })
|
401 |
|
402 | describe('isAsyncThunkAction', () => {
|
403 | test('should return false for a regular action', () => {
|
404 | const action = createAction<string>('action/type')('testPayload')
|
405 |
|
406 | expect(isAsyncThunkAction()(action)).toBe(false)
|
407 | expect(isAsyncThunkAction(action)).toBe(false)
|
408 | expect(isAsyncThunkAction(thunk)).toBe(false)
|
409 | })
|
410 |
|
411 | test('should return true for any async thunk action if no arguments were provided', () => {
|
412 | const thunk = createAsyncThunk<string>('a', () => 'result')
|
413 | const matcher = isAsyncThunkAction()
|
414 |
|
415 | const pendingAction = thunk.pending('fakeRequestId')
|
416 | expect(matcher(pendingAction)).toBe(true)
|
417 |
|
418 | const rejectedAction = thunk.rejected(
|
419 | new Error('rejected'),
|
420 | 'fakeRequestId'
|
421 | )
|
422 | expect(matcher(rejectedAction)).toBe(true)
|
423 |
|
424 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
425 | expect(matcher(fulfilledAction)).toBe(true)
|
426 | })
|
427 |
|
428 | test('should return true only for thunks provided as arguments', () => {
|
429 | const thunkA = createAsyncThunk<string>('a', () => 'result')
|
430 | const thunkB = createAsyncThunk<string>('b', () => 'result')
|
431 | const thunkC = createAsyncThunk<string>('c', () => 'result')
|
432 |
|
433 | const matchAC = isAsyncThunkAction(thunkA, thunkC)
|
434 | const matchB = isAsyncThunkAction(thunkB)
|
435 |
|
436 | function testAllActions(
|
437 | thunk: typeof thunkA | typeof thunkB | typeof thunkC,
|
438 | expected: boolean
|
439 | ) {
|
440 | const pendingAction = thunk.pending('fakeRequestId')
|
441 | expect(matchAC(pendingAction)).toBe(expected)
|
442 | expect(matchB(pendingAction)).toBe(!expected)
|
443 |
|
444 | const rejectedAction = thunk.rejected(
|
445 | new Error('rejected'),
|
446 | 'fakeRequestId'
|
447 | )
|
448 | expect(matchAC(rejectedAction)).toBe(expected)
|
449 | expect(matchB(rejectedAction)).toBe(!expected)
|
450 |
|
451 | const fulfilledAction = thunk.fulfilled('result', 'fakeRequestId')
|
452 | expect(matchAC(fulfilledAction)).toBe(expected)
|
453 | expect(matchB(fulfilledAction)).toBe(!expected)
|
454 | }
|
455 |
|
456 | testAllActions(thunkA, true)
|
457 | testAllActions(thunkC, true)
|
458 | testAllActions(thunkB, false)
|
459 | })
|
460 | })
|
461 |
|
\ | No newline at end of file |