From 886718c5a2b7a5b33594bc6ed2887526d9136a11 Mon Sep 17 00:00:00 2001 From: Zero Cho Date: Wed, 16 Nov 2016 15:03:20 -0800 Subject: [PATCH] Always recreate mocks in FakeTimers.useFakeTimers() If the flag resetMocks or jest.resetAllMocks is used in the test, all the timer tests following that will fail with no way of recovering. jest.resetAllMocks resets everything, including the one created by fakeTimers. There is also no public methods that we can use to reinstantiate a new FakeTimer object to mitigate that. Given both mock and fake timers are part of the jest, they should be working together out-of-the box without extra code gluing them. We change the useFakeTimers method to always create new mocks so it would not be reverted. --- .../timer_after_resetAllMocks-test.js | 20 ++++++++++ .../timer_after_resetAllMocks/index.js | 11 +++++ .../timer_after_resetAllMocks/package.json | 6 +++ .../timer_and_mock.test.js | 18 +++++++++ packages/jest-util/src/FakeTimers.js | 40 ++++++++++++------- 5 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 integration_tests/__tests__/timer_after_resetAllMocks-test.js create mode 100644 integration_tests/timer_after_resetAllMocks/index.js create mode 100644 integration_tests/timer_after_resetAllMocks/package.json create mode 100644 integration_tests/timer_after_resetAllMocks/timer_and_mock.test.js diff --git a/integration_tests/__tests__/timer_after_resetAllMocks-test.js b/integration_tests/__tests__/timer_after_resetAllMocks-test.js new file mode 100644 index 000000000000..907355de2f3f --- /dev/null +++ b/integration_tests/__tests__/timer_after_resetAllMocks-test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails oncall+jsinfra + */ +'use strict'; + +const runJest = require('../runJest'); +const skipOnWindows = require('skipOnWindows'); + +skipOnWindows.suite(); + +test('run timers after resetAllMocks test', () => { + const result = runJest('timer_after_resetAllMocks'); + expect(result.status).toBe(0); +}); diff --git a/integration_tests/timer_after_resetAllMocks/index.js b/integration_tests/timer_after_resetAllMocks/index.js new file mode 100644 index 000000000000..065ab53aaf25 --- /dev/null +++ b/integration_tests/timer_after_resetAllMocks/index.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +module.exports = () => {}; diff --git a/integration_tests/timer_after_resetAllMocks/package.json b/integration_tests/timer_after_resetAllMocks/package.json new file mode 100644 index 000000000000..24b23cafe0e3 --- /dev/null +++ b/integration_tests/timer_after_resetAllMocks/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "resetMocks": false + } +} diff --git a/integration_tests/timer_after_resetAllMocks/timer_and_mock.test.js b/integration_tests/timer_after_resetAllMocks/timer_and_mock.test.js new file mode 100644 index 000000000000..969cd00ede8d --- /dev/null +++ b/integration_tests/timer_after_resetAllMocks/timer_and_mock.test.js @@ -0,0 +1,18 @@ +describe('timers', () => { + it('should work before calling resetAllMocks', () => { + jest.useFakeTimers(); + const f = jest.fn(); + setImmediate(() => f()); + jest.runAllImmediates(); + expect(f.mock.calls.length).toBe(1); + }); + + it('should not break after calling resetAllMocks', () => { + jest.resetAllMocks(); + jest.useFakeTimers(); + const f = jest.fn(); + setImmediate(() => f()); + jest.runAllImmediates(); + expect(f.mock.calls.length).toBe(1); + }); +}); diff --git a/packages/jest-util/src/FakeTimers.js b/packages/jest-util/src/FakeTimers.js index ffb0e4f425c4..ce5bbaf83fa3 100644 --- a/packages/jest-util/src/FakeTimers.js +++ b/packages/jest-util/src/FakeTimers.js @@ -53,6 +53,7 @@ class FakeTimers { _global: Global; _immediates: Array; _maxLoops: number; + _moduleMocker: ModuleMocker; _now: number; _ticks: Array; _timerAPIs: TimerAPI; @@ -70,8 +71,7 @@ class FakeTimers { this._config = config; this._maxLoops = maxLoops || 100000; this._uuidCounter = 1; - - this.reset(); + this._moduleMocker = moduleMocker; // Store original timer APIs for future reference this._timerAPIs = { @@ -83,25 +83,16 @@ class FakeTimers { setTimeout: global.setTimeout, }; - const fn = impl => moduleMocker.getMockFn().mockImpl(impl); - - this._fakeTimerAPIs = { - clearImmediate: fn(this._fakeClearImmediate.bind(this)), - clearInterval: fn(this._fakeClearTimer.bind(this)), - clearTimeout: fn(this._fakeClearTimer.bind(this)), - setImmediate: fn(this._fakeSetImmediate.bind(this)), - setInterval: fn(this._fakeSetInterval.bind(this)), - setTimeout: fn(this._fakeSetTimeout.bind(this)), - }; - // If there's a process.nextTick on the global, mock it out // (only applicable to node/node-emulating environments) if (typeof global.process === 'object' && typeof global.process.nextTick === 'function') { this._timerAPIs.nextTick = global.process.nextTick; - this._fakeTimerAPIs.nextTick = fn(this._fakeNextTick.bind(this)); } + this.reset(); + this._createMocks(); + // These globally-accessible function are now deprecated! // They will go away very soon, so do not use them! // Instead, use the versions available on the `jest` object @@ -330,6 +321,8 @@ class FakeTimers { typeof this._global.process === 'object' && typeof this._global.process.nextTick === 'function'; + this._createMocks(); + this._global.clearImmediate = this._fakeTimerAPIs.clearImmediate; this._global.clearInterval = this._fakeTimerAPIs.clearInterval; this._global.clearTimeout = this._fakeTimerAPIs.clearTimeout; @@ -355,6 +348,25 @@ class FakeTimers { } } + _createMocks() { + const fn = impl => this._moduleMocker.getMockFn().mockImpl(impl); + + this._fakeTimerAPIs = { + clearImmediate: fn(this._fakeClearImmediate.bind(this)), + clearInterval: fn(this._fakeClearTimer.bind(this)), + clearTimeout: fn(this._fakeClearTimer.bind(this)), + setImmediate: fn(this._fakeSetImmediate.bind(this)), + setInterval: fn(this._fakeSetInterval.bind(this)), + setTimeout: fn(this._fakeSetTimeout.bind(this)), + }; + + // If there's a process.nextTick on the global, mock it out + // (only applicable to node/node-emulating environments) + if (this._timerAPIs.nextTick) { + this._fakeTimerAPIs.nextTick = fn(this._fakeNextTick.bind(this)); + } + } + _fakeClearTimer(uuid: TimerID) { if (this._timers.hasOwnProperty(uuid)) { delete this._timers[uuid];