123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', {
- value: true
- });
- exports.default = void 0;
- function _jestMessageUtil() {
- const data = require('jest-message-util');
- _jestMessageUtil = function _jestMessageUtil() {
- return data;
- };
- return data;
- }
- 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; // TODO: Copied from `jest-util` to avoid cyclic dependency. Import from `jest-util` in the next major
- const setGlobal = (globalToMutate, key, value) => {
- // @ts-ignore: no index
- globalToMutate[key] = value;
- };
- class FakeTimers {
- constructor({global, moduleMocker, timerConfig, config, maxLoops}) {
- _defineProperty(this, '_cancelledImmediates', void 0);
- _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 = {
- clearImmediate: global.clearImmediate,
- clearInterval: global.clearInterval,
- clearTimeout: global.clearTimeout,
- nextTick: global.process && global.process.nextTick,
- setImmediate: global.setImmediate,
- setInterval: global.setInterval,
- setTimeout: global.setTimeout
- };
- this.reset();
- this._createMocks();
- }
- clearAllTimers() {
- this._immediates.forEach(immediate =>
- this._fakeClearImmediate(immediate.uuid)
- );
- this._timers.clear();
- }
- dispose() {
- this._disposed = true;
- this.clearAllTimers();
- }
- reset() {
- this._cancelledTicks = {};
- this._cancelledImmediates = {};
- 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) {
- if (!this._cancelledImmediates.hasOwnProperty(immediate.uuid)) {
- // Callback may throw, so update the map prior calling.
- this._cancelledImmediates[immediate.uuid] = true;
- immediate.callback();
- }
- }
- 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;
- setGlobal(global, 'clearImmediate', this._timerAPIs.clearImmediate);
- setGlobal(global, 'clearInterval', this._timerAPIs.clearInterval);
- setGlobal(global, 'clearTimeout', this._timerAPIs.clearTimeout);
- setGlobal(global, 'setImmediate', this._timerAPIs.setImmediate);
- setGlobal(global, 'setInterval', this._timerAPIs.setInterval);
- setGlobal(global, 'setTimeout', this._timerAPIs.setTimeout);
- global.process.nextTick = this._timerAPIs.nextTick;
- }
- useFakeTimers() {
- this._createMocks();
- const global = this._global;
- setGlobal(global, 'clearImmediate', this._fakeTimerAPIs.clearImmediate);
- setGlobal(global, 'clearInterval', this._fakeTimerAPIs.clearInterval);
- setGlobal(global, 'clearTimeout', this._fakeTimerAPIs.clearTimeout);
- setGlobal(global, 'setImmediate', this._fakeTimerAPIs.setImmediate);
- setGlobal(global, 'setInterval', this._fakeTimerAPIs.setInterval);
- 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() {
- if (this._global.setTimeout !== 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.html\n` +
- `Stack Trace:\n` +
- (0, _jestMessageUtil().formatStackTrace)(
- new Error().stack,
- this._config,
- {
- noStackTrace: false
- }
- )
- );
- }
- }
- _createMocks() {
- const fn = (
- impl // @ts-ignore TODO: figure out better typings here
- ) => this._moduleMocker.fn().mockImplementation(impl); // TODO: add better typings; these are mocks, but typed as regular timers
- this._fakeTimerAPIs = {
- clearImmediate: fn(this._fakeClearImmediate.bind(this)),
- clearInterval: fn(this._fakeClearTimer.bind(this)),
- clearTimeout: fn(this._fakeClearTimer.bind(this)),
- nextTick: fn(this._fakeNextTick.bind(this)),
- setImmediate: fn(this._fakeSetImmediate.bind(this)),
- setInterval: fn(this._fakeSetInterval.bind(this)),
- setTimeout: fn(this._fakeSetTimeout.bind(this))
- };
- }
- _fakeClearTimer(timerRef) {
- const uuid = this._timerConfig.refToId(timerRef);
- if (uuid) {
- this._timers.delete(String(uuid));
- }
- }
- _fakeClearImmediate(uuid) {
- this._cancelledImmediates[uuid] = true;
- }
- _fakeNextTick(callback, ...args) {
- if (this._disposed) {
- return;
- }
- const uuid = String(this._uuidCounter++);
- this._ticks.push({
- callback: (function(_callback) {
- function callback() {
- return _callback.apply(this, arguments);
- }
- callback.toString = function() {
- return _callback.toString();
- };
- return 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);
- }
- });
- }
- _fakeSetImmediate(callback, ...args) {
- if (this._disposed) {
- return null;
- }
- const uuid = this._uuidCounter++;
- this._immediates.push({
- callback: (function(_callback2) {
- function callback() {
- return _callback2.apply(this, arguments);
- }
- callback.toString = function() {
- return _callback2.toString();
- };
- return callback;
- })(() => callback.apply(null, args)),
- uuid: String(uuid)
- });
- const cancelledImmediates = this._cancelledImmediates;
- this._timerAPIs.setImmediate(() => {
- if (!cancelledImmediates.hasOwnProperty(uuid)) {
- // Callback may throw, so update the map prior calling.
- cancelledImmediates[String(uuid)] = true;
- callback.apply(null, args);
- }
- });
- 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: (function(_callback3) {
- function callback() {
- return _callback3.apply(this, arguments);
- }
- callback.toString = function() {
- return _callback3.toString();
- };
- return 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: (function(_callback4) {
- function callback() {
- return _callback4.apply(this, arguments);
- }
- callback.toString = function() {
- return _callback4.toString();
- };
- return 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':
- const callback = timer.callback;
- this._timers.delete(timerHandle);
- 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;
|