123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728 |
- "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;
|