diff --git a/src/__tests__/helpers.js b/src/__tests__/helpers.js index 1bc85837..8e5d436f 100644 --- a/src/__tests__/helpers.js +++ b/src/__tests__/helpers.js @@ -5,10 +5,6 @@ import { runWithRealTimers, } from '../helpers' -const globalObj = typeof window === 'undefined' ? global : window - -afterEach(() => jest.useRealTimers()) - test('returns global document if exists', () => { expect(getDocument()).toBe(document) }) @@ -53,42 +49,47 @@ describe('query container validation throws when validation fails', () => { }) }) -test('should always use realTimers before using callback when timers are faked with useFakeTimers', () => { - const originalSetTimeout = globalObj.setTimeout +describe('run with real timers', () => { + const realSetTimeout = global.setTimeout - // legacy timers use mocks and do not rely on a clock instance - jest.useFakeTimers('legacy') - runWithRealTimers(() => { - expect(originalSetTimeout).toEqual(globalObj.setTimeout) + afterEach(() => { + // restore timers replaced by jest.useFakeTimers() + jest.useRealTimers() + // restore setTimeout replaced by assignment + global.setTimeout = realSetTimeout }) - expect(globalObj.setTimeout._isMockFunction).toBe(true) - expect(globalObj.setTimeout.clock).toBeUndefined() - jest.useRealTimers() - - // modern timers use a clock instance instead of a mock - jest.useFakeTimers('modern') - runWithRealTimers(() => { - expect(originalSetTimeout).toEqual(globalObj.setTimeout) + test('use real timers when timers are faked with jest.useFakeTimers(legacy)', () => { + // legacy timers use mocks and do not rely on a clock instance + jest.useFakeTimers('legacy') + runWithRealTimers(() => { + expect(global.setTimeout).toBe(realSetTimeout) + }) + expect(global.setTimeout._isMockFunction).toBe(true) + expect(global.setTimeout.clock).toBeUndefined() }) - expect(globalObj.setTimeout._isMockFunction).toBeUndefined() - expect(globalObj.setTimeout.clock).toBeDefined() -}) -test('should not use realTimers when timers are not faked with useFakeTimers', () => { - const originalSetTimeout = globalObj.setTimeout - - // useFakeTimers is not used, timers are faked in some other way - const fakedSetTimeout = callback => { - callback() - } - fakedSetTimeout.clock = jest.fn() + test('use real timers when timers are faked with jest.useFakeTimers(modern)', () => { + // modern timers use a clock instance instead of a mock + jest.useFakeTimers('modern') + runWithRealTimers(() => { + expect(global.setTimeout).toBe(realSetTimeout) + }) + expect(global.setTimeout._isMockFunction).toBeUndefined() + expect(global.setTimeout.clock).toBeDefined() + }) - globalObj.setTimeout = fakedSetTimeout + test('do not use real timers when timers are not faked with jest.useFakeTimers', () => { + // useFakeTimers is not used, timers are faked in some other way + const fakedSetTimeout = callback => { + callback() + } + fakedSetTimeout.clock = jest.fn() + global.setTimeout = fakedSetTimeout - runWithRealTimers(() => { - expect(fakedSetTimeout).toEqual(globalObj.setTimeout) + runWithRealTimers(() => { + expect(global.setTimeout).toBe(fakedSetTimeout) + }) + expect(global.setTimeout).toBe(fakedSetTimeout) }) - - globalObj.setTimeout = originalSetTimeout }) diff --git a/src/helpers.js b/src/helpers.js index f686d46b..7b8d95e5 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -5,52 +5,42 @@ const TEXT_NODE = 3 // Currently this fn only supports jest timers, but it could support other test runners in the future. function runWithRealTimers(callback) { - const fakeTimersType = getJestFakeTimersType() - if (fakeTimersType) { + return _runWithRealTimers(callback).callbackReturnValue +} + +function _runWithRealTimers(callback) { + const timerAPI = { + clearImmediate, + clearInterval, + clearTimeout, + setImmediate, + setInterval, + setTimeout, + } + + // istanbul ignore else + if (typeof jest !== 'undefined') { jest.useRealTimers() } const callbackReturnValue = callback() - if (fakeTimersType) { - jest.useFakeTimers(fakeTimersType) - } + const usedJestFakeTimers = Object.entries(timerAPI).some( + ([name, func]) => func !== globalObj[name], + ) - return callbackReturnValue -} - -function getJestFakeTimersType() { - // istanbul ignore if - if ( - typeof jest === 'undefined' || - typeof globalObj.setTimeout === 'undefined' - ) { - return null + if (usedJestFakeTimers) { + jest.useFakeTimers(timerAPI.setTimeout?.clock ? 'modern' : 'legacy') } - if ( - typeof globalObj.setTimeout._isMockFunction !== 'undefined' && - globalObj.setTimeout._isMockFunction - ) { - return 'legacy' - } - - if ( - typeof globalObj.setTimeout.clock !== 'undefined' && - typeof jest.getRealSystemTime !== 'undefined' - ) { - try { - // jest.getRealSystemTime is only supported for Jest's `modern` fake timers and otherwise throws - jest.getRealSystemTime() - return 'modern' - } catch { - // not using Jest's modern fake timers - } + return { + callbackReturnValue, + usedJestFakeTimers, } - return null } -const jestFakeTimersAreEnabled = () => Boolean(getJestFakeTimersType()) +const jestFakeTimersAreEnabled = () => + Boolean(_runWithRealTimers(() => {}).usedJestFakeTimers) // we only run our tests in node, and setImmediate is supported in node. // istanbul ignore next