Spaces:
Sleeping
Sleeping
| ; | |
| Object.defineProperty(exports, '__esModule', { | |
| value: true | |
| }); | |
| exports.default = void 0; | |
| function _util() { | |
| const data = _interopRequireDefault(require('util')); | |
| _util = function () { | |
| return data; | |
| }; | |
| return data; | |
| } | |
| function _jestMessageUtil() { | |
| const data = require('jest-message-util'); | |
| _jestMessageUtil = function () { | |
| return data; | |
| }; | |
| return data; | |
| } | |
| function _jestUtil() { | |
| const data = require('jest-util'); | |
| _jestUtil = function () { | |
| return data; | |
| }; | |
| return data; | |
| } | |
| function _interopRequireDefault(obj) { | |
| return obj && obj.__esModule ? obj : {default: obj}; | |
| } | |
| function _defineProperty(obj, key, value) { | |
| if (key in obj) { | |
| Object.defineProperty(obj, key, { | |
| value: value, | |
| enumerable: true, | |
| configurable: true, | |
| writable: true | |
| }); | |
| } else { | |
| obj[key] = value; | |
| } | |
| return obj; | |
| } | |
| const MS_IN_A_YEAR = 31536000000; | |
| class FakeTimers { | |
| constructor({global, moduleMocker, timerConfig, config, maxLoops}) { | |
| _defineProperty(this, '_cancelledTicks', void 0); | |
| _defineProperty(this, '_config', void 0); | |
| _defineProperty(this, '_disposed', void 0); | |
| _defineProperty(this, '_fakeTimerAPIs', void 0); | |
| _defineProperty(this, '_global', void 0); | |
| _defineProperty(this, '_immediates', void 0); | |
| _defineProperty(this, '_maxLoops', void 0); | |
| _defineProperty(this, '_moduleMocker', void 0); | |
| _defineProperty(this, '_now', void 0); | |
| _defineProperty(this, '_ticks', void 0); | |
| _defineProperty(this, '_timerAPIs', void 0); | |
| _defineProperty(this, '_timers', void 0); | |
| _defineProperty(this, '_uuidCounter', void 0); | |
| _defineProperty(this, '_timerConfig', void 0); | |
| this._global = global; | |
| this._timerConfig = timerConfig; | |
| this._config = config; | |
| this._maxLoops = maxLoops || 100000; | |
| this._uuidCounter = 1; | |
| this._moduleMocker = moduleMocker; // Store original timer APIs for future reference | |
| this._timerAPIs = { | |
| cancelAnimationFrame: global.cancelAnimationFrame, | |
| clearImmediate: global.clearImmediate, | |
| clearInterval: global.clearInterval, | |
| clearTimeout: global.clearTimeout, | |
| nextTick: global.process && global.process.nextTick, | |
| requestAnimationFrame: global.requestAnimationFrame, | |
| setImmediate: global.setImmediate, | |
| setInterval: global.setInterval, | |
| setTimeout: global.setTimeout | |
| }; | |
| this.reset(); | |
| } | |
| clearAllTimers() { | |
| this._immediates = []; | |
| this._timers.clear(); | |
| } | |
| dispose() { | |
| this._disposed = true; | |
| this.clearAllTimers(); | |
| } | |
| reset() { | |
| this._cancelledTicks = {}; | |
| this._now = 0; | |
| this._ticks = []; | |
| this._immediates = []; | |
| this._timers = new Map(); | |
| } | |
| runAllTicks() { | |
| this._checkFakeTimers(); // Only run a generous number of ticks and then bail. | |
| // This is just to help avoid recursive loops | |
| let i; | |
| for (i = 0; i < this._maxLoops; i++) { | |
| const tick = this._ticks.shift(); | |
| if (tick === undefined) { | |
| break; | |
| } | |
| if (!this._cancelledTicks.hasOwnProperty(tick.uuid)) { | |
| // Callback may throw, so update the map prior calling. | |
| this._cancelledTicks[tick.uuid] = true; | |
| tick.callback(); | |
| } | |
| } | |
| if (i === this._maxLoops) { | |
| throw new Error( | |
| 'Ran ' + | |
| this._maxLoops + | |
| ' ticks, and there are still more! ' + | |
| "Assuming we've hit an infinite recursion and bailing out..." | |
| ); | |
| } | |
| } | |
| runAllImmediates() { | |
| this._checkFakeTimers(); // Only run a generous number of immediates and then bail. | |
| let i; | |
| for (i = 0; i < this._maxLoops; i++) { | |
| const immediate = this._immediates.shift(); | |
| if (immediate === undefined) { | |
| break; | |
| } | |
| this._runImmediate(immediate); | |
| } | |
| if (i === this._maxLoops) { | |
| throw new Error( | |
| 'Ran ' + | |
| this._maxLoops + | |
| ' immediates, and there are still more! Assuming ' + | |
| "we've hit an infinite recursion and bailing out..." | |
| ); | |
| } | |
| } | |
| _runImmediate(immediate) { | |
| try { | |
| immediate.callback(); | |
| } finally { | |
| this._fakeClearImmediate(immediate.uuid); | |
| } | |
| } | |
| runAllTimers() { | |
| this._checkFakeTimers(); | |
| this.runAllTicks(); | |
| this.runAllImmediates(); // Only run a generous number of timers and then bail. | |
| // This is just to help avoid recursive loops | |
| let i; | |
| for (i = 0; i < this._maxLoops; i++) { | |
| const nextTimerHandle = this._getNextTimerHandle(); // If there are no more timer handles, stop! | |
| if (nextTimerHandle === null) { | |
| break; | |
| } | |
| this._runTimerHandle(nextTimerHandle); // Some of the immediate calls could be enqueued | |
| // during the previous handling of the timers, we should | |
| // run them as well. | |
| if (this._immediates.length) { | |
| this.runAllImmediates(); | |
| } | |
| if (this._ticks.length) { | |
| this.runAllTicks(); | |
| } | |
| } | |
| if (i === this._maxLoops) { | |
| throw new Error( | |
| 'Ran ' + | |
| this._maxLoops + | |
| ' timers, and there are still more! ' + | |
| "Assuming we've hit an infinite recursion and bailing out..." | |
| ); | |
| } | |
| } | |
| runOnlyPendingTimers() { | |
| // We need to hold the current shape of `this._timers` because existing | |
| // timers can add new ones to the map and hence would run more than necessary. | |
| // See https://github.com/facebook/jest/pull/4608 for details | |
| const timerEntries = Array.from(this._timers.entries()); | |
| this._checkFakeTimers(); | |
| this._immediates.forEach(this._runImmediate, this); | |
| timerEntries | |
| .sort(([, left], [, right]) => left.expiry - right.expiry) | |
| .forEach(([timerHandle]) => this._runTimerHandle(timerHandle)); | |
| } | |
| advanceTimersToNextTimer(steps = 1) { | |
| if (steps < 1) { | |
| return; | |
| } | |
| const nextExpiry = Array.from(this._timers.values()).reduce( | |
| (minExpiry, timer) => { | |
| if (minExpiry === null || timer.expiry < minExpiry) return timer.expiry; | |
| return minExpiry; | |
| }, | |
| null | |
| ); | |
| if (nextExpiry !== null) { | |
| this.advanceTimersByTime(nextExpiry - this._now); | |
| this.advanceTimersToNextTimer(steps - 1); | |
| } | |
| } | |
| advanceTimersByTime(msToRun) { | |
| this._checkFakeTimers(); // Only run a generous number of timers and then bail. | |
| // This is just to help avoid recursive loops | |
| let i; | |
| for (i = 0; i < this._maxLoops; i++) { | |
| const timerHandle = this._getNextTimerHandle(); // If there are no more timer handles, stop! | |
| if (timerHandle === null) { | |
| break; | |
| } | |
| const timerValue = this._timers.get(timerHandle); | |
| if (timerValue === undefined) { | |
| break; | |
| } | |
| const nextTimerExpiry = timerValue.expiry; | |
| if (this._now + msToRun < nextTimerExpiry) { | |
| // There are no timers between now and the target we're running to, so | |
| // adjust our time cursor and quit | |
| this._now += msToRun; | |
| break; | |
| } else { | |
| msToRun -= nextTimerExpiry - this._now; | |
| this._now = nextTimerExpiry; | |
| this._runTimerHandle(timerHandle); | |
| } | |
| } | |
| if (i === this._maxLoops) { | |
| throw new Error( | |
| 'Ran ' + | |
| this._maxLoops + | |
| ' timers, and there are still more! ' + | |
| "Assuming we've hit an infinite recursion and bailing out..." | |
| ); | |
| } | |
| } | |
| runWithRealTimers(cb) { | |
| const prevClearImmediate = this._global.clearImmediate; | |
| const prevClearInterval = this._global.clearInterval; | |
| const prevClearTimeout = this._global.clearTimeout; | |
| const prevNextTick = this._global.process.nextTick; | |
| const prevSetImmediate = this._global.setImmediate; | |
| const prevSetInterval = this._global.setInterval; | |
| const prevSetTimeout = this._global.setTimeout; | |
| this.useRealTimers(); | |
| let cbErr = null; | |
| let errThrown = false; | |
| try { | |
| cb(); | |
| } catch (e) { | |
| errThrown = true; | |
| cbErr = e; | |
| } | |
| this._global.clearImmediate = prevClearImmediate; | |
| this._global.clearInterval = prevClearInterval; | |
| this._global.clearTimeout = prevClearTimeout; | |
| this._global.process.nextTick = prevNextTick; | |
| this._global.setImmediate = prevSetImmediate; | |
| this._global.setInterval = prevSetInterval; | |
| this._global.setTimeout = prevSetTimeout; | |
| if (errThrown) { | |
| throw cbErr; | |
| } | |
| } | |
| useRealTimers() { | |
| const global = this._global; | |
| if (typeof global.cancelAnimationFrame === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'cancelAnimationFrame', | |
| this._timerAPIs.cancelAnimationFrame | |
| ); | |
| } | |
| if (typeof global.clearImmediate === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearImmediate', | |
| this._timerAPIs.clearImmediate | |
| ); | |
| } | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearInterval', | |
| this._timerAPIs.clearInterval | |
| ); | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearTimeout', | |
| this._timerAPIs.clearTimeout | |
| ); | |
| if (typeof global.requestAnimationFrame === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'requestAnimationFrame', | |
| this._timerAPIs.requestAnimationFrame | |
| ); | |
| } | |
| if (typeof global.setImmediate === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setImmediate', | |
| this._timerAPIs.setImmediate | |
| ); | |
| } | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setInterval', | |
| this._timerAPIs.setInterval | |
| ); | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setTimeout', | |
| this._timerAPIs.setTimeout | |
| ); | |
| global.process.nextTick = this._timerAPIs.nextTick; | |
| } | |
| useFakeTimers() { | |
| this._createMocks(); | |
| const global = this._global; | |
| if (typeof global.cancelAnimationFrame === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'cancelAnimationFrame', | |
| this._fakeTimerAPIs.cancelAnimationFrame | |
| ); | |
| } | |
| if (typeof global.clearImmediate === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearImmediate', | |
| this._fakeTimerAPIs.clearImmediate | |
| ); | |
| } | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearInterval', | |
| this._fakeTimerAPIs.clearInterval | |
| ); | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'clearTimeout', | |
| this._fakeTimerAPIs.clearTimeout | |
| ); | |
| if (typeof global.requestAnimationFrame === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'requestAnimationFrame', | |
| this._fakeTimerAPIs.requestAnimationFrame | |
| ); | |
| } | |
| if (typeof global.setImmediate === 'function') { | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setImmediate', | |
| this._fakeTimerAPIs.setImmediate | |
| ); | |
| } | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setInterval', | |
| this._fakeTimerAPIs.setInterval | |
| ); | |
| (0, _jestUtil().setGlobal)( | |
| global, | |
| 'setTimeout', | |
| this._fakeTimerAPIs.setTimeout | |
| ); | |
| global.process.nextTick = this._fakeTimerAPIs.nextTick; | |
| } | |
| getTimerCount() { | |
| this._checkFakeTimers(); | |
| return this._timers.size + this._immediates.length + this._ticks.length; | |
| } | |
| _checkFakeTimers() { | |
| var _this$_fakeTimerAPIs; | |
| if ( | |
| this._global.setTimeout !== | |
| ((_this$_fakeTimerAPIs = this._fakeTimerAPIs) === null || | |
| _this$_fakeTimerAPIs === void 0 | |
| ? void 0 | |
| : _this$_fakeTimerAPIs.setTimeout) | |
| ) { | |
| this._global.console.warn( | |
| 'A function to advance timers was called but the timers API is not ' + | |
| 'mocked with fake timers. Call `jest.useFakeTimers()` in this ' + | |
| 'test or enable fake timers globally by setting ' + | |
| '`"timers": "fake"` in ' + | |
| 'the configuration file. This warning is likely a result of a ' + | |
| 'default configuration change in Jest 15.\n\n' + | |
| 'Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15\n' + | |
| 'Stack Trace:\n' + | |
| (0, _jestMessageUtil().formatStackTrace)( | |
| new Error().stack, | |
| this._config, | |
| { | |
| noStackTrace: false | |
| } | |
| ) | |
| ); | |
| } | |
| } | |
| _createMocks() { | |
| const fn = ( | |
| impl // @ts-expect-error TODO: figure out better typings here | |
| ) => this._moduleMocker.fn().mockImplementation(impl); | |
| const promisifiableFakeSetTimeout = fn(this._fakeSetTimeout.bind(this)); // @ts-expect-error TODO: figure out better typings here | |
| promisifiableFakeSetTimeout[_util().default.promisify.custom] = ( | |
| delay, | |
| arg | |
| ) => | |
| new Promise(resolve => promisifiableFakeSetTimeout(resolve, delay, arg)); // TODO: add better typings; these are mocks, but typed as regular timers | |
| this._fakeTimerAPIs = { | |
| cancelAnimationFrame: fn(this._fakeClearTimer.bind(this)), | |
| clearImmediate: fn(this._fakeClearImmediate.bind(this)), | |
| clearInterval: fn(this._fakeClearTimer.bind(this)), | |
| clearTimeout: fn(this._fakeClearTimer.bind(this)), | |
| nextTick: fn(this._fakeNextTick.bind(this)), | |
| // @ts-expect-error TODO: figure out better typings here | |
| requestAnimationFrame: fn(this._fakeRequestAnimationFrame.bind(this)), | |
| // @ts-expect-error TODO: figure out better typings here | |
| setImmediate: fn(this._fakeSetImmediate.bind(this)), | |
| // @ts-expect-error TODO: figure out better typings here | |
| setInterval: fn(this._fakeSetInterval.bind(this)), | |
| // @ts-expect-error TODO: figure out better typings here | |
| setTimeout: promisifiableFakeSetTimeout | |
| }; | |
| } | |
| _fakeClearTimer(timerRef) { | |
| const uuid = this._timerConfig.refToId(timerRef); | |
| if (uuid) { | |
| this._timers.delete(String(uuid)); | |
| } | |
| } | |
| _fakeClearImmediate(uuid) { | |
| this._immediates = this._immediates.filter( | |
| immediate => immediate.uuid !== uuid | |
| ); | |
| } | |
| _fakeNextTick(callback, ...args) { | |
| if (this._disposed) { | |
| return; | |
| } | |
| const uuid = String(this._uuidCounter++); | |
| this._ticks.push({ | |
| callback: () => callback.apply(null, args), | |
| uuid | |
| }); | |
| const cancelledTicks = this._cancelledTicks; | |
| this._timerAPIs.nextTick(() => { | |
| if (!cancelledTicks.hasOwnProperty(uuid)) { | |
| // Callback may throw, so update the map prior calling. | |
| cancelledTicks[uuid] = true; | |
| callback.apply(null, args); | |
| } | |
| }); | |
| } | |
| _fakeRequestAnimationFrame(callback) { | |
| return this._fakeSetTimeout(() => { | |
| // TODO: Use performance.now() once it's mocked | |
| callback(this._now); | |
| }, 1000 / 60); | |
| } | |
| _fakeSetImmediate(callback, ...args) { | |
| if (this._disposed) { | |
| return null; | |
| } | |
| const uuid = String(this._uuidCounter++); | |
| this._immediates.push({ | |
| callback: () => callback.apply(null, args), | |
| uuid | |
| }); | |
| this._timerAPIs.setImmediate(() => { | |
| if (this._immediates.find(x => x.uuid === uuid)) { | |
| try { | |
| callback.apply(null, args); | |
| } finally { | |
| this._fakeClearImmediate(uuid); | |
| } | |
| } | |
| }); | |
| return uuid; | |
| } | |
| _fakeSetInterval(callback, intervalDelay, ...args) { | |
| if (this._disposed) { | |
| return null; | |
| } | |
| if (intervalDelay == null) { | |
| intervalDelay = 0; | |
| } | |
| const uuid = this._uuidCounter++; | |
| this._timers.set(String(uuid), { | |
| callback: () => callback.apply(null, args), | |
| expiry: this._now + intervalDelay, | |
| interval: intervalDelay, | |
| type: 'interval' | |
| }); | |
| return this._timerConfig.idToRef(uuid); | |
| } | |
| _fakeSetTimeout(callback, delay, ...args) { | |
| if (this._disposed) { | |
| return null; | |
| } // eslint-disable-next-line no-bitwise | |
| delay = Number(delay) | 0; | |
| const uuid = this._uuidCounter++; | |
| this._timers.set(String(uuid), { | |
| callback: () => callback.apply(null, args), | |
| expiry: this._now + delay, | |
| interval: undefined, | |
| type: 'timeout' | |
| }); | |
| return this._timerConfig.idToRef(uuid); | |
| } | |
| _getNextTimerHandle() { | |
| let nextTimerHandle = null; | |
| let soonestTime = MS_IN_A_YEAR; | |
| this._timers.forEach((timer, uuid) => { | |
| if (timer.expiry < soonestTime) { | |
| soonestTime = timer.expiry; | |
| nextTimerHandle = uuid; | |
| } | |
| }); | |
| return nextTimerHandle; | |
| } | |
| _runTimerHandle(timerHandle) { | |
| const timer = this._timers.get(timerHandle); | |
| if (!timer) { | |
| return; | |
| } | |
| switch (timer.type) { | |
| case 'timeout': | |
| this._timers.delete(timerHandle); | |
| timer.callback(); | |
| break; | |
| case 'interval': | |
| timer.expiry = this._now + (timer.interval || 0); | |
| timer.callback(); | |
| break; | |
| default: | |
| throw new Error('Unexpected timer type: ' + timer.type); | |
| } | |
| } | |
| } | |
| exports.default = FakeTimers; | |