aboutsummaryrefslogtreecommitdiff
path: root/node_modules/@sinonjs/fake-timers/src
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.h.kronqvist@gmail.com>2022-03-05 19:02:27 +0200
committerJoel Kronqvist <joel.h.kronqvist@gmail.com>2022-03-05 19:02:27 +0200
commit5d309ff52cd399a6b71968a6b9a70c8ac0b98981 (patch)
tree360f7eb50f956e2367ef38fa1fc6ac7ac5258042 /node_modules/@sinonjs/fake-timers/src
parentb500a50f1b97d93c98b36ed9a980f8188d648147 (diff)
downloadLYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.tar.gz
LYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.zip
Added node_modules for the updating to work properly.
Diffstat (limited to 'node_modules/@sinonjs/fake-timers/src')
-rw-r--r--node_modules/@sinonjs/fake-timers/src/fake-timers-src.js1728
1 files changed, 1728 insertions, 0 deletions
diff --git a/node_modules/@sinonjs/fake-timers/src/fake-timers-src.js b/node_modules/@sinonjs/fake-timers/src/fake-timers-src.js
new file mode 100644
index 0000000..e1a0a1e
--- /dev/null
+++ b/node_modules/@sinonjs/fake-timers/src/fake-timers-src.js
@@ -0,0 +1,1728 @@
+"use strict";
+
+const globalObject = require("@sinonjs/commons").global;
+
+/**
+ * @typedef {object} IdleDeadline
+ * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
+ * @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
+ */
+
+/**
+ * Queues a function to be called during a browser's idle periods
+ *
+ * @callback RequestIdleCallback
+ * @param {function(IdleDeadline)} callback
+ * @param {{timeout: number}} options - an options object
+ * @returns {number} the id
+ */
+
+/**
+ * @callback NextTick
+ * @param {VoidVarArgsFunc} callback - the callback to run
+ * @param {...*} arguments - optional arguments to call the callback with
+ * @returns {void}
+ */
+
+/**
+ * @callback SetImmediate
+ * @param {VoidVarArgsFunc} callback - the callback to run
+ * @param {...*} arguments - optional arguments to call the callback with
+ * @returns {NodeImmediate}
+ */
+
+/**
+ * @callback VoidVarArgsFunc
+ * @param {...*} callback - the callback to run
+ * @returns {void}
+ */
+
+/**
+ * @typedef RequestAnimationFrame
+ * @property {function(number):void} requestAnimationFrame
+ * @returns {number} - the id
+ */
+
+/**
+ * @typedef Performance
+ * @property {function(): number} now
+ */
+
+/* eslint-disable jsdoc/require-property-description */
+/**
+ * @typedef {object} Clock
+ * @property {number} now - the current time
+ * @property {Date} Date - the Date constructor
+ * @property {number} loopLimit - the maximum number of timers before assuming an infinite loop
+ * @property {RequestIdleCallback} requestIdleCallback
+ * @property {function(number):void} cancelIdleCallback
+ * @property {setTimeout} setTimeout
+ * @property {clearTimeout} clearTimeout
+ * @property {NextTick} nextTick
+ * @property {queueMicrotask} queueMicrotask
+ * @property {setInterval} setInterval
+ * @property {clearInterval} clearInterval
+ * @property {SetImmediate} setImmediate
+ * @property {function(NodeImmediate):void} clearImmediate
+ * @property {function():number} countTimers
+ * @property {RequestAnimationFrame} requestAnimationFrame
+ * @property {function(number):void} cancelAnimationFrame
+ * @property {function():void} runMicrotasks
+ * @property {function(string | number): number} tick
+ * @property {function(string | number): Promise<number>} tickAsync
+ * @property {function(): number} next
+ * @property {function(): Promise<number>} nextAsync
+ * @property {function(): number} runAll
+ * @property {function(): number} runToFrame
+ * @property {function(): Promise<number>} runAllAsync
+ * @property {function(): number} runToLast
+ * @property {function(): Promise<number>} runToLastAsync
+ * @property {function(): void} reset
+ * @property {function(number | Date): void} setSystemTime
+ * @property {Performance} performance
+ * @property {function(number[]): number[]} hrtime - process.hrtime (legacy)
+ * @property {function(): void} uninstall Uninstall the clock.
+ * @property {Function[]} methods - the methods that are faked
+ * @property {boolean} [shouldClearNativeTimers] inherited from config
+ */
+/* eslint-enable jsdoc/require-property-description */
+
+/**
+ * Configuration object for the `install` method.
+ *
+ * @typedef {object} Config
+ * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
+ * @property {string[]} [toFake] names of the methods that should be faked.
+ * @property {number} [loopLimit] the maximum number of timers that will be run when calling runAll()
+ * @property {boolean} [shouldAdvanceTime] tells FakeTimers to increment mocked time automatically (default false)
+ * @property {number} [advanceTimeDelta] increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
+ * @property {boolean} [shouldClearNativeTimers] forwards clear timer calls to native functions if they are not fakes (default: false)
+ */
+
+/* eslint-disable jsdoc/require-property-description */
+/**
+ * The internal structure to describe a scheduled fake timer
+ *
+ * @typedef {object} Timer
+ * @property {Function} func
+ * @property {*[]} args
+ * @property {number} delay
+ * @property {number} callAt
+ * @property {number} createdAt
+ * @property {boolean} immediate
+ * @property {number} id
+ * @property {Error} [error]
+ */
+
+/**
+ * A Node timer
+ *
+ * @typedef {object} NodeImmediate
+ * @property {function(): boolean} hasRef
+ * @property {function(): NodeImmediate} ref
+ * @property {function(): NodeImmediate} unref
+ */
+/* eslint-enable jsdoc/require-property-description */
+
+/* eslint-disable complexity */
+
+/**
+ * Mocks available features in the specified global namespace.
+ *
+ * @param {*} _global Namespace to mock (e.g. `window`)
+ * @returns {FakeTimers}
+ */
+function withGlobal(_global) {
+ const userAgent = _global.navigator && _global.navigator.userAgent;
+ const isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
+ const maxTimeout = Math.pow(2, 31) - 1; //see https://heycam.github.io/webidl/#abstract-opdef-converttoint
+ const idCounterStart = 1e12; // arbitrarily large number to avoid collisions with native timer IDs
+ const NOOP = function () {
+ return undefined;
+ };
+ const NOOP_ARRAY = function () {
+ return [];
+ };
+ const timeoutResult = _global.setTimeout(NOOP, 0);
+ const addTimerReturnsObject = typeof timeoutResult === "object";
+ const hrtimePresent =
+ _global.process && typeof _global.process.hrtime === "function";
+ const hrtimeBigintPresent =
+ hrtimePresent && typeof _global.process.hrtime.bigint === "function";
+ const nextTickPresent =
+ _global.process && typeof _global.process.nextTick === "function";
+ const utilPromisify = _global.process && require("util").promisify;
+ const performancePresent =
+ _global.performance && typeof _global.performance.now === "function";
+ const hasPerformancePrototype =
+ _global.Performance &&
+ (typeof _global.Performance).match(/^(function|object)$/);
+ const queueMicrotaskPresent = _global.hasOwnProperty("queueMicrotask");
+ const requestAnimationFramePresent =
+ _global.requestAnimationFrame &&
+ typeof _global.requestAnimationFrame === "function";
+ const cancelAnimationFramePresent =
+ _global.cancelAnimationFrame &&
+ typeof _global.cancelAnimationFrame === "function";
+ const requestIdleCallbackPresent =
+ _global.requestIdleCallback &&
+ typeof _global.requestIdleCallback === "function";
+ const cancelIdleCallbackPresent =
+ _global.cancelIdleCallback &&
+ typeof _global.cancelIdleCallback === "function";
+ const setImmediatePresent =
+ _global.setImmediate && typeof _global.setImmediate === "function";
+
+ // Make properties writable in IE, as per
+ // https://www.adequatelygood.com/Replacing-setTimeout-Globally.html
+ /* eslint-disable no-self-assign */
+ if (isRunningInIE) {
+ _global.setTimeout = _global.setTimeout;
+ _global.clearTimeout = _global.clearTimeout;
+ _global.setInterval = _global.setInterval;
+ _global.clearInterval = _global.clearInterval;
+ _global.Date = _global.Date;
+ }
+
+ // setImmediate is not a standard function
+ // avoid adding the prop to the window object if not present
+ if (setImmediatePresent) {
+ _global.setImmediate = _global.setImmediate;
+ _global.clearImmediate = _global.clearImmediate;
+ }
+ /* eslint-enable no-self-assign */
+
+ _global.clearTimeout(timeoutResult);
+
+ const NativeDate = _global.Date;
+ let uniqueTimerId = idCounterStart;
+
+ /**
+ * @param {number} num
+ * @returns {boolean}
+ */
+ function isNumberFinite(num) {
+ if (Number.isFinite) {
+ return Number.isFinite(num);
+ }
+
+ return isFinite(num);
+ }
+
+ let isNearInfiniteLimit = false;
+
+ /**
+ * @param {Clock} clock
+ * @param {number} i
+ */
+ function checkIsNearInfiniteLimit(clock, i) {
+ if (clock.loopLimit && i === clock.loopLimit - 1) {
+ isNearInfiniteLimit = true;
+ }
+ }
+
+ /**
+ *
+ */
+ function resetIsNearInfiniteLimit() {
+ isNearInfiniteLimit = false;
+ }
+
+ /**
+ * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
+ * number of milliseconds. This is used to support human-readable strings passed
+ * to clock.tick()
+ *
+ * @param {string} str
+ * @returns {number}
+ */
+ function parseTime(str) {
+ if (!str) {
+ return 0;
+ }
+
+ const strings = str.split(":");
+ const l = strings.length;
+ let i = l;
+ let ms = 0;
+ let parsed;
+
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+ throw new Error(
+ "tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits"
+ );
+ }
+
+ while (i--) {
+ parsed = parseInt(strings[i], 10);
+
+ if (parsed >= 60) {
+ throw new Error(`Invalid time ${str}`);
+ }
+
+ ms += parsed * Math.pow(60, l - i - 1);
+ }
+
+ return ms * 1000;
+ }
+
+ /**
+ * Get the decimal part of the millisecond value as nanoseconds
+ *
+ * @param {number} msFloat the number of milliseconds
+ * @returns {number} an integer number of nanoseconds in the range [0,1e6)
+ *
+ * Example: nanoRemainer(123.456789) -> 456789
+ */
+ function nanoRemainder(msFloat) {
+ const modulo = 1e6;
+ const remainder = (msFloat * 1e6) % modulo;
+ const positiveRemainder =
+ remainder < 0 ? remainder + modulo : remainder;
+
+ return Math.floor(positiveRemainder);
+ }
+
+ /**
+ * Used to grok the `now` parameter to createClock.
+ *
+ * @param {Date|number} epoch the system time
+ * @returns {number}
+ */
+ function getEpoch(epoch) {
+ if (!epoch) {
+ return 0;
+ }
+ if (typeof epoch.getTime === "function") {
+ return epoch.getTime();
+ }
+ if (typeof epoch === "number") {
+ return epoch;
+ }
+ throw new TypeError("now should be milliseconds since UNIX epoch");
+ }
+
+ /**
+ * @param {number} from
+ * @param {number} to
+ * @param {Timer} timer
+ * @returns {boolean}
+ */
+ function inRange(from, to, timer) {
+ return timer && timer.callAt >= from && timer.callAt <= to;
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {Timer} job
+ */
+ function getInfiniteLoopError(clock, job) {
+ const infiniteLoopError = new Error(
+ `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!`
+ );
+
+ if (!job.error) {
+ return infiniteLoopError;
+ }
+
+ // pattern never matched in Node
+ const computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/;
+ let clockMethodPattern = new RegExp(
+ String(Object.keys(clock).join("|"))
+ );
+
+ if (addTimerReturnsObject) {
+ // node.js environment
+ clockMethodPattern = new RegExp(
+ `\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+`
+ );
+ }
+
+ let matchedLineIndex = -1;
+ job.error.stack.split("\n").some(function (line, i) {
+ // If we've matched a computed target line (e.g. setTimeout) then we
+ // don't need to look any further. Return true to stop iterating.
+ const matchedComputedTarget = line.match(computedTargetPattern);
+ /* istanbul ignore if */
+ if (matchedComputedTarget) {
+ matchedLineIndex = i;
+ return true;
+ }
+
+ // If we've matched a clock method line, then there may still be
+ // others further down the trace. Return false to keep iterating.
+ const matchedClockMethod = line.match(clockMethodPattern);
+ if (matchedClockMethod) {
+ matchedLineIndex = i;
+ return false;
+ }
+
+ // If we haven't matched anything on this line, but we matched
+ // previously and set the matched line index, then we can stop.
+ // If we haven't matched previously, then we should keep iterating.
+ return matchedLineIndex >= 0;
+ });
+
+ const stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${
+ job.func.name || "anonymous"
+ }\n${job.error.stack
+ .split("\n")
+ .slice(matchedLineIndex + 1)
+ .join("\n")}`;
+
+ try {
+ Object.defineProperty(infiniteLoopError, "stack", {
+ value: stack,
+ });
+ } catch (e) {
+ // noop
+ }
+
+ return infiniteLoopError;
+ }
+
+ /**
+ * @param {Date} target
+ * @param {Date} source
+ * @returns {Date} the target after modifications
+ */
+ function mirrorDateProperties(target, source) {
+ let prop;
+ for (prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ target[prop] = source[prop];
+ }
+ }
+
+ // set special now implementation
+ if (source.now) {
+ target.now = function now() {
+ return target.clock.now;
+ };
+ } else {
+ delete target.now;
+ }
+
+ // set special toSource implementation
+ if (source.toSource) {
+ target.toSource = function toSource() {
+ return source.toSource();
+ };
+ } else {
+ delete target.toSource;
+ }
+
+ // set special toString implementation
+ target.toString = function toString() {
+ return source.toString();
+ };
+
+ target.prototype = source.prototype;
+ target.parse = source.parse;
+ target.UTC = source.UTC;
+ target.prototype.toUTCString = source.prototype.toUTCString;
+
+ return target;
+ }
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function createDate() {
+ /**
+ * @param {number} year
+ * @param {number} month
+ * @param {number} date
+ * @param {number} hour
+ * @param {number} minute
+ * @param {number} second
+ * @param {number} ms
+ *
+ * @returns {Date}
+ */
+ function ClockDate(year, month, date, hour, minute, second, ms) {
+ // the Date constructor called as a function, ref Ecma-262 Edition 5.1, section 15.9.2.
+ // This remains so in the 10th edition of 2019 as well.
+ if (!(this instanceof ClockDate)) {
+ return new NativeDate(ClockDate.clock.now).toString();
+ }
+
+ // if Date is called as a constructor with 'new' keyword
+ // Defensive and verbose to avoid potential harm in passing
+ // explicit undefined when user does not pass argument
+ switch (arguments.length) {
+ case 0:
+ return new NativeDate(ClockDate.clock.now);
+ case 1:
+ return new NativeDate(year);
+ case 2:
+ return new NativeDate(year, month);
+ case 3:
+ return new NativeDate(year, month, date);
+ case 4:
+ return new NativeDate(year, month, date, hour);
+ case 5:
+ return new NativeDate(year, month, date, hour, minute);
+ case 6:
+ return new NativeDate(
+ year,
+ month,
+ date,
+ hour,
+ minute,
+ second
+ );
+ default:
+ return new NativeDate(
+ year,
+ month,
+ date,
+ hour,
+ minute,
+ second,
+ ms
+ );
+ }
+ }
+
+ return mirrorDateProperties(ClockDate, NativeDate);
+ }
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function enqueueJob(clock, job) {
+ // enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
+ if (!clock.jobs) {
+ clock.jobs = [];
+ }
+ clock.jobs.push(job);
+ }
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function runJobs(clock) {
+ // runs all microtick-deferred tasks - ecma262/#sec-runjobs
+ if (!clock.jobs) {
+ return;
+ }
+ for (let i = 0; i < clock.jobs.length; i++) {
+ const job = clock.jobs[i];
+ job.func.apply(null, job.args);
+
+ checkIsNearInfiniteLimit(clock, i);
+ if (clock.loopLimit && i > clock.loopLimit) {
+ throw getInfiniteLoopError(clock, job);
+ }
+ }
+ resetIsNearInfiniteLimit();
+ clock.jobs = [];
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {Timer} timer
+ * @returns {number} id of the created timer
+ */
+ function addTimer(clock, timer) {
+ if (timer.func === undefined) {
+ throw new Error("Callback must be provided to timer calls");
+ }
+
+ if (addTimerReturnsObject) {
+ // Node.js environment
+ if (typeof timer.func !== "function") {
+ throw new TypeError(
+ `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${
+ timer.func
+ } of type ${typeof timer.func}`
+ );
+ }
+ }
+
+ if (isNearInfiniteLimit) {
+ timer.error = new Error();
+ }
+
+ timer.type = timer.immediate ? "Immediate" : "Timeout";
+
+ if (timer.hasOwnProperty("delay")) {
+ if (typeof timer.delay !== "number") {
+ timer.delay = parseInt(timer.delay, 10);
+ }
+
+ if (!isNumberFinite(timer.delay)) {
+ timer.delay = 0;
+ }
+ timer.delay = timer.delay > maxTimeout ? 1 : timer.delay;
+ timer.delay = Math.max(0, timer.delay);
+ }
+
+ if (timer.hasOwnProperty("interval")) {
+ timer.type = "Interval";
+ timer.interval = timer.interval > maxTimeout ? 1 : timer.interval;
+ }
+
+ if (timer.hasOwnProperty("animation")) {
+ timer.type = "AnimationFrame";
+ timer.animation = true;
+ }
+
+ if (timer.hasOwnProperty("idleCallback")) {
+ timer.type = "IdleCallback";
+ timer.idleCallback = true;
+ }
+
+ if (!clock.timers) {
+ clock.timers = {};
+ }
+
+ timer.id = uniqueTimerId++;
+ timer.createdAt = clock.now;
+ timer.callAt =
+ clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
+
+ clock.timers[timer.id] = timer;
+
+ if (addTimerReturnsObject) {
+ const res = {
+ ref: function () {
+ return res;
+ },
+ unref: function () {
+ return res;
+ },
+ refresh: function () {
+ clearTimeout(timer.id);
+ const args = [timer.func, timer.delay].concat(timer.args);
+ return setTimeout.apply(null, args);
+ },
+ [Symbol.toPrimitive]: function () {
+ return timer.id;
+ },
+ };
+ return res;
+ }
+
+ return timer.id;
+ }
+
+ /* eslint consistent-return: "off" */
+ /**
+ * Timer comparitor
+ *
+ * @param {Timer} a
+ * @param {Timer} b
+ * @returns {number}
+ */
+ function compareTimers(a, b) {
+ // Sort first by absolute timing
+ if (a.callAt < b.callAt) {
+ return -1;
+ }
+ if (a.callAt > b.callAt) {
+ return 1;
+ }
+
+ // Sort next by immediate, immediate timers take precedence
+ if (a.immediate && !b.immediate) {
+ return -1;
+ }
+ if (!a.immediate && b.immediate) {
+ return 1;
+ }
+
+ // Sort next by creation time, earlier-created timers take precedence
+ if (a.createdAt < b.createdAt) {
+ return -1;
+ }
+ if (a.createdAt > b.createdAt) {
+ return 1;
+ }
+
+ // Sort next by id, lower-id timers take precedence
+ if (a.id < b.id) {
+ return -1;
+ }
+ if (a.id > b.id) {
+ return 1;
+ }
+
+ // As timer ids are unique, no fallback `0` is necessary
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {number} from
+ * @param {number} to
+ *
+ * @returns {Timer}
+ */
+ function firstTimerInRange(clock, from, to) {
+ const timers = clock.timers;
+ let timer = null;
+ let id, isInRange;
+
+ for (id in timers) {
+ if (timers.hasOwnProperty(id)) {
+ isInRange = inRange(from, to, timers[id]);
+
+ if (
+ isInRange &&
+ (!timer || compareTimers(timer, timers[id]) === 1)
+ ) {
+ timer = timers[id];
+ }
+ }
+ }
+
+ return timer;
+ }
+
+ /**
+ * @param {Clock} clock
+ * @returns {Timer}
+ */
+ function firstTimer(clock) {
+ const timers = clock.timers;
+ let timer = null;
+ let id;
+
+ for (id in timers) {
+ if (timers.hasOwnProperty(id)) {
+ if (!timer || compareTimers(timer, timers[id]) === 1) {
+ timer = timers[id];
+ }
+ }
+ }
+
+ return timer;
+ }
+
+ /**
+ * @param {Clock} clock
+ * @returns {Timer}
+ */
+ function lastTimer(clock) {
+ const timers = clock.timers;
+ let timer = null;
+ let id;
+
+ for (id in timers) {
+ if (timers.hasOwnProperty(id)) {
+ if (!timer || compareTimers(timer, timers[id]) === -1) {
+ timer = timers[id];
+ }
+ }
+ }
+
+ return timer;
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {Timer} timer
+ */
+ function callTimer(clock, timer) {
+ if (typeof timer.interval === "number") {
+ clock.timers[timer.id].callAt += timer.interval;
+ } else {
+ delete clock.timers[timer.id];
+ }
+
+ if (typeof timer.func === "function") {
+ timer.func.apply(null, timer.args);
+ } else {
+ /* eslint no-eval: "off" */
+ const eval2 = eval;
+ (function () {
+ eval2(timer.func);
+ })();
+ }
+ }
+
+ /**
+ * Gets clear handler name for a given timer type
+ * @param {string} ttype
+ */
+ function getClearHandler(ttype) {
+ if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
+ return `cancel${ttype}`;
+ }
+ return `clear${ttype}`;
+ }
+
+ /**
+ * Gets schedule handler name for a given timer type
+ * @param {string} ttype
+ */
+ function getScheduleHandler(ttype) {
+ if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
+ return `request${ttype}`;
+ }
+ return `set${ttype}`;
+ }
+
+ /**
+ * Creates an anonymous function to warn only once
+ */
+ function createWarnOnce() {
+ let calls = 0;
+ return function (msg) {
+ // eslint-disable-next-line
+ !calls++ && console.warn(msg);
+ };
+ }
+ const warnOnce = createWarnOnce();
+
+ /**
+ * @param {Clock} clock
+ * @param {number} timerId
+ * @param {string} ttype
+ */
+ function clearTimer(clock, timerId, ttype) {
+ if (!timerId) {
+ // null appears to be allowed in most browsers, and appears to be
+ // relied upon by some libraries, like Bootstrap carousel
+ return;
+ }
+
+ if (!clock.timers) {
+ clock.timers = {};
+ }
+
+ // in Node, the ID is stored as the primitive value for `Timeout` objects
+ // for `Immediate` objects, no ID exists, so it gets coerced to NaN
+ const id = Number(timerId);
+
+ if (Number.isNaN(id) || id < idCounterStart) {
+ const handlerName = getClearHandler(ttype);
+
+ if (clock.shouldClearNativeTimers === true) {
+ const nativeHandler = clock[`_${handlerName}`];
+ return typeof nativeHandler === "function"
+ ? nativeHandler(timerId)
+ : undefined;
+ }
+ warnOnce(
+ `FakeTimers: ${handlerName} was invoked to clear a native timer instead of one created by this library.` +
+ "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`."
+ );
+ }
+
+ if (clock.timers.hasOwnProperty(id)) {
+ // check that the ID matches a timer of the correct type
+ const timer = clock.timers[id];
+ if (
+ timer.type === ttype ||
+ (timer.type === "Timeout" && ttype === "Interval") ||
+ (timer.type === "Interval" && ttype === "Timeout")
+ ) {
+ delete clock.timers[id];
+ } else {
+ const clear = getClearHandler(ttype);
+ const schedule = getScheduleHandler(timer.type);
+ throw new Error(
+ `Cannot clear timer: timer created with ${schedule}() but cleared with ${clear}()`
+ );
+ }
+ }
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {Config} config
+ * @returns {Timer[]}
+ */
+ function uninstall(clock, config) {
+ let method, i, l;
+ const installedHrTime = "_hrtime";
+ const installedNextTick = "_nextTick";
+
+ for (i = 0, l = clock.methods.length; i < l; i++) {
+ method = clock.methods[i];
+ if (method === "hrtime" && _global.process) {
+ _global.process.hrtime = clock[installedHrTime];
+ } else if (method === "nextTick" && _global.process) {
+ _global.process.nextTick = clock[installedNextTick];
+ } else if (method === "performance") {
+ const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
+ clock,
+ `_${method}`
+ );
+ if (
+ originalPerfDescriptor &&
+ originalPerfDescriptor.get &&
+ !originalPerfDescriptor.set
+ ) {
+ Object.defineProperty(
+ _global,
+ method,
+ originalPerfDescriptor
+ );
+ } else if (originalPerfDescriptor.configurable) {
+ _global[method] = clock[`_${method}`];
+ }
+ } else {
+ if (_global[method] && _global[method].hadOwnProperty) {
+ _global[method] = clock[`_${method}`];
+ } else {
+ try {
+ delete _global[method];
+ } catch (ignore) {
+ /* eslint no-empty: "off" */
+ }
+ }
+ }
+ }
+
+ if (config.shouldAdvanceTime === true) {
+ _global.clearInterval(clock.attachedInterval);
+ }
+
+ // Prevent multiple executions which will completely remove these props
+ clock.methods = [];
+
+ // return pending timers, to enable checking what timers remained on uninstall
+ if (!clock.timers) {
+ return [];
+ }
+ return Object.keys(clock.timers).map(function mapper(key) {
+ return clock.timers[key];
+ });
+ }
+
+ /**
+ * @param {object} target the target containing the method to replace
+ * @param {string} method the keyname of the method on the target
+ * @param {Clock} clock
+ */
+ function hijackMethod(target, method, clock) {
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(
+ target,
+ method
+ );
+ clock[`_${method}`] = target[method];
+
+ if (method === "Date") {
+ const date = mirrorDateProperties(clock[method], target[method]);
+ target[method] = date;
+ } else if (method === "performance") {
+ const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
+ target,
+ method
+ );
+ // JSDOM has a read only performance field so we have to save/copy it differently
+ if (
+ originalPerfDescriptor &&
+ originalPerfDescriptor.get &&
+ !originalPerfDescriptor.set
+ ) {
+ Object.defineProperty(
+ clock,
+ `_${method}`,
+ originalPerfDescriptor
+ );
+
+ const perfDescriptor = Object.getOwnPropertyDescriptor(
+ clock,
+ method
+ );
+ Object.defineProperty(target, method, perfDescriptor);
+ } else {
+ target[method] = clock[method];
+ }
+ } else {
+ target[method] = function () {
+ return clock[method].apply(clock, arguments);
+ };
+
+ Object.defineProperties(
+ target[method],
+ Object.getOwnPropertyDescriptors(clock[method])
+ );
+ }
+
+ target[method].clock = clock;
+ }
+
+ /**
+ * @param {Clock} clock
+ * @param {number} advanceTimeDelta
+ */
+ function doIntervalTick(clock, advanceTimeDelta) {
+ clock.tick(advanceTimeDelta);
+ }
+
+ /**
+ * @typedef {object} Timers
+ * @property {setTimeout} setTimeout
+ * @property {clearTimeout} clearTimeout
+ * @property {setInterval} setInterval
+ * @property {clearInterval} clearInterval
+ * @property {Date} Date
+ * @property {SetImmediate=} setImmediate
+ * @property {function(NodeImmediate): void=} clearImmediate
+ * @property {function(number[]):number[]=} hrtime
+ * @property {NextTick=} nextTick
+ * @property {Performance=} performance
+ * @property {RequestAnimationFrame=} requestAnimationFrame
+ * @property {boolean=} queueMicrotask
+ * @property {function(number): void=} cancelAnimationFrame
+ * @property {RequestIdleCallback=} requestIdleCallback
+ * @property {function(number): void=} cancelIdleCallback
+ */
+
+ /** @type {Timers} */
+ const timers = {
+ setTimeout: _global.setTimeout,
+ clearTimeout: _global.clearTimeout,
+ setInterval: _global.setInterval,
+ clearInterval: _global.clearInterval,
+ Date: _global.Date,
+ };
+
+ if (setImmediatePresent) {
+ timers.setImmediate = _global.setImmediate;
+ timers.clearImmediate = _global.clearImmediate;
+ }
+
+ if (hrtimePresent) {
+ timers.hrtime = _global.process.hrtime;
+ }
+
+ if (nextTickPresent) {
+ timers.nextTick = _global.process.nextTick;
+ }
+
+ if (performancePresent) {
+ timers.performance = _global.performance;
+ }
+
+ if (requestAnimationFramePresent) {
+ timers.requestAnimationFrame = _global.requestAnimationFrame;
+ }
+
+ if (queueMicrotaskPresent) {
+ timers.queueMicrotask = true;
+ }
+
+ if (cancelAnimationFramePresent) {
+ timers.cancelAnimationFrame = _global.cancelAnimationFrame;
+ }
+
+ if (requestIdleCallbackPresent) {
+ timers.requestIdleCallback = _global.requestIdleCallback;
+ }
+
+ if (cancelIdleCallbackPresent) {
+ timers.cancelIdleCallback = _global.cancelIdleCallback;
+ }
+
+ const originalSetTimeout = _global.setImmediate || _global.setTimeout;
+
+ /**
+ * @param {Date|number} [start] the system time - non-integer values are floored
+ * @param {number} [loopLimit] maximum number of timers that will be run when calling runAll()
+ * @returns {Clock}
+ */
+ function createClock(start, loopLimit) {
+ // eslint-disable-next-line no-param-reassign
+ start = Math.floor(getEpoch(start));
+ // eslint-disable-next-line no-param-reassign
+ loopLimit = loopLimit || 1000;
+ let nanos = 0;
+ const adjustedSystemTime = [0, 0]; // [millis, nanoremainder]
+
+ if (NativeDate === undefined) {
+ throw new Error(
+ "The global scope doesn't have a `Date` object" +
+ " (see https://github.com/sinonjs/sinon/issues/1852#issuecomment-419622780)"
+ );
+ }
+
+ const clock = {
+ now: start,
+ Date: createDate(),
+ loopLimit: loopLimit,
+ };
+
+ clock.Date.clock = clock;
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function getTimeToNextFrame() {
+ return 16 - ((clock.now - start) % 16);
+ }
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function hrtime(prev) {
+ const millisSinceStart = clock.now - adjustedSystemTime[0] - start;
+ const secsSinceStart = Math.floor(millisSinceStart / 1000);
+ const remainderInNanos =
+ (millisSinceStart - secsSinceStart * 1e3) * 1e6 +
+ nanos -
+ adjustedSystemTime[1];
+
+ if (Array.isArray(prev)) {
+ if (prev[1] > 1e9) {
+ throw new TypeError(
+ "Number of nanoseconds can't exceed a billion"
+ );
+ }
+
+ const oldSecs = prev[0];
+ let nanoDiff = remainderInNanos - prev[1];
+ let secDiff = secsSinceStart - oldSecs;
+
+ if (nanoDiff < 0) {
+ nanoDiff += 1e9;
+ secDiff -= 1;
+ }
+
+ return [secDiff, nanoDiff];
+ }
+ return [secsSinceStart, remainderInNanos];
+ }
+
+ if (hrtimeBigintPresent) {
+ hrtime.bigint = function () {
+ const parts = hrtime();
+ return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]); // eslint-disable-line
+ };
+ }
+
+ clock.requestIdleCallback = function requestIdleCallback(
+ func,
+ timeout
+ ) {
+ let timeToNextIdlePeriod = 0;
+
+ if (clock.countTimers() > 0) {
+ timeToNextIdlePeriod = 50; // const for now
+ }
+
+ const result = addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay:
+ typeof timeout === "undefined"
+ ? timeToNextIdlePeriod
+ : Math.min(timeout, timeToNextIdlePeriod),
+ idleCallback: true,
+ });
+
+ return Number(result);
+ };
+
+ clock.cancelIdleCallback = function cancelIdleCallback(timerId) {
+ return clearTimer(clock, timerId, "IdleCallback");
+ };
+
+ clock.setTimeout = function setTimeout(func, timeout) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout,
+ });
+ };
+ if (typeof _global.Promise !== "undefined" && utilPromisify) {
+ clock.setTimeout[
+ utilPromisify.custom
+ ] = function promisifiedSetTimeout(timeout, arg) {
+ return new _global.Promise(function setTimeoutExecutor(
+ resolve
+ ) {
+ addTimer(clock, {
+ func: resolve,
+ args: [arg],
+ delay: timeout,
+ });
+ });
+ };
+ }
+
+ clock.clearTimeout = function clearTimeout(timerId) {
+ return clearTimer(clock, timerId, "Timeout");
+ };
+
+ clock.nextTick = function nextTick(func) {
+ return enqueueJob(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 1),
+ error: isNearInfiniteLimit ? new Error() : null,
+ });
+ };
+
+ clock.queueMicrotask = function queueMicrotask(func) {
+ return clock.nextTick(func); // explicitly drop additional arguments
+ };
+
+ clock.setInterval = function setInterval(func, timeout) {
+ // eslint-disable-next-line no-param-reassign
+ timeout = parseInt(timeout, 10);
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout,
+ interval: timeout,
+ });
+ };
+
+ clock.clearInterval = function clearInterval(timerId) {
+ return clearTimer(clock, timerId, "Interval");
+ };
+
+ if (setImmediatePresent) {
+ clock.setImmediate = function setImmediate(func) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 1),
+ immediate: true,
+ });
+ };
+
+ if (typeof _global.Promise !== "undefined" && utilPromisify) {
+ clock.setImmediate[
+ utilPromisify.custom
+ ] = function promisifiedSetImmediate(arg) {
+ return new _global.Promise(function setImmediateExecutor(
+ resolve
+ ) {
+ addTimer(clock, {
+ func: resolve,
+ args: [arg],
+ immediate: true,
+ });
+ });
+ };
+ }
+
+ clock.clearImmediate = function clearImmediate(timerId) {
+ return clearTimer(clock, timerId, "Immediate");
+ };
+ }
+
+ clock.countTimers = function countTimers() {
+ return (
+ Object.keys(clock.timers || {}).length +
+ (clock.jobs || []).length
+ );
+ };
+
+ clock.requestAnimationFrame = function requestAnimationFrame(func) {
+ const result = addTimer(clock, {
+ func: func,
+ delay: getTimeToNextFrame(),
+ args: [clock.now + getTimeToNextFrame()],
+ animation: true,
+ });
+
+ return Number(result);
+ };
+
+ clock.cancelAnimationFrame = function cancelAnimationFrame(timerId) {
+ return clearTimer(clock, timerId, "AnimationFrame");
+ };
+
+ clock.runMicrotasks = function runMicrotasks() {
+ runJobs(clock);
+ };
+
+ /**
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
+ * @param {boolean} isAsync
+ * @param {Function} resolve
+ * @param {Function} reject
+ * @returns {number|undefined} will return the new `now` value or nothing for async
+ */
+ function doTick(tickValue, isAsync, resolve, reject) {
+ const msFloat =
+ typeof tickValue === "number"
+ ? tickValue
+ : parseTime(tickValue);
+ const ms = Math.floor(msFloat);
+ const remainder = nanoRemainder(msFloat);
+ let nanosTotal = nanos + remainder;
+ let tickTo = clock.now + ms;
+
+ if (msFloat < 0) {
+ throw new TypeError("Negative ticks are not supported");
+ }
+
+ // adjust for positive overflow
+ if (nanosTotal >= 1e6) {
+ tickTo += 1;
+ nanosTotal -= 1e6;
+ }
+
+ nanos = nanosTotal;
+ let tickFrom = clock.now;
+ let previous = clock.now;
+ // ESLint fails to detect this correctly
+ /* eslint-disable prefer-const */
+ let timer,
+ firstException,
+ oldNow,
+ nextPromiseTick,
+ compensationCheck,
+ postTimerCall;
+ /* eslint-enable prefer-const */
+
+ clock.duringTick = true;
+
+ // perform microtasks
+ oldNow = clock.now;
+ runJobs(clock);
+ if (oldNow !== clock.now) {
+ // compensate for any setSystemTime() call during microtask callback
+ tickFrom += clock.now - oldNow;
+ tickTo += clock.now - oldNow;
+ }
+
+ //eslint-disable-next-line jsdoc/require-jsdoc
+ function doTickInner() {
+ // perform each timer in the requested range
+ timer = firstTimerInRange(clock, tickFrom, tickTo);
+ // eslint-disable-next-line no-unmodified-loop-condition
+ while (timer && tickFrom <= tickTo) {
+ if (clock.timers[timer.id]) {
+ tickFrom = timer.callAt;
+ clock.now = timer.callAt;
+ oldNow = clock.now;
+ try {
+ runJobs(clock);
+ callTimer(clock, timer);
+ } catch (e) {
+ firstException = firstException || e;
+ }
+
+ if (isAsync) {
+ // finish up after native setImmediate callback to allow
+ // all native es6 promises to process their callbacks after
+ // each timer fires.
+ originalSetTimeout(nextPromiseTick);
+ return;
+ }
+
+ compensationCheck();
+ }
+
+ postTimerCall();
+ }
+
+ // perform process.nextTick()s again
+ oldNow = clock.now;
+ runJobs(clock);
+ if (oldNow !== clock.now) {
+ // compensate for any setSystemTime() call during process.nextTick() callback
+ tickFrom += clock.now - oldNow;
+ tickTo += clock.now - oldNow;
+ }
+ clock.duringTick = false;
+
+ // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo]
+ timer = firstTimerInRange(clock, tickFrom, tickTo);
+ if (timer) {
+ try {
+ clock.tick(tickTo - clock.now); // do it all again - for the remainder of the requested range
+ } catch (e) {
+ firstException = firstException || e;
+ }
+ } else {
+ // no timers remaining in the requested range: move the clock all the way to the end
+ clock.now = tickTo;
+
+ // update nanos
+ nanos = nanosTotal;
+ }
+ if (firstException) {
+ throw firstException;
+ }
+
+ if (isAsync) {
+ resolve(clock.now);
+ } else {
+ return clock.now;
+ }
+ }
+
+ nextPromiseTick =
+ isAsync &&
+ function () {
+ try {
+ compensationCheck();
+ postTimerCall();
+ doTickInner();
+ } catch (e) {
+ reject(e);
+ }
+ };
+
+ compensationCheck = function () {
+ // compensate for any setSystemTime() call during timer callback
+ if (oldNow !== clock.now) {
+ tickFrom += clock.now - oldNow;
+ tickTo += clock.now - oldNow;
+ previous += clock.now - oldNow;
+ }
+ };
+
+ postTimerCall = function () {
+ timer = firstTimerInRange(clock, previous, tickTo);
+ previous = tickFrom;
+ };
+
+ return doTickInner();
+ }
+
+ /**
+ * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
+ * @returns {number} will return the new `now` value
+ */
+ clock.tick = function tick(tickValue) {
+ return doTick(tickValue, false);
+ };
+
+ if (typeof _global.Promise !== "undefined") {
+ /**
+ * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
+ * @returns {Promise}
+ */
+ clock.tickAsync = function tickAsync(tickValue) {
+ return new _global.Promise(function (resolve, reject) {
+ originalSetTimeout(function () {
+ try {
+ doTick(tickValue, true, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+ };
+ }
+
+ clock.next = function next() {
+ runJobs(clock);
+ const timer = firstTimer(clock);
+ if (!timer) {
+ return clock.now;
+ }
+
+ clock.duringTick = true;
+ try {
+ clock.now = timer.callAt;
+ callTimer(clock, timer);
+ runJobs(clock);
+ return clock.now;
+ } finally {
+ clock.duringTick = false;
+ }
+ };
+
+ if (typeof _global.Promise !== "undefined") {
+ clock.nextAsync = function nextAsync() {
+ return new _global.Promise(function (resolve, reject) {
+ originalSetTimeout(function () {
+ try {
+ const timer = firstTimer(clock);
+ if (!timer) {
+ resolve(clock.now);
+ return;
+ }
+
+ let err;
+ clock.duringTick = true;
+ clock.now = timer.callAt;
+ try {
+ callTimer(clock, timer);
+ } catch (e) {
+ err = e;
+ }
+ clock.duringTick = false;
+
+ originalSetTimeout(function () {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(clock.now);
+ }
+ });
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+ };
+ }
+
+ clock.runAll = function runAll() {
+ let numTimers, i;
+ runJobs(clock);
+ for (i = 0; i < clock.loopLimit; i++) {
+ if (!clock.timers) {
+ resetIsNearInfiniteLimit();
+ return clock.now;
+ }
+
+ numTimers = Object.keys(clock.timers).length;
+ if (numTimers === 0) {
+ resetIsNearInfiniteLimit();
+ return clock.now;
+ }
+
+ clock.next();
+ checkIsNearInfiniteLimit(clock, i);
+ }
+
+ const excessJob = firstTimer(clock);
+ throw getInfiniteLoopError(clock, excessJob);
+ };
+
+ clock.runToFrame = function runToFrame() {
+ return clock.tick(getTimeToNextFrame());
+ };
+
+ if (typeof _global.Promise !== "undefined") {
+ clock.runAllAsync = function runAllAsync() {
+ return new _global.Promise(function (resolve, reject) {
+ let i = 0;
+ /**
+ *
+ */
+ function doRun() {
+ originalSetTimeout(function () {
+ try {
+ let numTimers;
+ if (i < clock.loopLimit) {
+ if (!clock.timers) {
+ resetIsNearInfiniteLimit();
+ resolve(clock.now);
+ return;
+ }
+
+ numTimers = Object.keys(clock.timers)
+ .length;
+ if (numTimers === 0) {
+ resetIsNearInfiniteLimit();
+ resolve(clock.now);
+ return;
+ }
+
+ clock.next();
+
+ i++;
+
+ doRun();
+ checkIsNearInfiniteLimit(clock, i);
+ return;
+ }
+
+ const excessJob = firstTimer(clock);
+ reject(getInfiniteLoopError(clock, excessJob));
+ } catch (e) {
+ reject(e);
+ }
+ });
+ }
+ doRun();
+ });
+ };
+ }
+
+ clock.runToLast = function runToLast() {
+ const timer = lastTimer(clock);
+ if (!timer) {
+ runJobs(clock);
+ return clock.now;
+ }
+
+ return clock.tick(timer.callAt - clock.now);
+ };
+
+ if (typeof _global.Promise !== "undefined") {
+ clock.runToLastAsync = function runToLastAsync() {
+ return new _global.Promise(function (resolve, reject) {
+ originalSetTimeout(function () {
+ try {
+ const timer = lastTimer(clock);
+ if (!timer) {
+ resolve(clock.now);
+ }
+
+ resolve(clock.tickAsync(timer.callAt));
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+ };
+ }
+
+ clock.reset = function reset() {
+ nanos = 0;
+ clock.timers = {};
+ clock.jobs = [];
+ clock.now = start;
+ };
+
+ clock.setSystemTime = function setSystemTime(systemTime) {
+ // determine time difference
+ const newNow = getEpoch(systemTime);
+ const difference = newNow - clock.now;
+ let id, timer;
+
+ adjustedSystemTime[0] = adjustedSystemTime[0] + difference;
+ adjustedSystemTime[1] = adjustedSystemTime[1] + nanos;
+ // update 'system clock'
+ clock.now = newNow;
+ nanos = 0;
+
+ // update timers and intervals to keep them stable
+ for (id in clock.timers) {
+ if (clock.timers.hasOwnProperty(id)) {
+ timer = clock.timers[id];
+ timer.createdAt += difference;
+ timer.callAt += difference;
+ }
+ }
+ };
+
+ if (performancePresent) {
+ clock.performance = Object.create(null);
+
+ if (hasPerformancePrototype) {
+ const proto = _global.Performance.prototype;
+
+ Object.getOwnPropertyNames(proto).forEach(function (name) {
+ if (name.indexOf("getEntries") === 0) {
+ // match expected return type for getEntries functions
+ clock.performance[name] = NOOP_ARRAY;
+ } else {
+ clock.performance[name] = NOOP;
+ }
+ });
+ }
+
+ clock.performance.now = function FakeTimersNow() {
+ const hrt = hrtime();
+ const millis = hrt[0] * 1000 + hrt[1] / 1e6;
+ return millis;
+ };
+ }
+
+ if (hrtimePresent) {
+ clock.hrtime = hrtime;
+ }
+
+ return clock;
+ }
+
+ /* eslint-disable complexity */
+
+ /**
+ * @param {Config=} [config] Optional config
+ * @returns {Clock}
+ */
+ function install(config) {
+ if (
+ arguments.length > 1 ||
+ config instanceof Date ||
+ Array.isArray(config) ||
+ typeof config === "number"
+ ) {
+ throw new TypeError(
+ `FakeTimers.install called with ${String(
+ config
+ )} install requires an object parameter`
+ );
+ }
+
+ // eslint-disable-next-line no-param-reassign
+ config = typeof config !== "undefined" ? config : {};
+ config.shouldAdvanceTime = config.shouldAdvanceTime || false;
+ config.advanceTimeDelta = config.advanceTimeDelta || 20;
+ config.shouldClearNativeTimers =
+ config.shouldClearNativeTimers || false;
+
+ if (config.target) {
+ throw new TypeError(
+ "config.target is no longer supported. Use `withGlobal(target)` instead."
+ );
+ }
+
+ let i, l;
+ const clock = createClock(config.now, config.loopLimit);
+ clock.shouldClearNativeTimers = config.shouldClearNativeTimers;
+
+ clock.uninstall = function () {
+ return uninstall(clock, config);
+ };
+
+ clock.methods = config.toFake || [];
+
+ if (clock.methods.length === 0) {
+ // do not fake nextTick by default - GitHub#126
+ clock.methods = Object.keys(timers).filter(function (key) {
+ return key !== "nextTick" && key !== "queueMicrotask";
+ });
+ }
+
+ if (config.shouldAdvanceTime === true) {
+ const intervalTick = doIntervalTick.bind(
+ null,
+ clock,
+ config.advanceTimeDelta
+ );
+ const intervalId = _global.setInterval(
+ intervalTick,
+ config.advanceTimeDelta
+ );
+ clock.attachedInterval = intervalId;
+ }
+
+ for (i = 0, l = clock.methods.length; i < l; i++) {
+ const nameOfMethodToReplace = clock.methods[i];
+ if (nameOfMethodToReplace === "hrtime") {
+ if (
+ _global.process &&
+ typeof _global.process.hrtime === "function"
+ ) {
+ hijackMethod(_global.process, nameOfMethodToReplace, clock);
+ }
+ } else if (nameOfMethodToReplace === "nextTick") {
+ if (
+ _global.process &&
+ typeof _global.process.nextTick === "function"
+ ) {
+ hijackMethod(_global.process, nameOfMethodToReplace, clock);
+ }
+ } else {
+ hijackMethod(_global, nameOfMethodToReplace, clock);
+ }
+ }
+
+ return clock;
+ }
+
+ /* eslint-enable complexity */
+
+ return {
+ timers: timers,
+ createClock: createClock,
+ install: install,
+ withGlobal: withGlobal,
+ };
+}
+
+/**
+ * @typedef {object} FakeTimers
+ * @property {Timers} timers
+ * @property {createClock} createClock
+ * @property {Function} install
+ * @property {withGlobal} withGlobal
+ */
+
+/* eslint-enable complexity */
+
+/** @type {FakeTimers} */
+const defaultImplementation = withGlobal(globalObject);
+
+exports.timers = defaultImplementation.timers;
+exports.createClock = defaultImplementation.createClock;
+exports.install = defaultImplementation.install;
+exports.withGlobal = withGlobal;