@@ -9,6 +9,7 @@ export const resolveTo = resolveIn(0)
9
9
export const rejectIn = ms => err =>
10
10
new Promise ( ( resolve , reject ) => setTimeout ( reject , ms , new Error ( err ) ) )
11
11
export const rejectTo = rejectIn ( 0 )
12
+ export const sleep = ms => resolveIn ( ms ) ( )
12
13
13
14
export const common = Async => ( ) => {
14
15
test ( "passes `data`, `error`, metadata and methods as render props" , async ( ) => {
@@ -99,22 +100,22 @@ export const withPromise = Async => () => {
99
100
test ( "invokes `onResolve` callback when the promise resolves" , async ( ) => {
100
101
const onResolve = jest . fn ( )
101
102
render ( < Async promise = { resolveTo ( "ok" ) } onResolve = { onResolve } /> )
102
- await resolveTo ( )
103
+ await sleep ( 10 )
103
104
expect ( onResolve ) . toHaveBeenCalledWith ( "ok" )
104
105
} )
105
106
106
107
test ( "invokes `onReject` callback when the promise rejects" , async ( ) => {
107
108
const onReject = jest . fn ( )
108
109
render ( < Async promise = { rejectTo ( "err" ) } onReject = { onReject } /> )
109
- await resolveTo ( )
110
+ await sleep ( 10 )
110
111
expect ( onReject ) . toHaveBeenCalledWith ( new Error ( "err" ) )
111
112
} )
112
113
113
114
test ( "cancels a pending promise when unmounted" , async ( ) => {
114
115
const onResolve = jest . fn ( )
115
116
const { unmount } = render ( < Async promise = { resolveTo ( "ok" ) } onResolve = { onResolve } /> )
116
117
unmount ( )
117
- await resolveTo ( )
118
+ await sleep ( 10 )
118
119
expect ( onResolve ) . not . toHaveBeenCalled ( )
119
120
} )
120
121
@@ -124,7 +125,7 @@ export const withPromise = Async => () => {
124
125
const onResolve = jest . fn ( )
125
126
const { rerender } = render ( < Async promise = { promise1 } onResolve = { onResolve } /> )
126
127
rerender ( < Async promise = { promise2 } onResolve = { onResolve } /> )
127
- await resolveTo ( )
128
+ await sleep ( 10 )
128
129
expect ( onResolve ) . not . toHaveBeenCalledWith ( "one" )
129
130
expect ( onResolve ) . toHaveBeenCalledWith ( "two" )
130
131
} )
@@ -133,7 +134,7 @@ export const withPromise = Async => () => {
133
134
const onResolve = jest . fn ( )
134
135
const { rerender } = render ( < Async promise = { resolveTo ( ) } onResolve = { onResolve } /> )
135
136
rerender ( < Async onResolve = { onResolve } /> )
136
- await resolveTo ( )
137
+ await sleep ( 10 )
137
138
expect ( onResolve ) . not . toHaveBeenCalled ( )
138
139
} )
139
140
@@ -191,14 +192,14 @@ export const withPromiseFn = (Async, abortCtrl) => () => {
191
192
test ( "invokes `onResolve` callback when the promise resolves" , async ( ) => {
192
193
const onResolve = jest . fn ( )
193
194
render ( < Async promiseFn = { ( ) => resolveTo ( "ok" ) } onResolve = { onResolve } /> )
194
- await resolveTo ( )
195
+ await sleep ( 10 )
195
196
expect ( onResolve ) . toHaveBeenCalledWith ( "ok" )
196
197
} )
197
198
198
199
test ( "invokes `onReject` callback when the promise rejects" , async ( ) => {
199
200
const onReject = jest . fn ( )
200
201
render ( < Async promiseFn = { ( ) => rejectTo ( "err" ) } onReject = { onReject } /> )
201
- await resolveTo ( )
202
+ await sleep ( 10 )
202
203
expect ( onReject ) . toHaveBeenCalledWith ( new Error ( "err" ) )
203
204
} )
204
205
@@ -280,7 +281,7 @@ export const withPromiseFn = (Async, abortCtrl) => () => {
280
281
const onResolve = jest . fn ( )
281
282
const { unmount } = render ( < Async promiseFn = { ( ) => resolveTo ( "ok" ) } onResolve = { onResolve } /> )
282
283
unmount ( )
283
- await resolveTo ( )
284
+ await sleep ( 10 )
284
285
expect ( onResolve ) . not . toHaveBeenCalled ( )
285
286
expect ( abortCtrl . abort ) . toHaveBeenCalledTimes ( 1 )
286
287
} )
@@ -291,7 +292,7 @@ export const withPromiseFn = (Async, abortCtrl) => () => {
291
292
const onResolve = jest . fn ( )
292
293
const { rerender } = render ( < Async promiseFn = { promiseFn1 } onResolve = { onResolve } /> )
293
294
rerender ( < Async promiseFn = { promiseFn2 } onResolve = { onResolve } /> )
294
- await resolveTo ( )
295
+ await sleep ( 10 )
295
296
expect ( onResolve ) . not . toHaveBeenCalledWith ( "one" )
296
297
expect ( onResolve ) . toHaveBeenCalledWith ( "two" )
297
298
expect ( abortCtrl . abort ) . toHaveBeenCalledTimes ( 1 )
@@ -301,15 +302,15 @@ export const withPromiseFn = (Async, abortCtrl) => () => {
301
302
const onResolve = jest . fn ( )
302
303
const { rerender } = render ( < Async promiseFn = { ( ) => resolveTo ( ) } onResolve = { onResolve } /> )
303
304
rerender ( < Async onResolve = { onResolve } /> )
304
- await resolveTo ( )
305
+ await sleep ( 10 )
305
306
expect ( onResolve ) . not . toHaveBeenCalled ( )
306
307
expect ( abortCtrl . abort ) . toHaveBeenCalledTimes ( 1 )
307
308
} )
308
309
309
310
test ( "does not run `promiseFn` on mount when `initialValue` is provided" , async ( ) => {
310
311
const promiseFn = jest . fn ( ) . mockReturnValue ( resolveTo ( ) )
311
312
render ( < Async promiseFn = { promiseFn } initialValue = { { } } /> )
312
- await resolveTo ( )
313
+ await sleep ( 10 )
313
314
expect ( promiseFn ) . not . toHaveBeenCalled ( )
314
315
} )
315
316
@@ -415,3 +416,81 @@ export const withDeferFn = (Async, abortCtrl) => () => {
415
416
await waitForElement ( ( ) => getByText ( "done" ) )
416
417
} )
417
418
}
419
+
420
+ export const withReducer = Async => ( ) => {
421
+ test ( "receives state, action and the original reducer" , async ( ) => {
422
+ const promise = resolveTo ( "done" )
423
+ const reducer = jest . fn ( ( state , action , asyncReducer ) => asyncReducer ( state , action ) )
424
+ const { getByText } = render (
425
+ < Async promise = { promise } reducer = { reducer } >
426
+ { ( { data } ) => data || null }
427
+ </ Async >
428
+ )
429
+ await waitForElement ( ( ) => getByText ( "done" ) )
430
+ expect ( reducer ) . toHaveBeenCalledWith (
431
+ expect . objectContaining ( { status : expect . any ( String ) } ) ,
432
+ expect . objectContaining ( { type : expect . any ( String ) } ) ,
433
+ expect . any ( Function )
434
+ )
435
+ } )
436
+
437
+ test ( "allows overriding state updates" , async ( ) => {
438
+ const promise = resolveTo ( "done" )
439
+ const reducer = ( state , action , asyncReducer ) => {
440
+ if ( action . type === "fulfill" ) action . payload = "cool"
441
+ return asyncReducer ( state , action )
442
+ }
443
+ const { getByText } = render (
444
+ < Async promise = { promise } reducer = { reducer } >
445
+ { ( { data } ) => data || null }
446
+ </ Async >
447
+ )
448
+ await waitForElement ( ( ) => getByText ( "cool" ) )
449
+ } )
450
+ }
451
+
452
+ export const withDispatcher = Async => ( ) => {
453
+ test ( "receives action, the original dispatch method and options" , async ( ) => {
454
+ const promise = resolveTo ( "done" )
455
+ const dispatcher = jest . fn ( ( action , dispatch ) => dispatch ( action ) )
456
+ const props = { promise, dispatcher }
457
+ const { getByText } = render ( < Async { ...props } > { ( { data } ) => data || null } </ Async > )
458
+ await waitForElement ( ( ) => getByText ( "done" ) )
459
+ expect ( dispatcher ) . toHaveBeenCalledWith (
460
+ expect . objectContaining ( { type : expect . any ( String ) } ) ,
461
+ expect . any ( Function ) ,
462
+ expect . objectContaining ( props )
463
+ )
464
+ } )
465
+
466
+ test ( "allows overriding actions before dispatch" , async ( ) => {
467
+ const promise = resolveTo ( "done" )
468
+ const dispatcher = ( action , dispatch ) => {
469
+ if ( action . type === "fulfill" ) action . payload = "cool"
470
+ dispatch ( action )
471
+ }
472
+ const { getByText } = render (
473
+ < Async promise = { promise } dispatcher = { dispatcher } >
474
+ { ( { data } ) => data || null }
475
+ </ Async >
476
+ )
477
+ await waitForElement ( ( ) => getByText ( "cool" ) )
478
+ } )
479
+
480
+ test ( "allows dispatching additional actions" , async ( ) => {
481
+ const promise = resolveTo ( "done" )
482
+ const customAction = { type : "custom" }
483
+ const dispatcher = ( action , dispatch ) => {
484
+ dispatch ( action )
485
+ dispatch ( customAction )
486
+ }
487
+ const reducer = jest . fn ( ( state , action , asyncReducer ) => asyncReducer ( state , action ) )
488
+ const { getByText } = render (
489
+ < Async promise = { promise } dispatcher = { dispatcher } reducer = { reducer } >
490
+ { ( { data } ) => data || null }
491
+ </ Async >
492
+ )
493
+ await waitForElement ( ( ) => getByText ( "done" ) )
494
+ expect ( reducer ) . toHaveBeenCalledWith ( expect . anything ( ) , customAction , expect . anything ( ) )
495
+ } )
496
+ }
0 commit comments