Skip to content

Commit b4849bc

Browse files
Lei ChenLei Chen
Lei Chen
authored and
Lei Chen
committed
fix fakeTimer problem
add new fakeTimer test and revise the function add advanceTime
1 parent 4a03704 commit b4849bc

File tree

4 files changed

+81
-7
lines changed

4 files changed

+81
-7
lines changed

src/__tests__/asyncHook.fakeTimers.test.ts

+35
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,41 @@ describe('async hook (fake timers) tests', () => {
5151

5252
expect(complete).toBe(true)
5353
})
54+
55+
test('should waitFor arbitrary expectation to pass when fake timers are not advanced explicitly', async () => {
56+
const fn = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)
57+
58+
const { waitFor } = renderHook(() => null)
59+
60+
await waitFor(() => {
61+
expect(fn()).toBe(true)
62+
})
63+
})
64+
65+
test('should reject if timeout is passed close to when promise resolves', async () => {
66+
const { waitFor } = renderHook(() => null)
67+
68+
let actual = 0
69+
const expected = 1
70+
71+
setTimeout(() => {
72+
actual = expected
73+
}, 101)
74+
75+
let complete = false
76+
77+
await expect(
78+
waitFor(
79+
() => {
80+
expect(actual).toBe(expected)
81+
complete = true
82+
},
83+
{ timeout: 100, interval: 50 }
84+
)
85+
).rejects.toThrow(Error('Timed out in waitFor after 100ms.'))
86+
87+
expect(complete).toBe(false)
88+
})
5489
})
5590
})
5691

src/core/asyncUtils.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import {
77
AsyncUtils
88
} from '../types'
99

10-
import { createTimeoutController } from '../helpers/createTimeoutController'
10+
import { createTimeoutController, DEFAULT_TIMEOUT } from '../helpers/createTimeoutController'
1111
import { TimeoutError } from '../helpers/error'
1212

1313
const DEFAULT_INTERVAL = 50
14-
const DEFAULT_TIMEOUT = 1000
1514

1615
function asyncUtils(act: Act, addResolver: (callback: () => void) => void): AsyncUtils {
1716
const wait = async (callback: () => boolean | void, { interval, timeout }: WaitOptions) => {
@@ -20,11 +19,11 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn
2019
return callbackResult ?? callbackResult === undefined
2120
}
2221

23-
const timeoutSignal = createTimeoutController(timeout)
22+
const timeoutSignal = createTimeoutController(timeout, false)
2423

2524
const waitForResult = async () => {
2625
while (true) {
27-
const intervalSignal = createTimeoutController(interval)
26+
const intervalSignal = createTimeoutController(interval, true)
2827
timeoutSignal.onTimeout(() => intervalSignal.cancel())
2928

3029
await intervalSignal.wrap(new Promise<void>(addResolver))

src/helpers/createTimeoutController.ts

+30-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
import { WaitOptions } from '../types'
2+
import { jestFakeTimersAreEnabled } from './jestFakeTimersAreEnabled'
3+
const DEFAULT_TIMEOUT = 1000
24

3-
function createTimeoutController(timeout: WaitOptions['timeout']) {
5+
function createTimeoutController(
6+
timeout: WaitOptions['timeout'] = DEFAULT_TIMEOUT,
7+
allowFakeTimers: boolean
8+
) {
49
let timeoutId: NodeJS.Timeout
510
const timeoutCallbacks: Array<() => void> = []
11+
let finished: boolean = false
612

13+
const advanceTime = async (currentMs: number) => {
14+
if (currentMs <= timeout) {
15+
jest.advanceTimersByTime(1)
16+
17+
await Promise.resolve()
18+
19+
if (finished) {
20+
return
21+
}
22+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
23+
await advanceTime(currentMs + 1)
24+
}
25+
}
726
const timeoutController = {
827
onTimeout(callback: () => void) {
928
timeoutCallbacks.push(callback)
@@ -19,15 +38,23 @@ function createTimeoutController(timeout: WaitOptions['timeout']) {
1938
timeoutCallbacks.forEach((callback) => callback())
2039
resolve()
2140
}, timeout)
41+
42+
if (jestFakeTimersAreEnabled() && allowFakeTimers) {
43+
advanceTime(0)
44+
}
2245
}
2346

2447
promise
2548
.then(resolve)
2649
.catch(reject)
27-
.finally(() => timeoutController.cancel())
50+
.finally(() => {
51+
finished = true
52+
timeoutController.cancel()
53+
})
2854
})
2955
},
3056
cancel() {
57+
finished = true
3158
clearTimeout(timeoutId)
3259
},
3360
timedOut: false
@@ -36,4 +63,4 @@ function createTimeoutController(timeout: WaitOptions['timeout']) {
3663
return timeoutController
3764
}
3865

39-
export { createTimeoutController }
66+
export { createTimeoutController, DEFAULT_TIMEOUT }
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const jestFakeTimersAreEnabled = () => {
2+
/* istanbul ignore else */
3+
if (typeof jest !== 'undefined' && jest !== null) {
4+
return (
5+
// legacy timers
6+
jest.isMockFunction(setTimeout) ||
7+
// modern timers
8+
Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
9+
)
10+
}
11+
// istanbul ignore next
12+
return false
13+
}

0 commit comments

Comments
 (0)