From 62449f29d97d5a49c162b38f3a82284777e7d3af Mon Sep 17 00:00:00 2001 From: Lei Chen Date: Mon, 6 Sep 2021 12:33:29 -0700 Subject: [PATCH] fix fakeTimer problem add new fakeTimer test and revise the function add advanceTime revise the advanceTime use jest.advanceTimersByTime --- src/__tests__/asyncHook.fakeTimers.test.ts | 35 ++++++++++++++++++++++ src/core/asyncUtils.ts | 7 ++--- src/helpers/createTimeoutController.ts | 13 ++++++-- src/helpers/jestFakeTimersAreEnabled.ts | 13 ++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 src/helpers/jestFakeTimersAreEnabled.ts diff --git a/src/__tests__/asyncHook.fakeTimers.test.ts b/src/__tests__/asyncHook.fakeTimers.test.ts index 98d6b2c9..69bff39d 100644 --- a/src/__tests__/asyncHook.fakeTimers.test.ts +++ b/src/__tests__/asyncHook.fakeTimers.test.ts @@ -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) + }) }) }) diff --git a/src/core/asyncUtils.ts b/src/core/asyncUtils.ts index a7424036..e759e54a 100644 --- a/src/core/asyncUtils.ts +++ b/src/core/asyncUtils.ts @@ -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) => { @@ -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(addResolver)) diff --git a/src/helpers/createTimeoutController.ts b/src/helpers/createTimeoutController.ts index 643d3768..80db8653 100644 --- a/src/helpers/createTimeoutController.ts +++ b/src/helpers/createTimeoutController.ts @@ -1,6 +1,11 @@ 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> = [] @@ -19,6 +24,10 @@ function createTimeoutController(timeout: WaitOptions['timeout']) { timeoutCallbacks.forEach((callback) => callback()) resolve() }, timeout) + + if (jestFakeTimersAreEnabled() && allowFakeTimers) { + jest.advanceTimersByTime(timeout) + } } promise @@ -36,4 +45,4 @@ function createTimeoutController(timeout: WaitOptions['timeout']) { return timeoutController } -export { createTimeoutController } +export { createTimeoutController, DEFAULT_TIMEOUT } diff --git a/src/helpers/jestFakeTimersAreEnabled.ts b/src/helpers/jestFakeTimersAreEnabled.ts new file mode 100644 index 00000000..5a0e2a88 --- /dev/null +++ b/src/helpers/jestFakeTimersAreEnabled.ts @@ -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 +}