1 | import {
|
2 | createAsyncThunk,
|
3 | miniSerializeError,
|
4 | unwrapResult
|
5 | } from './createAsyncThunk'
|
6 | import { configureStore } from './configureStore'
|
7 | import { AnyAction } from 'redux'
|
8 |
|
9 | import {
|
10 | mockConsole,
|
11 | createConsole,
|
12 | getLog
|
13 | } from 'console-testing-library/pure'
|
14 |
|
15 | declare global {
|
16 | interface Window {
|
17 | AbortController: AbortController
|
18 | }
|
19 | }
|
20 |
|
21 | describe('createAsyncThunk', () => {
|
22 | it('creates the action types', () => {
|
23 | const thunkActionCreator = createAsyncThunk('testType', async () => 42)
|
24 |
|
25 | expect(thunkActionCreator.fulfilled.type).toBe('testType/fulfilled')
|
26 | expect(thunkActionCreator.pending.type).toBe('testType/pending')
|
27 | expect(thunkActionCreator.rejected.type).toBe('testType/rejected')
|
28 | })
|
29 |
|
30 | it('exposes the typePrefix it was created with', () => {
|
31 | const thunkActionCreator = createAsyncThunk('testType', async () => 42)
|
32 |
|
33 | expect(thunkActionCreator.typePrefix).toBe('testType')
|
34 | })
|
35 |
|
36 | it('works without passing arguments to the payload creator', async () => {
|
37 | const thunkActionCreator = createAsyncThunk('testType', async () => 42)
|
38 |
|
39 | let timesReducerCalled = 0
|
40 |
|
41 | const reducer = () => {
|
42 | timesReducerCalled++
|
43 | }
|
44 |
|
45 | const store = configureStore({
|
46 | reducer
|
47 | })
|
48 |
|
49 |
|
50 | timesReducerCalled = 0
|
51 |
|
52 | await store.dispatch(thunkActionCreator())
|
53 |
|
54 | expect(timesReducerCalled).toBe(2)
|
55 | })
|
56 |
|
57 | it('accepts arguments and dispatches the actions on resolve', async () => {
|
58 | const dispatch = jest.fn()
|
59 |
|
60 | let passedArg: any
|
61 |
|
62 | const result = 42
|
63 | const args = 123
|
64 | let generatedRequestId = ''
|
65 |
|
66 | const thunkActionCreator = createAsyncThunk(
|
67 | 'testType',
|
68 | async (arg: number, { requestId }) => {
|
69 | passedArg = arg
|
70 | generatedRequestId = requestId
|
71 | return result
|
72 | }
|
73 | )
|
74 |
|
75 | const thunkFunction = thunkActionCreator(args)
|
76 |
|
77 | const thunkPromise = thunkFunction(dispatch, () => {}, undefined)
|
78 |
|
79 | expect(thunkPromise.requestId).toBe(generatedRequestId)
|
80 | expect(thunkPromise.arg).toBe(args)
|
81 |
|
82 | await thunkPromise
|
83 |
|
84 | expect(passedArg).toBe(args)
|
85 |
|
86 | expect(dispatch).toHaveBeenNthCalledWith(
|
87 | 1,
|
88 | thunkActionCreator.pending(generatedRequestId, args)
|
89 | )
|
90 |
|
91 | expect(dispatch).toHaveBeenNthCalledWith(
|
92 | 2,
|
93 | thunkActionCreator.fulfilled(result, generatedRequestId, args)
|
94 | )
|
95 | })
|
96 |
|
97 | it('accepts arguments and dispatches the actions on reject', async () => {
|
98 | const dispatch = jest.fn()
|
99 |
|
100 | const args = 123
|
101 | let generatedRequestId = ''
|
102 |
|
103 | const error = new Error('Panic!')
|
104 |
|
105 | const thunkActionCreator = createAsyncThunk(
|
106 | 'testType',
|
107 | async (args: number, { requestId }) => {
|
108 | generatedRequestId = requestId
|
109 | throw error
|
110 | }
|
111 | )
|
112 |
|
113 | const thunkFunction = thunkActionCreator(args)
|
114 |
|
115 | try {
|
116 | await thunkFunction(dispatch, () => {}, undefined)
|
117 | } catch (e) {}
|
118 |
|
119 | expect(dispatch).toHaveBeenNthCalledWith(
|
120 | 1,
|
121 | thunkActionCreator.pending(generatedRequestId, args)
|
122 | )
|
123 |
|
124 | expect(dispatch).toHaveBeenCalledTimes(2)
|
125 |
|
126 |
|
127 | const errorAction = dispatch.mock.calls[1][0]
|
128 | expect(errorAction.error).toEqual(miniSerializeError(error))
|
129 | expect(errorAction.meta.requestId).toBe(generatedRequestId)
|
130 | expect(errorAction.meta.arg).toBe(args)
|
131 | })
|
132 |
|
133 | it('dispatches an empty error when throwing a random object without serializedError properties', async () => {
|
134 | const dispatch = jest.fn()
|
135 |
|
136 | const args = 123
|
137 | let generatedRequestId = ''
|
138 |
|
139 | const errorObject = { wny: 'dothis' }
|
140 |
|
141 | const thunkActionCreator = createAsyncThunk(
|
142 | 'testType',
|
143 | async (args: number, { requestId }) => {
|
144 | generatedRequestId = requestId
|
145 | throw errorObject
|
146 | }
|
147 | )
|
148 |
|
149 | const thunkFunction = thunkActionCreator(args)
|
150 |
|
151 | try {
|
152 | await thunkFunction(dispatch, () => {}, undefined)
|
153 | } catch (e) {}
|
154 |
|
155 | expect(dispatch).toHaveBeenNthCalledWith(
|
156 | 1,
|
157 | thunkActionCreator.pending(generatedRequestId, args)
|
158 | )
|
159 |
|
160 | expect(dispatch).toHaveBeenCalledTimes(2)
|
161 |
|
162 | const errorAction = dispatch.mock.calls[1][0]
|
163 | expect(errorAction.error).toEqual({})
|
164 | expect(errorAction.meta.requestId).toBe(generatedRequestId)
|
165 | expect(errorAction.meta.arg).toBe(args)
|
166 | })
|
167 |
|
168 | it('dispatches an action with a formatted error when throwing an object with known error keys', async () => {
|
169 | const dispatch = jest.fn()
|
170 |
|
171 | const args = 123
|
172 | let generatedRequestId = ''
|
173 |
|
174 | const errorObject = {
|
175 | name: 'Custom thrown error',
|
176 | message: 'This is not necessary',
|
177 | code: '400'
|
178 | }
|
179 |
|
180 | const thunkActionCreator = createAsyncThunk(
|
181 | 'testType',
|
182 | async (args: number, { requestId }) => {
|
183 | generatedRequestId = requestId
|
184 | throw errorObject
|
185 | }
|
186 | )
|
187 |
|
188 | const thunkFunction = thunkActionCreator(args)
|
189 |
|
190 | try {
|
191 | await thunkFunction(dispatch, () => {}, undefined)
|
192 | } catch (e) {}
|
193 |
|
194 | expect(dispatch).toHaveBeenNthCalledWith(
|
195 | 1,
|
196 | thunkActionCreator.pending(generatedRequestId, args)
|
197 | )
|
198 |
|
199 | expect(dispatch).toHaveBeenCalledTimes(2)
|
200 |
|
201 |
|
202 | const errorAction = dispatch.mock.calls[1][0]
|
203 | expect(errorAction.error).toEqual(miniSerializeError(errorObject))
|
204 | expect(Object.keys(errorAction.error)).not.toContain('stack')
|
205 | expect(errorAction.meta.requestId).toBe(generatedRequestId)
|
206 | expect(errorAction.meta.arg).toBe(args)
|
207 | })
|
208 |
|
209 | it('dispatches a rejected action with a customized payload when a user returns rejectWithValue()', async () => {
|
210 | const dispatch = jest.fn()
|
211 |
|
212 | const args = 123
|
213 | let generatedRequestId = ''
|
214 |
|
215 | const errorPayload = {
|
216 | errorMessage:
|
217 | 'I am a fake server-provided 400 payload with validation details',
|
218 | errors: [
|
219 | { field_one: 'Must be a string' },
|
220 | { field_two: 'Must be a number' }
|
221 | ]
|
222 | }
|
223 |
|
224 | const thunkActionCreator = createAsyncThunk(
|
225 | 'testType',
|
226 | async (args: number, { requestId, rejectWithValue }) => {
|
227 | generatedRequestId = requestId
|
228 |
|
229 | return rejectWithValue(errorPayload)
|
230 | }
|
231 | )
|
232 |
|
233 | const thunkFunction = thunkActionCreator(args)
|
234 |
|
235 | try {
|
236 | await thunkFunction(dispatch, () => {}, undefined)
|
237 | } catch (e) {}
|
238 |
|
239 | expect(dispatch).toHaveBeenNthCalledWith(
|
240 | 1,
|
241 | thunkActionCreator.pending(generatedRequestId, args)
|
242 | )
|
243 |
|
244 | expect(dispatch).toHaveBeenCalledTimes(2)
|
245 |
|
246 |
|
247 | const errorAction = dispatch.mock.calls[1][0]
|
248 |
|
249 | expect(errorAction.error.message).toEqual('Rejected')
|
250 | expect(errorAction.payload).toBe(errorPayload)
|
251 | expect(errorAction.meta.arg).toBe(args)
|
252 | })
|
253 |
|
254 | it('dispatches a rejected action with a miniSerializeError when rejectWithValue conditions are not satisfied', async () => {
|
255 | const dispatch = jest.fn()
|
256 |
|
257 | const args = 123
|
258 | let generatedRequestId = ''
|
259 |
|
260 | const error = new Error('Panic!')
|
261 |
|
262 | const errorPayload = {
|
263 | errorMessage:
|
264 | 'I am a fake server-provided 400 payload with validation details',
|
265 | errors: [
|
266 | { field_one: 'Must be a string' },
|
267 | { field_two: 'Must be a number' }
|
268 | ]
|
269 | }
|
270 |
|
271 | const thunkActionCreator = createAsyncThunk(
|
272 | 'testType',
|
273 | async (args: number, { requestId, rejectWithValue }) => {
|
274 | generatedRequestId = requestId
|
275 |
|
276 | try {
|
277 | throw error
|
278 | } catch (err) {
|
279 | if (!err.response) {
|
280 | throw err
|
281 | }
|
282 | return rejectWithValue(errorPayload)
|
283 | }
|
284 | }
|
285 | )
|
286 |
|
287 | const thunkFunction = thunkActionCreator(args)
|
288 |
|
289 | try {
|
290 | await thunkFunction(dispatch, () => {}, undefined)
|
291 | } catch (e) {}
|
292 |
|
293 | expect(dispatch).toHaveBeenNthCalledWith(
|
294 | 1,
|
295 | thunkActionCreator.pending(generatedRequestId, args)
|
296 | )
|
297 |
|
298 | expect(dispatch).toHaveBeenCalledTimes(2)
|
299 |
|
300 |
|
301 | const errorAction = dispatch.mock.calls[1][0]
|
302 | expect(errorAction.error).toEqual(miniSerializeError(error))
|
303 | expect(errorAction.payload).toEqual(undefined)
|
304 | expect(errorAction.meta.requestId).toBe(generatedRequestId)
|
305 | expect(errorAction.meta.arg).toBe(args)
|
306 | })
|
307 | })
|
308 |
|
309 | describe('createAsyncThunk with abortController', () => {
|
310 | const asyncThunk = createAsyncThunk('test', function abortablePayloadCreator(
|
311 | _: any,
|
312 | { signal }
|
313 | ) {
|
314 | return new Promise((resolve, reject) => {
|
315 | if (signal.aborted) {
|
316 | reject(
|
317 | new DOMException(
|
318 | 'This should never be reached as it should already be handled.',
|
319 | 'AbortError'
|
320 | )
|
321 | )
|
322 | }
|
323 | signal.addEventListener('abort', () => {
|
324 | reject(new DOMException('Was aborted while running', 'AbortError'))
|
325 | })
|
326 | setTimeout(resolve, 100)
|
327 | })
|
328 | })
|
329 |
|
330 | let store = configureStore({
|
331 | reducer(store: AnyAction[] = []) {
|
332 | return store
|
333 | }
|
334 | })
|
335 |
|
336 | beforeEach(() => {
|
337 | store = configureStore({
|
338 | reducer(store: AnyAction[] = [], action) {
|
339 | return [...store, action]
|
340 | }
|
341 | })
|
342 | })
|
343 |
|
344 | test('normal usage', async () => {
|
345 | await store.dispatch(asyncThunk({}))
|
346 | expect(store.getState()).toEqual([
|
347 | expect.any(Object),
|
348 | expect.objectContaining({ type: 'test/pending' }),
|
349 | expect.objectContaining({ type: 'test/fulfilled' })
|
350 | ])
|
351 | })
|
352 |
|
353 | test('abort after dispatch', async () => {
|
354 | const promise = store.dispatch(asyncThunk({}))
|
355 | promise.abort('AbortReason')
|
356 | const result = await promise
|
357 | const expectedAbortedAction = {
|
358 | type: 'test/rejected',
|
359 | error: {
|
360 | message: 'AbortReason',
|
361 | name: 'AbortError'
|
362 | },
|
363 | meta: { aborted: true, requestId: promise.requestId }
|
364 | }
|
365 |
|
366 |
|
367 | expect(store.getState()).toMatchObject([
|
368 | {},
|
369 | { type: 'test/pending' },
|
370 | expectedAbortedAction
|
371 | ])
|
372 |
|
373 |
|
374 | expect(result).toMatchObject(expectedAbortedAction)
|
375 |
|
376 |
|
377 | expect(() => unwrapResult(result)).toThrowError(
|
378 | expect.objectContaining(expectedAbortedAction.error)
|
379 | )
|
380 | })
|
381 |
|
382 | test('even when the payloadCreator does not directly support the signal, no further actions are dispatched', async () => {
|
383 | const unawareAsyncThunk = createAsyncThunk('unaware', async () => {
|
384 | await new Promise(resolve => setTimeout(resolve, 100))
|
385 | return 'finished'
|
386 | })
|
387 |
|
388 | const promise = store.dispatch(unawareAsyncThunk())
|
389 | promise.abort('AbortReason')
|
390 | const result = await promise
|
391 |
|
392 | const expectedAbortedAction = {
|
393 | type: 'unaware/rejected',
|
394 | error: {
|
395 | message: 'AbortReason',
|
396 | name: 'AbortError'
|
397 | }
|
398 | }
|
399 |
|
400 |
|
401 | expect(store.getState()).toEqual([
|
402 | expect.any(Object),
|
403 | expect.objectContaining({ type: 'unaware/pending' }),
|
404 | expect.objectContaining(expectedAbortedAction)
|
405 | ])
|
406 |
|
407 |
|
408 | expect(result).toMatchObject(expectedAbortedAction)
|
409 |
|
410 |
|
411 | expect(() => unwrapResult(result)).toThrowError(
|
412 | expect.objectContaining(expectedAbortedAction.error)
|
413 | )
|
414 | })
|
415 |
|
416 | test('dispatch(asyncThunk) returns on abort and does not wait for the promiseProvider to finish', async () => {
|
417 | let running = false
|
418 | const longRunningAsyncThunk = createAsyncThunk('longRunning', async () => {
|
419 | running = true
|
420 | await new Promise(resolve => setTimeout(resolve, 30000))
|
421 | running = false
|
422 | })
|
423 |
|
424 | const promise = store.dispatch(longRunningAsyncThunk())
|
425 | expect(running).toBeTruthy()
|
426 | promise.abort()
|
427 | const result = await promise
|
428 | expect(running).toBeTruthy()
|
429 | expect(result).toMatchObject({
|
430 | type: 'longRunning/rejected',
|
431 | error: { message: 'Aborted', name: 'AbortError' },
|
432 | meta: { aborted: true }
|
433 | })
|
434 | })
|
435 |
|
436 | describe('behaviour with missing AbortController', () => {
|
437 | let keepAbortController: typeof window['AbortController']
|
438 | let freshlyLoadedModule: typeof import('./createAsyncThunk')
|
439 | let restore: () => void
|
440 | let nodeEnv: string
|
441 |
|
442 | beforeEach(() => {
|
443 | keepAbortController = window.AbortController
|
444 | delete (window as any).AbortController
|
445 | jest.resetModules()
|
446 | freshlyLoadedModule = require('./createAsyncThunk')
|
447 | restore = mockConsole(createConsole())
|
448 | nodeEnv = process.env.NODE_ENV!
|
449 | process.env.NODE_ENV = 'development'
|
450 | })
|
451 |
|
452 | afterEach(() => {
|
453 | process.env.NODE_ENV = nodeEnv
|
454 | restore()
|
455 | window.AbortController = keepAbortController
|
456 | jest.resetModules()
|
457 | })
|
458 |
|
459 | test('calling `abort` on an asyncThunk works with a FallbackAbortController if no global abortController is not available', async () => {
|
460 | const longRunningAsyncThunk = freshlyLoadedModule.createAsyncThunk(
|
461 | 'longRunning',
|
462 | async () => {
|
463 | await new Promise(resolve => setTimeout(resolve, 30000))
|
464 | }
|
465 | )
|
466 |
|
467 | store.dispatch(longRunningAsyncThunk()).abort()
|
468 |
|
469 | store.dispatch(longRunningAsyncThunk()).abort()
|
470 |
|
471 | expect(getLog().log).toMatchInlineSnapshot(`
|
472 | "This platform does not implement AbortController.
|
473 | If you want to use the AbortController to react to \`abort\` events, please consider importing a polyfill like 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'."
|
474 | `)
|
475 | })
|
476 | })
|
477 | })
|
478 |
|
479 | test('non-serializable arguments are ignored by serializableStateInvariantMiddleware', async () => {
|
480 | const restore = mockConsole(createConsole())
|
481 | const nonSerializableValue = new Map()
|
482 | const asyncThunk = createAsyncThunk('test', (arg: Map<any, any>) => {})
|
483 |
|
484 | configureStore({
|
485 | reducer: () => 0
|
486 | }).dispatch(asyncThunk(nonSerializableValue))
|
487 |
|
488 | expect(getLog().log).toMatchInlineSnapshot(`""`)
|
489 | restore()
|
490 | })
|
491 |
|
492 | describe('conditional skipping of asyncThunks', () => {
|
493 | const arg = {}
|
494 | const getState = jest.fn(() => ({}))
|
495 | const dispatch = jest.fn((x: any) => x)
|
496 | const payloadCreator = jest.fn((x: typeof arg) => 10)
|
497 | const condition = jest.fn(() => false)
|
498 | const extra = {}
|
499 |
|
500 | beforeEach(() => {
|
501 | getState.mockClear()
|
502 | dispatch.mockClear()
|
503 | payloadCreator.mockClear()
|
504 | condition.mockClear()
|
505 | })
|
506 |
|
507 | test('returning false from condition skips payloadCreator and returns a rejected action', async () => {
|
508 | const asyncThunk = createAsyncThunk('test', payloadCreator, { condition })
|
509 | const result = await asyncThunk(arg)(dispatch, getState, extra)
|
510 |
|
511 | expect(condition).toHaveBeenCalled()
|
512 | expect(payloadCreator).not.toHaveBeenCalled()
|
513 | expect(asyncThunk.rejected.match(result)).toBe(true)
|
514 | expect((result as any).meta.condition).toBe(true)
|
515 | })
|
516 |
|
517 | test('return falsy from condition does not skip payload creator', async () => {
|
518 |
|
519 | condition.mockReturnValueOnce((undefined as unknown) as boolean)
|
520 | const asyncThunk = createAsyncThunk('test', payloadCreator, { condition })
|
521 | const result = await asyncThunk(arg)(dispatch, getState, extra)
|
522 |
|
523 | expect(condition).toHaveBeenCalled()
|
524 | expect(payloadCreator).toHaveBeenCalled()
|
525 | expect(asyncThunk.fulfilled.match(result)).toBe(true)
|
526 | expect(result.payload).toBe(10)
|
527 | })
|
528 |
|
529 | test('returning true from condition executes payloadCreator', async () => {
|
530 | condition.mockReturnValueOnce(true)
|
531 | const asyncThunk = createAsyncThunk('test', payloadCreator, { condition })
|
532 | const result = await asyncThunk(arg)(dispatch, getState, extra)
|
533 |
|
534 | expect(condition).toHaveBeenCalled()
|
535 | expect(payloadCreator).toHaveBeenCalled()
|
536 | expect(asyncThunk.fulfilled.match(result)).toBe(true)
|
537 | expect(result.payload).toBe(10)
|
538 | })
|
539 |
|
540 | test('condition is called with arg, getState and extra', async () => {
|
541 | const asyncThunk = createAsyncThunk('test', payloadCreator, { condition })
|
542 | await asyncThunk(arg)(dispatch, getState, extra)
|
543 |
|
544 | expect(condition).toHaveBeenCalledTimes(1)
|
545 | expect(condition).toHaveBeenLastCalledWith(
|
546 | arg,
|
547 | expect.objectContaining({ getState, extra })
|
548 | )
|
549 | })
|
550 |
|
551 | test('rejected action is not dispatched by default', async () => {
|
552 | const asyncThunk = createAsyncThunk('test', payloadCreator, { condition })
|
553 | await asyncThunk(arg)(dispatch, getState, extra)
|
554 |
|
555 | expect(dispatch).toHaveBeenCalledTimes(0)
|
556 | })
|
557 |
|
558 | test('does not fail when attempting to abort a canceled promise', async () => {
|
559 | const asyncPayloadCreator = jest.fn(async (x: typeof arg) => {
|
560 | await new Promise(resolve => setTimeout(resolve, 2000))
|
561 | return 10
|
562 | })
|
563 |
|
564 | const asyncThunk = createAsyncThunk('test', asyncPayloadCreator, {
|
565 | condition
|
566 | })
|
567 | const promise = asyncThunk(arg)(dispatch, getState, extra)
|
568 | promise.abort(
|
569 | `If the promise was 1. somehow canceled, 2. in a 'started' state and 3. we attempted to abort, this would crash the tests`
|
570 | )
|
571 | })
|
572 |
|
573 | test('rejected action can be dispatched via option', async () => {
|
574 | const asyncThunk = createAsyncThunk('test', payloadCreator, {
|
575 | condition,
|
576 | dispatchConditionRejection: true
|
577 | })
|
578 | await asyncThunk(arg)(dispatch, getState, extra)
|
579 |
|
580 | expect(dispatch).toHaveBeenCalledTimes(1)
|
581 | expect(dispatch).toHaveBeenLastCalledWith(
|
582 | expect.objectContaining({
|
583 | error: {
|
584 | message: 'Aborted due to condition callback returning false.',
|
585 | name: 'ConditionError'
|
586 | },
|
587 | meta: {
|
588 | aborted: false,
|
589 | arg: arg,
|
590 | rejectedWithValue: false,
|
591 | condition: true,
|
592 | requestId: expect.stringContaining(''),
|
593 | requestStatus: 'rejected'
|
594 | },
|
595 | payload: undefined,
|
596 | type: 'test/rejected'
|
597 | })
|
598 | )
|
599 | })
|
600 |
|
601 | test('serializeError implementation', async () => {
|
602 | function serializeError() {
|
603 | return 'serialized!'
|
604 | }
|
605 | const errorObject = 'something else!'
|
606 |
|
607 | const store = configureStore({
|
608 | reducer: (state = [], action) => [...state, action]
|
609 | })
|
610 |
|
611 | const asyncThunk = createAsyncThunk<
|
612 | unknown,
|
613 | void,
|
614 | { serializedErrorType: string }
|
615 | >('test', () => Promise.reject(errorObject), { serializeError })
|
616 | const rejected = await store.dispatch(asyncThunk())
|
617 | if (!asyncThunk.rejected.match(rejected)) {
|
618 | throw new Error()
|
619 | }
|
620 |
|
621 | const expectation = {
|
622 | type: 'test/rejected',
|
623 | payload: undefined,
|
624 | error: 'serialized!',
|
625 | meta: expect.any(Object)
|
626 | }
|
627 | expect(rejected).toEqual(expectation)
|
628 | expect(store.getState()[2]).toEqual(expectation)
|
629 | expect(rejected.error).not.toEqual(miniSerializeError(errorObject))
|
630 | })
|
631 | })
|
632 | describe('unwrapResult', () => {
|
633 | const getState = jest.fn(() => ({}))
|
634 | const dispatch = jest.fn((x: any) => x)
|
635 | const extra = {}
|
636 | test('fulfilled case', async () => {
|
637 | const asyncThunk = createAsyncThunk('test', () => {
|
638 | return 'fulfilled!'
|
639 | })
|
640 |
|
641 | const unwrapPromise = asyncThunk()(dispatch, getState, extra).then(
|
642 | unwrapResult
|
643 | )
|
644 |
|
645 | await expect(unwrapPromise).resolves.toBe('fulfilled!')
|
646 | })
|
647 | test('error case', async () => {
|
648 | const error = new Error('Panic!')
|
649 | const asyncThunk = createAsyncThunk('test', () => {
|
650 | throw error
|
651 | })
|
652 |
|
653 | const unwrapPromise = asyncThunk()(dispatch, getState, extra).then(
|
654 | unwrapResult
|
655 | )
|
656 |
|
657 | await expect(unwrapPromise).rejects.toEqual(miniSerializeError(error))
|
658 | })
|
659 | test('rejectWithValue case', async () => {
|
660 | const asyncThunk = createAsyncThunk('test', (_, { rejectWithValue }) => {
|
661 | return rejectWithValue('rejectWithValue!')
|
662 | })
|
663 |
|
664 | const unwrapPromise = asyncThunk()(dispatch, getState, extra).then(
|
665 | unwrapResult
|
666 | )
|
667 |
|
668 | await expect(unwrapPromise).rejects.toBe('rejectWithValue!')
|
669 | })
|
670 | })
|
671 |
|
\ | No newline at end of file |