Skip to content

Commit

Permalink
fix fakeTimer problem
Browse files Browse the repository at this point in the history
add new fakeTimer test and revise the function

add advanceTime
  • Loading branch information
Lei Chen authored and Lei Chen committed Sep 8, 2021
1 parent 4a03704 commit b4849bc
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 7 deletions.
35 changes: 35 additions & 0 deletions src/__tests__/asyncHook.fakeTimers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,41 @@ describe('async hook (fake timers) tests', () => {

expect(complete).toBe(true)
})

test('should waitFor arbitrary expectation to pass when fake timers are not advanced explicitly', async () => {
const fn = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)

const { waitFor } = renderHook(() => null)

await waitFor(() => {
expect(fn()).toBe(true)
})
})

test('should reject if timeout is passed close to when promise resolves', async () => {
const { waitFor } = renderHook(() => null)

let actual = 0
const expected = 1

setTimeout(() => {
actual = expected
}, 101)

let complete = false

await expect(
waitFor(
() => {
expect(actual).toBe(expected)
complete = true
},
{ timeout: 100, interval: 50 }
)
).rejects.toThrow(Error('Timed out in waitFor after 100ms.'))

expect(complete).toBe(false)
})
})
})

Expand Down
7 changes: 3 additions & 4 deletions src/core/asyncUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import {
AsyncUtils
} from '../types'

import { createTimeoutController } from '../helpers/createTimeoutController'
import { createTimeoutController, DEFAULT_TIMEOUT } from '../helpers/createTimeoutController'
import { TimeoutError } from '../helpers/error'

const DEFAULT_INTERVAL = 50
const DEFAULT_TIMEOUT = 1000

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

const timeoutSignal = createTimeoutController(timeout)
const timeoutSignal = createTimeoutController(timeout, false)

const waitForResult = async () => {
while (true) {
const intervalSignal = createTimeoutController(interval)
const intervalSignal = createTimeoutController(interval, true)
timeoutSignal.onTimeout(() => intervalSignal.cancel())

await intervalSignal.wrap(new Promise<void>(addResolver))
Expand Down
33 changes: 30 additions & 3 deletions src/helpers/createTimeoutController.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { WaitOptions } from '../types'
import { jestFakeTimersAreEnabled } from './jestFakeTimersAreEnabled'
const DEFAULT_TIMEOUT = 1000

function createTimeoutController(timeout: WaitOptions['timeout']) {
function createTimeoutController(
timeout: WaitOptions['timeout'] = DEFAULT_TIMEOUT,
allowFakeTimers: boolean
) {
let timeoutId: NodeJS.Timeout
const timeoutCallbacks: Array<() => void> = []
let finished: boolean = false

const advanceTime = async (currentMs: number) => {
if (currentMs <= timeout) {
jest.advanceTimersByTime(1)

await Promise.resolve()

if (finished) {
return
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await advanceTime(currentMs + 1)
}
}
const timeoutController = {
onTimeout(callback: () => void) {
timeoutCallbacks.push(callback)
Expand All @@ -19,15 +38,23 @@ function createTimeoutController(timeout: WaitOptions['timeout']) {
timeoutCallbacks.forEach((callback) => callback())
resolve()
}, timeout)

if (jestFakeTimersAreEnabled() && allowFakeTimers) {
advanceTime(0)
}
}

promise
.then(resolve)
.catch(reject)
.finally(() => timeoutController.cancel())
.finally(() => {
finished = true
timeoutController.cancel()
})
})
},
cancel() {
finished = true
clearTimeout(timeoutId)
},
timedOut: false
Expand All @@ -36,4 +63,4 @@ function createTimeoutController(timeout: WaitOptions['timeout']) {
return timeoutController
}

export { createTimeoutController }
export { createTimeoutController, DEFAULT_TIMEOUT }
13 changes: 13 additions & 0 deletions src/helpers/jestFakeTimersAreEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const jestFakeTimersAreEnabled = () => {
/* istanbul ignore else */
if (typeof jest !== 'undefined' && jest !== null) {
return (
// legacy timers
jest.isMockFunction(setTimeout) ||
// modern timers
Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
)
}
// istanbul ignore next
return false
}

0 comments on commit b4849bc

Please sign in to comment.