From ac1b5597fba3398336f1087d0fbddd3ab982aade 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 change timeout type fix converage and revise type --- src/__tests__/asyncHook.fakeTimers.test.ts | 35 ++++++++++++++++++++++ src/core/asyncUtils.ts | 6 ++-- src/helpers/createTimeoutController.ts | 10 +++++-- src/helpers/jestFakeTimersAreEnabled.ts | 13 ++++++++ 4 files changed, 58 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..a62e5940 100644 --- a/src/core/asyncUtils.ts +++ b/src/core/asyncUtils.ts @@ -10,8 +10,8 @@ import { import { createTimeoutController } from '../helpers/createTimeoutController' import { TimeoutError } from '../helpers/error' -const DEFAULT_INTERVAL = 50 const DEFAULT_TIMEOUT = 1000 +const DEFAULT_INTERVAL = 50 function asyncUtils(act: Act, addResolver: (callback: () => void) => void): AsyncUtils { const wait = async (callback: () => boolean | void, { interval, timeout }: WaitOptions) => { @@ -20,11 +20,11 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn return callbackResult ?? callbackResult === undefined } - const timeoutSignal = createTimeoutController(timeout) + const timeoutSignal = createTimeoutController(timeout as number | boolean, false) const waitForResult = async () => { while (true) { - const intervalSignal = createTimeoutController(interval) + const intervalSignal = createTimeoutController(interval as number | boolean, 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..033d052f 100644 --- a/src/helpers/createTimeoutController.ts +++ b/src/helpers/createTimeoutController.ts @@ -1,6 +1,6 @@ -import { WaitOptions } from '../types' +import { jestFakeTimersAreEnabled } from './jestFakeTimersAreEnabled' -function createTimeoutController(timeout: WaitOptions['timeout']) { +function createTimeoutController(timeout: number | boolean, allowFakeTimers: boolean) { let timeoutId: NodeJS.Timeout const timeoutCallbacks: Array<() => void> = [] @@ -18,7 +18,11 @@ function createTimeoutController(timeout: WaitOptions['timeout']) { timeoutController.timedOut = true timeoutCallbacks.forEach((callback) => callback()) resolve() - }, timeout) + }, timeout as number) + + if (jestFakeTimersAreEnabled() && allowFakeTimers) { + jest.advanceTimersByTime(timeout as number) + } } promise 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 +}