1
+ import { useState , useRef , useEffect } from 'react'
2
+
1
3
describe ( 'async hook (fake timers) tests' , ( ) => {
4
+ const useSequence = ( values : string [ ] , intervalMs = 50 ) => {
5
+ const [ first , ...otherValues ] = values
6
+ const [ value , setValue ] = useState ( ( ) => first )
7
+ const index = useRef ( 0 )
8
+
9
+ useEffect ( ( ) => {
10
+ const interval = setInterval ( ( ) => {
11
+ setValue ( otherValues [ index . current ++ ] )
12
+ if ( index . current >= otherValues . length ) {
13
+ clearInterval ( interval )
14
+ }
15
+ } , intervalMs )
16
+ return ( ) => {
17
+ clearInterval ( interval )
18
+ }
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ } , otherValues )
21
+
22
+ return value
23
+ }
24
+
2
25
beforeEach ( ( ) => {
3
26
jest . useFakeTimers ( )
4
27
} )
@@ -54,9 +77,9 @@ describe('async hook (fake timers) tests', () => {
54
77
55
78
test ( 'should waitFor arbitrary expectation to pass when fake timers are not advanced explicitly' , async ( ) => {
56
79
const fn = jest . fn ( ) . mockReturnValueOnce ( false ) . mockReturnValueOnce ( true )
57
-
80
+
58
81
const { waitFor } = renderHook ( ( ) => null )
59
-
82
+
60
83
await waitFor ( ( ) => {
61
84
expect ( fn ( ) ) . toBe ( true )
62
85
} )
@@ -70,7 +93,7 @@ describe('async hook (fake timers) tests', () => {
70
93
71
94
setTimeout ( ( ) => {
72
95
actual = expected
73
- } , 101 )
96
+ } , 30 )
74
97
75
98
let complete = false
76
99
@@ -80,14 +103,244 @@ describe('async hook (fake timers) tests', () => {
80
103
expect ( actual ) . toBe ( expected )
81
104
complete = true
82
105
} ,
83
- { timeout : 100 , interval : 50 }
106
+ { timeout : 29 , interval : 10 }
84
107
)
85
- ) . rejects . toThrow ( Error ( 'Timed out in waitFor after 100ms .' ) )
108
+ ) . rejects . toThrow ( Error ( 'Timed out in waitFor after 29ms .' ) )
86
109
87
110
expect ( complete ) . toBe ( false )
88
111
} )
112
+
113
+ test ( 'should wait for next update' , async ( ) => {
114
+ const { result, waitForNextUpdate } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' ] ) )
115
+
116
+ expect ( result . current ) . toBe ( 'first' )
117
+
118
+ await waitForNextUpdate ( )
119
+
120
+ expect ( result . current ) . toBe ( 'second' )
121
+ } )
122
+
123
+ test ( 'should wait for multiple updates' , async ( ) => {
124
+ const { result, waitForNextUpdate } = renderHook ( ( ) =>
125
+ useSequence ( [ 'first' , 'second' , 'third' ] )
126
+ )
127
+
128
+ expect ( result . current ) . toBe ( 'first' )
129
+
130
+ await waitForNextUpdate ( )
131
+
132
+ expect ( result . current ) . toBe ( 'second' )
133
+
134
+ await waitForNextUpdate ( )
135
+
136
+ expect ( result . current ) . toBe ( 'third' )
137
+ } )
138
+
139
+ test ( 'should reject if timeout exceeded when waiting for next update' , async ( ) => {
140
+ const { result, waitForNextUpdate } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' ] ) )
141
+
142
+ expect ( result . current ) . toBe ( 'first' )
143
+
144
+ await expect ( waitForNextUpdate ( { timeout : 10 } ) ) . rejects . toThrow (
145
+ Error ( 'Timed out in waitForNextUpdate after 10ms.' )
146
+ )
147
+ } )
148
+
149
+ // eslint-disable-next-line jest/no-disabled-tests
150
+ test . skip ( 'should not reject when waiting for next update if timeout has been disabled' , async ( ) => {
151
+ const { result, waitForNextUpdate } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' ] , 1100 ) )
152
+
153
+ expect ( result . current ) . toBe ( 'first' )
154
+
155
+ await waitForNextUpdate ( { timeout : false } )
156
+
157
+ expect ( result . current ) . toBe ( 'second' )
158
+ } )
159
+
160
+ test ( 'should wait for expectation to pass' , async ( ) => {
161
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' , 'third' ] ) )
162
+
163
+ expect ( result . current ) . toBe ( 'first' )
164
+
165
+ let complete = false
166
+ await waitFor ( ( ) => {
167
+ expect ( result . current ) . toBe ( 'third' )
168
+ complete = true
169
+ } )
170
+ expect ( complete ) . toBe ( true )
171
+ } )
172
+
173
+ test ( 'should wait for arbitrary expectation to pass' , async ( ) => {
174
+ const { waitFor } = renderHook ( ( ) => null )
175
+
176
+ let actual = 0
177
+ const expected = 1
178
+
179
+ setTimeout ( ( ) => {
180
+ actual = expected
181
+ } , 200 )
182
+
183
+ let complete = false
184
+ await waitFor ( ( ) => {
185
+ expect ( actual ) . toBe ( expected )
186
+ complete = true
187
+ } )
188
+
189
+ expect ( complete ) . toBe ( true )
190
+ } )
191
+
192
+ test ( 'should not hang if expectation is already passing' , async ( ) => {
193
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' ] ) )
194
+
195
+ expect ( result . current ) . toBe ( 'first' )
196
+
197
+ let complete = false
198
+ await waitFor ( ( ) => {
199
+ expect ( result . current ) . toBe ( 'first' )
200
+ complete = true
201
+ } )
202
+ expect ( complete ) . toBe ( true )
203
+ } )
204
+
205
+ test ( 'should wait for truthy value' , async ( ) => {
206
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' , 'third' ] ) )
207
+
208
+ expect ( result . current ) . toBe ( 'first' )
209
+
210
+ await waitFor ( ( ) => result . current === 'third' )
211
+
212
+ expect ( result . current ) . toBe ( 'third' )
213
+ } )
214
+
215
+ test ( 'should wait for arbitrary truthy value' , async ( ) => {
216
+ const { waitFor } = renderHook ( ( ) => null )
217
+
218
+ let actual = 0
219
+ const expected = 1
220
+
221
+ setTimeout ( ( ) => {
222
+ actual = expected
223
+ } , 200 )
224
+
225
+ await waitFor ( ( ) => actual === 1 )
226
+
227
+ expect ( actual ) . toBe ( expected )
228
+ } )
229
+
230
+ test ( 'should reject if timeout exceeded when waiting for expectation to pass' , async ( ) => {
231
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' , 'third' ] ) )
232
+
233
+ expect ( result . current ) . toBe ( 'first' )
234
+
235
+ await expect (
236
+ waitFor (
237
+ ( ) => {
238
+ expect ( result . current ) . toBe ( 'third' )
239
+ } ,
240
+ { timeout : 75 }
241
+ )
242
+ ) . rejects . toThrow ( Error ( 'Timed out in waitFor after 75ms.' ) )
243
+ } )
244
+
245
+ test ( 'should not reject when waiting for expectation to pass if timeout has been disabled' , async ( ) => {
246
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' , 'third' ] , 550 ) )
247
+
248
+ expect ( result . current ) . toBe ( 'first' )
249
+
250
+ await waitFor (
251
+ ( ) => {
252
+ expect ( result . current ) . toBe ( 'third' )
253
+ } ,
254
+ { timeout : false }
255
+ )
256
+
257
+ expect ( result . current ) . toBe ( 'third' )
258
+ } )
259
+
260
+ test ( 'should check on interval when waiting for expectation to pass' , async ( ) => {
261
+ const { result, waitFor } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' , 'third' ] ) )
262
+
263
+ let checks = 0
264
+
265
+ await waitFor (
266
+ ( ) => {
267
+ checks ++
268
+ return result . current === 'third'
269
+ } ,
270
+ { interval : 100 }
271
+ )
272
+
273
+ expect ( checks ) . toBe ( 3 )
274
+ } )
275
+
276
+ test ( 'should wait for value to change' , async ( ) => {
277
+ const { result, waitForValueToChange } = renderHook ( ( ) =>
278
+ useSequence ( [ 'first' , 'second' , 'third' ] )
279
+ )
280
+
281
+ expect ( result . current ) . toBe ( 'first' )
282
+
283
+ await waitForValueToChange ( ( ) => result . current === 'third' )
284
+
285
+ expect ( result . current ) . toBe ( 'third' )
286
+ } )
287
+
288
+ test ( 'should wait for arbitrary value to change' , async ( ) => {
289
+ const { waitForValueToChange } = renderHook ( ( ) => null )
290
+
291
+ let actual = 0
292
+ const expected = 1
293
+
294
+ setTimeout ( ( ) => {
295
+ actual = expected
296
+ } , 200 )
297
+
298
+ await waitForValueToChange ( ( ) => actual )
299
+
300
+ expect ( actual ) . toBe ( expected )
301
+ } )
302
+
303
+ test ( 'should reject if timeout exceeded when waiting for value to change' , async ( ) => {
304
+ const { result, waitForValueToChange } = renderHook ( ( ) =>
305
+ useSequence ( [ 'first' , 'second' , 'third' ] )
306
+ )
307
+
308
+ expect ( result . current ) . toBe ( 'first' )
309
+
310
+ await expect (
311
+ waitForValueToChange ( ( ) => result . current === 'third' , {
312
+ timeout : 75
313
+ } )
314
+ ) . rejects . toThrow ( Error ( 'Timed out in waitForValueToChange after 75ms.' ) )
315
+ } )
316
+
317
+ test ( 'should not reject when waiting for value to change if timeout is disabled' , async ( ) => {
318
+ const { result, waitForValueToChange } = renderHook ( ( ) =>
319
+ useSequence ( [ 'first' , 'second' , 'third' ] , 550 )
320
+ )
321
+
322
+ expect ( result . current ) . toBe ( 'first' )
323
+
324
+ await waitForValueToChange ( ( ) => result . current === 'third' , {
325
+ timeout : false
326
+ } )
327
+
328
+ expect ( result . current ) . toBe ( 'third' )
329
+ } )
330
+
331
+ test ( 'should reject if selector throws error' , async ( ) => {
332
+ const { result, waitForValueToChange } = renderHook ( ( ) => useSequence ( [ 'first' , 'second' ] ) )
333
+
334
+ expect ( result . current ) . toBe ( 'first' )
335
+
336
+ await expect (
337
+ waitForValueToChange ( ( ) => {
338
+ if ( result . current === 'second' ) {
339
+ throw new Error ( 'Something Unexpected' )
340
+ }
341
+ return result . current
342
+ } )
343
+ ) . rejects . toThrow ( Error ( 'Something Unexpected' ) )
344
+ } )
89
345
} )
90
346
} )
91
-
92
- // eslint-disable-next-line jest/no-export
93
- export { }
0 commit comments