UNPKG

14.8 kBPlain TextView Raw
1import type { ThunkAction, AnyAction } from '@reduxjs/toolkit'
2import {
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
15const thunk: ThunkAction<any, any, any, AnyAction> = () => {}
16
17describe('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
104describe('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
146describe('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
204describe('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
262describe('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 // note: doesn't throw because we don't unwrap it
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 // rejected-with-value is a narrower requirement than rejected
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 // note: doesn't throw because we don't unwrap it
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
344describe('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
402describe('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