fake-timers-src.js 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728
  1. "use strict";
  2. const globalObject = require("@sinonjs/commons").global;
  3. /**
  4. * @typedef {object} IdleDeadline
  5. * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
  6. * @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
  7. */
  8. /**
  9. * Queues a function to be called during a browser's idle periods
  10. *
  11. * @callback RequestIdleCallback
  12. * @param {function(IdleDeadline)} callback
  13. * @param {{timeout: number}} options - an options object
  14. * @returns {number} the id
  15. */
  16. /**
  17. * @callback NextTick
  18. * @param {VoidVarArgsFunc} callback - the callback to run
  19. * @param {...*} arguments - optional arguments to call the callback with
  20. * @returns {void}
  21. */
  22. /**
  23. * @callback SetImmediate
  24. * @param {VoidVarArgsFunc} callback - the callback to run
  25. * @param {...*} arguments - optional arguments to call the callback with
  26. * @returns {NodeImmediate}
  27. */
  28. /**
  29. * @callback VoidVarArgsFunc
  30. * @param {...*} callback - the callback to run
  31. * @returns {void}
  32. */
  33. /**
  34. * @typedef RequestAnimationFrame
  35. * @property {function(number):void} requestAnimationFrame
  36. * @returns {number} - the id
  37. */
  38. /**
  39. * @typedef Performance
  40. * @property {function(): number} now
  41. */
  42. /* eslint-disable jsdoc/require-property-description */
  43. /**
  44. * @typedef {object} Clock
  45. * @property {number} now - the current time
  46. * @property {Date} Date - the Date constructor
  47. * @property {number} loopLimit - the maximum number of timers before assuming an infinite loop
  48. * @property {RequestIdleCallback} requestIdleCallback
  49. * @property {function(number):void} cancelIdleCallback
  50. * @property {setTimeout} setTimeout
  51. * @property {clearTimeout} clearTimeout
  52. * @property {NextTick} nextTick
  53. * @property {queueMicrotask} queueMicrotask
  54. * @property {setInterval} setInterval
  55. * @property {clearInterval} clearInterval
  56. * @property {SetImmediate} setImmediate
  57. * @property {function(NodeImmediate):void} clearImmediate
  58. * @property {function():number} countTimers
  59. * @property {RequestAnimationFrame} requestAnimationFrame
  60. * @property {function(number):void} cancelAnimationFrame
  61. * @property {function():void} runMicrotasks
  62. * @property {function(string | number): number} tick
  63. * @property {function(string | number): Promise<number>} tickAsync
  64. * @property {function(): number} next
  65. * @property {function(): Promise<number>} nextAsync
  66. * @property {function(): number} runAll
  67. * @property {function(): number} runToFrame
  68. * @property {function(): Promise<number>} runAllAsync
  69. * @property {function(): number} runToLast
  70. * @property {function(): Promise<number>} runToLastAsync
  71. * @property {function(): void} reset
  72. * @property {function(number | Date): void} setSystemTime
  73. * @property {Performance} performance
  74. * @property {function(number[]): number[]} hrtime - process.hrtime (legacy)
  75. * @property {function(): void} uninstall Uninstall the clock.
  76. * @property {Function[]} methods - the methods that are faked
  77. * @property {boolean} [shouldClearNativeTimers] inherited from config
  78. */
  79. /* eslint-enable jsdoc/require-property-description */
  80. /**
  81. * Configuration object for the `install` method.
  82. *
  83. * @typedef {object} Config
  84. * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
  85. * @property {string[]} [toFake] names of the methods that should be faked.
  86. * @property {number} [loopLimit] the maximum number of timers that will be run when calling runAll()
  87. * @property {boolean} [shouldAdvanceTime] tells FakeTimers to increment mocked time automatically (default false)
  88. * @property {number} [advanceTimeDelta] increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
  89. * @property {boolean} [shouldClearNativeTimers] forwards clear timer calls to native functions if they are not fakes (default: false)
  90. */
  91. /* eslint-disable jsdoc/require-property-description */
  92. /**
  93. * The internal structure to describe a scheduled fake timer
  94. *
  95. * @typedef {object} Timer
  96. * @property {Function} func
  97. * @property {*[]} args
  98. * @property {number} delay
  99. * @property {number} callAt
  100. * @property {number} createdAt
  101. * @property {boolean} immediate
  102. * @property {number} id
  103. * @property {Error} [error]
  104. */
  105. /**
  106. * A Node timer
  107. *
  108. * @typedef {object} NodeImmediate
  109. * @property {function(): boolean} hasRef
  110. * @property {function(): NodeImmediate} ref
  111. * @property {function(): NodeImmediate} unref
  112. */
  113. /* eslint-enable jsdoc/require-property-description */
  114. /* eslint-disable complexity */
  115. /**
  116. * Mocks available features in the specified global namespace.
  117. *
  118. * @param {*} _global Namespace to mock (e.g. `window`)
  119. * @returns {FakeTimers}
  120. */
  121. function withGlobal(_global) {
  122. const userAgent = _global.navigator && _global.navigator.userAgent;
  123. const isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
  124. const maxTimeout = Math.pow(2, 31) - 1; //see https://heycam.github.io/webidl/#abstract-opdef-converttoint
  125. const idCounterStart = 1e12; // arbitrarily large number to avoid collisions with native timer IDs
  126. const NOOP = function () {
  127. return undefined;
  128. };
  129. const NOOP_ARRAY = function () {
  130. return [];
  131. };
  132. const timeoutResult = _global.setTimeout(NOOP, 0);
  133. const addTimerReturnsObject = typeof timeoutResult === "object";
  134. const hrtimePresent =
  135. _global.process && typeof _global.process.hrtime === "function";
  136. const hrtimeBigintPresent =
  137. hrtimePresent && typeof _global.process.hrtime.bigint === "function";
  138. const nextTickPresent =
  139. _global.process && typeof _global.process.nextTick === "function";
  140. const utilPromisify = _global.process && require("util").promisify;
  141. const performancePresent =
  142. _global.performance && typeof _global.performance.now === "function";
  143. const hasPerformancePrototype =
  144. _global.Performance &&
  145. (typeof _global.Performance).match(/^(function|object)$/);
  146. const queueMicrotaskPresent = _global.hasOwnProperty("queueMicrotask");
  147. const requestAnimationFramePresent =
  148. _global.requestAnimationFrame &&
  149. typeof _global.requestAnimationFrame === "function";
  150. const cancelAnimationFramePresent =
  151. _global.cancelAnimationFrame &&
  152. typeof _global.cancelAnimationFrame === "function";
  153. const requestIdleCallbackPresent =
  154. _global.requestIdleCallback &&
  155. typeof _global.requestIdleCallback === "function";
  156. const cancelIdleCallbackPresent =
  157. _global.cancelIdleCallback &&
  158. typeof _global.cancelIdleCallback === "function";
  159. const setImmediatePresent =
  160. _global.setImmediate && typeof _global.setImmediate === "function";
  161. // Make properties writable in IE, as per
  162. // https://www.adequatelygood.com/Replacing-setTimeout-Globally.html
  163. /* eslint-disable no-self-assign */
  164. if (isRunningInIE) {
  165. _global.setTimeout = _global.setTimeout;
  166. _global.clearTimeout = _global.clearTimeout;
  167. _global.setInterval = _global.setInterval;
  168. _global.clearInterval = _global.clearInterval;
  169. _global.Date = _global.Date;
  170. }
  171. // setImmediate is not a standard function
  172. // avoid adding the prop to the window object if not present
  173. if (setImmediatePresent) {
  174. _global.setImmediate = _global.setImmediate;
  175. _global.clearImmediate = _global.clearImmediate;
  176. }
  177. /* eslint-enable no-self-assign */
  178. _global.clearTimeout(timeoutResult);
  179. const NativeDate = _global.Date;
  180. let uniqueTimerId = idCounterStart;
  181. /**
  182. * @param {number} num
  183. * @returns {boolean}
  184. */
  185. function isNumberFinite(num) {
  186. if (Number.isFinite) {
  187. return Number.isFinite(num);
  188. }
  189. return isFinite(num);
  190. }
  191. let isNearInfiniteLimit = false;
  192. /**
  193. * @param {Clock} clock
  194. * @param {number} i
  195. */
  196. function checkIsNearInfiniteLimit(clock, i) {
  197. if (clock.loopLimit && i === clock.loopLimit - 1) {
  198. isNearInfiniteLimit = true;
  199. }
  200. }
  201. /**
  202. *
  203. */
  204. function resetIsNearInfiniteLimit() {
  205. isNearInfiniteLimit = false;
  206. }
  207. /**
  208. * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
  209. * number of milliseconds. This is used to support human-readable strings passed
  210. * to clock.tick()
  211. *
  212. * @param {string} str
  213. * @returns {number}
  214. */
  215. function parseTime(str) {
  216. if (!str) {
  217. return 0;
  218. }
  219. const strings = str.split(":");
  220. const l = strings.length;
  221. let i = l;
  222. let ms = 0;
  223. let parsed;
  224. if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
  225. throw new Error(
  226. "tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits"
  227. );
  228. }
  229. while (i--) {
  230. parsed = parseInt(strings[i], 10);
  231. if (parsed >= 60) {
  232. throw new Error(`Invalid time ${str}`);
  233. }
  234. ms += parsed * Math.pow(60, l - i - 1);
  235. }
  236. return ms * 1000;
  237. }
  238. /**
  239. * Get the decimal part of the millisecond value as nanoseconds
  240. *
  241. * @param {number} msFloat the number of milliseconds
  242. * @returns {number} an integer number of nanoseconds in the range [0,1e6)
  243. *
  244. * Example: nanoRemainer(123.456789) -> 456789
  245. */
  246. function nanoRemainder(msFloat) {
  247. const modulo = 1e6;
  248. const remainder = (msFloat * 1e6) % modulo;
  249. const positiveRemainder =
  250. remainder < 0 ? remainder + modulo : remainder;
  251. return Math.floor(positiveRemainder);
  252. }
  253. /**
  254. * Used to grok the `now` parameter to createClock.
  255. *
  256. * @param {Date|number} epoch the system time
  257. * @returns {number}
  258. */
  259. function getEpoch(epoch) {
  260. if (!epoch) {
  261. return 0;
  262. }
  263. if (typeof epoch.getTime === "function") {
  264. return epoch.getTime();
  265. }
  266. if (typeof epoch === "number") {
  267. return epoch;
  268. }
  269. throw new TypeError("now should be milliseconds since UNIX epoch");
  270. }
  271. /**
  272. * @param {number} from
  273. * @param {number} to
  274. * @param {Timer} timer
  275. * @returns {boolean}
  276. */
  277. function inRange(from, to, timer) {
  278. return timer && timer.callAt >= from && timer.callAt <= to;
  279. }
  280. /**
  281. * @param {Clock} clock
  282. * @param {Timer} job
  283. */
  284. function getInfiniteLoopError(clock, job) {
  285. const infiniteLoopError = new Error(
  286. `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!`
  287. );
  288. if (!job.error) {
  289. return infiniteLoopError;
  290. }
  291. // pattern never matched in Node
  292. const computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/;
  293. let clockMethodPattern = new RegExp(
  294. String(Object.keys(clock).join("|"))
  295. );
  296. if (addTimerReturnsObject) {
  297. // node.js environment
  298. clockMethodPattern = new RegExp(
  299. `\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+`
  300. );
  301. }
  302. let matchedLineIndex = -1;
  303. job.error.stack.split("\n").some(function (line, i) {
  304. // If we've matched a computed target line (e.g. setTimeout) then we
  305. // don't need to look any further. Return true to stop iterating.
  306. const matchedComputedTarget = line.match(computedTargetPattern);
  307. /* istanbul ignore if */
  308. if (matchedComputedTarget) {
  309. matchedLineIndex = i;
  310. return true;
  311. }
  312. // If we've matched a clock method line, then there may still be
  313. // others further down the trace. Return false to keep iterating.
  314. const matchedClockMethod = line.match(clockMethodPattern);
  315. if (matchedClockMethod) {
  316. matchedLineIndex = i;
  317. return false;
  318. }
  319. // If we haven't matched anything on this line, but we matched
  320. // previously and set the matched line index, then we can stop.
  321. // If we haven't matched previously, then we should keep iterating.
  322. return matchedLineIndex >= 0;
  323. });
  324. const stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${
  325. job.func.name || "anonymous"
  326. }\n${job.error.stack
  327. .split("\n")
  328. .slice(matchedLineIndex + 1)
  329. .join("\n")}`;
  330. try {
  331. Object.defineProperty(infiniteLoopError, "stack", {
  332. value: stack,
  333. });
  334. } catch (e) {
  335. // noop
  336. }
  337. return infiniteLoopError;
  338. }
  339. /**
  340. * @param {Date} target
  341. * @param {Date} source
  342. * @returns {Date} the target after modifications
  343. */
  344. function mirrorDateProperties(target, source) {
  345. let prop;
  346. for (prop in source) {
  347. if (source.hasOwnProperty(prop)) {
  348. target[prop] = source[prop];
  349. }
  350. }
  351. // set special now implementation
  352. if (source.now) {
  353. target.now = function now() {
  354. return target.clock.now;
  355. };
  356. } else {
  357. delete target.now;
  358. }
  359. // set special toSource implementation
  360. if (source.toSource) {
  361. target.toSource = function toSource() {
  362. return source.toSource();
  363. };
  364. } else {
  365. delete target.toSource;
  366. }
  367. // set special toString implementation
  368. target.toString = function toString() {
  369. return source.toString();
  370. };
  371. target.prototype = source.prototype;
  372. target.parse = source.parse;
  373. target.UTC = source.UTC;
  374. target.prototype.toUTCString = source.prototype.toUTCString;
  375. return target;
  376. }
  377. //eslint-disable-next-line jsdoc/require-jsdoc
  378. function createDate() {
  379. /**
  380. * @param {number} year
  381. * @param {number} month
  382. * @param {number} date
  383. * @param {number} hour
  384. * @param {number} minute
  385. * @param {number} second
  386. * @param {number} ms
  387. *
  388. * @returns {Date}
  389. */
  390. function ClockDate(year, month, date, hour, minute, second, ms) {
  391. // the Date constructor called as a function, ref Ecma-262 Edition 5.1, section 15.9.2.
  392. // This remains so in the 10th edition of 2019 as well.
  393. if (!(this instanceof ClockDate)) {
  394. return new NativeDate(ClockDate.clock.now).toString();
  395. }
  396. // if Date is called as a constructor with 'new' keyword
  397. // Defensive and verbose to avoid potential harm in passing
  398. // explicit undefined when user does not pass argument
  399. switch (arguments.length) {
  400. case 0:
  401. return new NativeDate(ClockDate.clock.now);
  402. case 1:
  403. return new NativeDate(year);
  404. case 2:
  405. return new NativeDate(year, month);
  406. case 3:
  407. return new NativeDate(year, month, date);
  408. case 4:
  409. return new NativeDate(year, month, date, hour);
  410. case 5:
  411. return new NativeDate(year, month, date, hour, minute);
  412. case 6:
  413. return new NativeDate(
  414. year,
  415. month,
  416. date,
  417. hour,
  418. minute,
  419. second
  420. );
  421. default:
  422. return new NativeDate(
  423. year,
  424. month,
  425. date,
  426. hour,
  427. minute,
  428. second,
  429. ms
  430. );
  431. }
  432. }
  433. return mirrorDateProperties(ClockDate, NativeDate);
  434. }
  435. //eslint-disable-next-line jsdoc/require-jsdoc
  436. function enqueueJob(clock, job) {
  437. // enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
  438. if (!clock.jobs) {
  439. clock.jobs = [];
  440. }
  441. clock.jobs.push(job);
  442. }
  443. //eslint-disable-next-line jsdoc/require-jsdoc
  444. function runJobs(clock) {
  445. // runs all microtick-deferred tasks - ecma262/#sec-runjobs
  446. if (!clock.jobs) {
  447. return;
  448. }
  449. for (let i = 0; i < clock.jobs.length; i++) {
  450. const job = clock.jobs[i];
  451. job.func.apply(null, job.args);
  452. checkIsNearInfiniteLimit(clock, i);
  453. if (clock.loopLimit && i > clock.loopLimit) {
  454. throw getInfiniteLoopError(clock, job);
  455. }
  456. }
  457. resetIsNearInfiniteLimit();
  458. clock.jobs = [];
  459. }
  460. /**
  461. * @param {Clock} clock
  462. * @param {Timer} timer
  463. * @returns {number} id of the created timer
  464. */
  465. function addTimer(clock, timer) {
  466. if (timer.func === undefined) {
  467. throw new Error("Callback must be provided to timer calls");
  468. }
  469. if (addTimerReturnsObject) {
  470. // Node.js environment
  471. if (typeof timer.func !== "function") {
  472. throw new TypeError(
  473. `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${
  474. timer.func
  475. } of type ${typeof timer.func}`
  476. );
  477. }
  478. }
  479. if (isNearInfiniteLimit) {
  480. timer.error = new Error();
  481. }
  482. timer.type = timer.immediate ? "Immediate" : "Timeout";
  483. if (timer.hasOwnProperty("delay")) {
  484. if (typeof timer.delay !== "number") {
  485. timer.delay = parseInt(timer.delay, 10);
  486. }
  487. if (!isNumberFinite(timer.delay)) {
  488. timer.delay = 0;
  489. }
  490. timer.delay = timer.delay > maxTimeout ? 1 : timer.delay;
  491. timer.delay = Math.max(0, timer.delay);
  492. }
  493. if (timer.hasOwnProperty("interval")) {
  494. timer.type = "Interval";
  495. timer.interval = timer.interval > maxTimeout ? 1 : timer.interval;
  496. }
  497. if (timer.hasOwnProperty("animation")) {
  498. timer.type = "AnimationFrame";
  499. timer.animation = true;
  500. }
  501. if (timer.hasOwnProperty("idleCallback")) {
  502. timer.type = "IdleCallback";
  503. timer.idleCallback = true;
  504. }
  505. if (!clock.timers) {
  506. clock.timers = {};
  507. }
  508. timer.id = uniqueTimerId++;
  509. timer.createdAt = clock.now;
  510. timer.callAt =
  511. clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
  512. clock.timers[timer.id] = timer;
  513. if (addTimerReturnsObject) {
  514. const res = {
  515. ref: function () {
  516. return res;
  517. },
  518. unref: function () {
  519. return res;
  520. },
  521. refresh: function () {
  522. clearTimeout(timer.id);
  523. const args = [timer.func, timer.delay].concat(timer.args);
  524. return setTimeout.apply(null, args);
  525. },
  526. [Symbol.toPrimitive]: function () {
  527. return timer.id;
  528. },
  529. };
  530. return res;
  531. }
  532. return timer.id;
  533. }
  534. /* eslint consistent-return: "off" */
  535. /**
  536. * Timer comparitor
  537. *
  538. * @param {Timer} a
  539. * @param {Timer} b
  540. * @returns {number}
  541. */
  542. function compareTimers(a, b) {
  543. // Sort first by absolute timing
  544. if (a.callAt < b.callAt) {
  545. return -1;
  546. }
  547. if (a.callAt > b.callAt) {
  548. return 1;
  549. }
  550. // Sort next by immediate, immediate timers take precedence
  551. if (a.immediate && !b.immediate) {
  552. return -1;
  553. }
  554. if (!a.immediate && b.immediate) {
  555. return 1;
  556. }
  557. // Sort next by creation time, earlier-created timers take precedence
  558. if (a.createdAt < b.createdAt) {
  559. return -1;
  560. }
  561. if (a.createdAt > b.createdAt) {
  562. return 1;
  563. }
  564. // Sort next by id, lower-id timers take precedence
  565. if (a.id < b.id) {
  566. return -1;
  567. }
  568. if (a.id > b.id) {
  569. return 1;
  570. }
  571. // As timer ids are unique, no fallback `0` is necessary
  572. }
  573. /**
  574. * @param {Clock} clock
  575. * @param {number} from
  576. * @param {number} to
  577. *
  578. * @returns {Timer}
  579. */
  580. function firstTimerInRange(clock, from, to) {
  581. const timers = clock.timers;
  582. let timer = null;
  583. let id, isInRange;
  584. for (id in timers) {
  585. if (timers.hasOwnProperty(id)) {
  586. isInRange = inRange(from, to, timers[id]);
  587. if (
  588. isInRange &&
  589. (!timer || compareTimers(timer, timers[id]) === 1)
  590. ) {
  591. timer = timers[id];
  592. }
  593. }
  594. }
  595. return timer;
  596. }
  597. /**
  598. * @param {Clock} clock
  599. * @returns {Timer}
  600. */
  601. function firstTimer(clock) {
  602. const timers = clock.timers;
  603. let timer = null;
  604. let id;
  605. for (id in timers) {
  606. if (timers.hasOwnProperty(id)) {
  607. if (!timer || compareTimers(timer, timers[id]) === 1) {
  608. timer = timers[id];
  609. }
  610. }
  611. }
  612. return timer;
  613. }
  614. /**
  615. * @param {Clock} clock
  616. * @returns {Timer}
  617. */
  618. function lastTimer(clock) {
  619. const timers = clock.timers;
  620. let timer = null;
  621. let id;
  622. for (id in timers) {
  623. if (timers.hasOwnProperty(id)) {
  624. if (!timer || compareTimers(timer, timers[id]) === -1) {
  625. timer = timers[id];
  626. }
  627. }
  628. }
  629. return timer;
  630. }
  631. /**
  632. * @param {Clock} clock
  633. * @param {Timer} timer
  634. */
  635. function callTimer(clock, timer) {
  636. if (typeof timer.interval === "number") {
  637. clock.timers[timer.id].callAt += timer.interval;
  638. } else {
  639. delete clock.timers[timer.id];
  640. }
  641. if (typeof timer.func === "function") {
  642. timer.func.apply(null, timer.args);
  643. } else {
  644. /* eslint no-eval: "off" */
  645. const eval2 = eval;
  646. (function () {
  647. eval2(timer.func);
  648. })();
  649. }
  650. }
  651. /**
  652. * Gets clear handler name for a given timer type
  653. * @param {string} ttype
  654. */
  655. function getClearHandler(ttype) {
  656. if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
  657. return `cancel${ttype}`;
  658. }
  659. return `clear${ttype}`;
  660. }
  661. /**
  662. * Gets schedule handler name for a given timer type
  663. * @param {string} ttype
  664. */
  665. function getScheduleHandler(ttype) {
  666. if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
  667. return `request${ttype}`;
  668. }
  669. return `set${ttype}`;
  670. }
  671. /**
  672. * Creates an anonymous function to warn only once
  673. */
  674. function createWarnOnce() {
  675. let calls = 0;
  676. return function (msg) {
  677. // eslint-disable-next-line
  678. !calls++ && console.warn(msg);
  679. };
  680. }
  681. const warnOnce = createWarnOnce();
  682. /**
  683. * @param {Clock} clock
  684. * @param {number} timerId
  685. * @param {string} ttype
  686. */
  687. function clearTimer(clock, timerId, ttype) {
  688. if (!timerId) {
  689. // null appears to be allowed in most browsers, and appears to be
  690. // relied upon by some libraries, like Bootstrap carousel
  691. return;
  692. }
  693. if (!clock.timers) {
  694. clock.timers = {};
  695. }
  696. // in Node, the ID is stored as the primitive value for `Timeout` objects
  697. // for `Immediate` objects, no ID exists, so it gets coerced to NaN
  698. const id = Number(timerId);
  699. if (Number.isNaN(id) || id < idCounterStart) {
  700. const handlerName = getClearHandler(ttype);
  701. if (clock.shouldClearNativeTimers === true) {
  702. const nativeHandler = clock[`_${handlerName}`];
  703. return typeof nativeHandler === "function"
  704. ? nativeHandler(timerId)
  705. : undefined;
  706. }
  707. warnOnce(
  708. `FakeTimers: ${handlerName} was invoked to clear a native timer instead of one created by this library.` +
  709. "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`."
  710. );
  711. }
  712. if (clock.timers.hasOwnProperty(id)) {
  713. // check that the ID matches a timer of the correct type
  714. const timer = clock.timers[id];
  715. if (
  716. timer.type === ttype ||
  717. (timer.type === "Timeout" && ttype === "Interval") ||
  718. (timer.type === "Interval" && ttype === "Timeout")
  719. ) {
  720. delete clock.timers[id];
  721. } else {
  722. const clear = getClearHandler(ttype);
  723. const schedule = getScheduleHandler(timer.type);
  724. throw new Error(
  725. `Cannot clear timer: timer created with ${schedule}() but cleared with ${clear}()`
  726. );
  727. }
  728. }
  729. }
  730. /**
  731. * @param {Clock} clock
  732. * @param {Config} config
  733. * @returns {Timer[]}
  734. */
  735. function uninstall(clock, config) {
  736. let method, i, l;
  737. const installedHrTime = "_hrtime";
  738. const installedNextTick = "_nextTick";
  739. for (i = 0, l = clock.methods.length; i < l; i++) {
  740. method = clock.methods[i];
  741. if (method === "hrtime" && _global.process) {
  742. _global.process.hrtime = clock[installedHrTime];
  743. } else if (method === "nextTick" && _global.process) {
  744. _global.process.nextTick = clock[installedNextTick];
  745. } else if (method === "performance") {
  746. const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
  747. clock,
  748. `_${method}`
  749. );
  750. if (
  751. originalPerfDescriptor &&
  752. originalPerfDescriptor.get &&
  753. !originalPerfDescriptor.set
  754. ) {
  755. Object.defineProperty(
  756. _global,
  757. method,
  758. originalPerfDescriptor
  759. );
  760. } else if (originalPerfDescriptor.configurable) {
  761. _global[method] = clock[`_${method}`];
  762. }
  763. } else {
  764. if (_global[method] && _global[method].hadOwnProperty) {
  765. _global[method] = clock[`_${method}`];
  766. } else {
  767. try {
  768. delete _global[method];
  769. } catch (ignore) {
  770. /* eslint no-empty: "off" */
  771. }
  772. }
  773. }
  774. }
  775. if (config.shouldAdvanceTime === true) {
  776. _global.clearInterval(clock.attachedInterval);
  777. }
  778. // Prevent multiple executions which will completely remove these props
  779. clock.methods = [];
  780. // return pending timers, to enable checking what timers remained on uninstall
  781. if (!clock.timers) {
  782. return [];
  783. }
  784. return Object.keys(clock.timers).map(function mapper(key) {
  785. return clock.timers[key];
  786. });
  787. }
  788. /**
  789. * @param {object} target the target containing the method to replace
  790. * @param {string} method the keyname of the method on the target
  791. * @param {Clock} clock
  792. */
  793. function hijackMethod(target, method, clock) {
  794. clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(
  795. target,
  796. method
  797. );
  798. clock[`_${method}`] = target[method];
  799. if (method === "Date") {
  800. const date = mirrorDateProperties(clock[method], target[method]);
  801. target[method] = date;
  802. } else if (method === "performance") {
  803. const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
  804. target,
  805. method
  806. );
  807. // JSDOM has a read only performance field so we have to save/copy it differently
  808. if (
  809. originalPerfDescriptor &&
  810. originalPerfDescriptor.get &&
  811. !originalPerfDescriptor.set
  812. ) {
  813. Object.defineProperty(
  814. clock,
  815. `_${method}`,
  816. originalPerfDescriptor
  817. );
  818. const perfDescriptor = Object.getOwnPropertyDescriptor(
  819. clock,
  820. method
  821. );
  822. Object.defineProperty(target, method, perfDescriptor);
  823. } else {
  824. target[method] = clock[method];
  825. }
  826. } else {
  827. target[method] = function () {
  828. return clock[method].apply(clock, arguments);
  829. };
  830. Object.defineProperties(
  831. target[method],
  832. Object.getOwnPropertyDescriptors(clock[method])
  833. );
  834. }
  835. target[method].clock = clock;
  836. }
  837. /**
  838. * @param {Clock} clock
  839. * @param {number} advanceTimeDelta
  840. */
  841. function doIntervalTick(clock, advanceTimeDelta) {
  842. clock.tick(advanceTimeDelta);
  843. }
  844. /**
  845. * @typedef {object} Timers
  846. * @property {setTimeout} setTimeout
  847. * @property {clearTimeout} clearTimeout
  848. * @property {setInterval} setInterval
  849. * @property {clearInterval} clearInterval
  850. * @property {Date} Date
  851. * @property {SetImmediate=} setImmediate
  852. * @property {function(NodeImmediate): void=} clearImmediate
  853. * @property {function(number[]):number[]=} hrtime
  854. * @property {NextTick=} nextTick
  855. * @property {Performance=} performance
  856. * @property {RequestAnimationFrame=} requestAnimationFrame
  857. * @property {boolean=} queueMicrotask
  858. * @property {function(number): void=} cancelAnimationFrame
  859. * @property {RequestIdleCallback=} requestIdleCallback
  860. * @property {function(number): void=} cancelIdleCallback
  861. */
  862. /** @type {Timers} */
  863. const timers = {
  864. setTimeout: _global.setTimeout,
  865. clearTimeout: _global.clearTimeout,
  866. setInterval: _global.setInterval,
  867. clearInterval: _global.clearInterval,
  868. Date: _global.Date,
  869. };
  870. if (setImmediatePresent) {
  871. timers.setImmediate = _global.setImmediate;
  872. timers.clearImmediate = _global.clearImmediate;
  873. }
  874. if (hrtimePresent) {
  875. timers.hrtime = _global.process.hrtime;
  876. }
  877. if (nextTickPresent) {
  878. timers.nextTick = _global.process.nextTick;
  879. }
  880. if (performancePresent) {
  881. timers.performance = _global.performance;
  882. }
  883. if (requestAnimationFramePresent) {
  884. timers.requestAnimationFrame = _global.requestAnimationFrame;
  885. }
  886. if (queueMicrotaskPresent) {
  887. timers.queueMicrotask = true;
  888. }
  889. if (cancelAnimationFramePresent) {
  890. timers.cancelAnimationFrame = _global.cancelAnimationFrame;
  891. }
  892. if (requestIdleCallbackPresent) {
  893. timers.requestIdleCallback = _global.requestIdleCallback;
  894. }
  895. if (cancelIdleCallbackPresent) {
  896. timers.cancelIdleCallback = _global.cancelIdleCallback;
  897. }
  898. const originalSetTimeout = _global.setImmediate || _global.setTimeout;
  899. /**
  900. * @param {Date|number} [start] the system time - non-integer values are floored
  901. * @param {number} [loopLimit] maximum number of timers that will be run when calling runAll()
  902. * @returns {Clock}
  903. */
  904. function createClock(start, loopLimit) {
  905. // eslint-disable-next-line no-param-reassign
  906. start = Math.floor(getEpoch(start));
  907. // eslint-disable-next-line no-param-reassign
  908. loopLimit = loopLimit || 1000;
  909. let nanos = 0;
  910. const adjustedSystemTime = [0, 0]; // [millis, nanoremainder]
  911. if (NativeDate === undefined) {
  912. throw new Error(
  913. "The global scope doesn't have a `Date` object" +
  914. " (see https://github.com/sinonjs/sinon/issues/1852#issuecomment-419622780)"
  915. );
  916. }
  917. const clock = {
  918. now: start,
  919. Date: createDate(),
  920. loopLimit: loopLimit,
  921. };
  922. clock.Date.clock = clock;
  923. //eslint-disable-next-line jsdoc/require-jsdoc
  924. function getTimeToNextFrame() {
  925. return 16 - ((clock.now - start) % 16);
  926. }
  927. //eslint-disable-next-line jsdoc/require-jsdoc
  928. function hrtime(prev) {
  929. const millisSinceStart = clock.now - adjustedSystemTime[0] - start;
  930. const secsSinceStart = Math.floor(millisSinceStart / 1000);
  931. const remainderInNanos =
  932. (millisSinceStart - secsSinceStart * 1e3) * 1e6 +
  933. nanos -
  934. adjustedSystemTime[1];
  935. if (Array.isArray(prev)) {
  936. if (prev[1] > 1e9) {
  937. throw new TypeError(
  938. "Number of nanoseconds can't exceed a billion"
  939. );
  940. }
  941. const oldSecs = prev[0];
  942. let nanoDiff = remainderInNanos - prev[1];
  943. let secDiff = secsSinceStart - oldSecs;
  944. if (nanoDiff < 0) {
  945. nanoDiff += 1e9;
  946. secDiff -= 1;
  947. }
  948. return [secDiff, nanoDiff];
  949. }
  950. return [secsSinceStart, remainderInNanos];
  951. }
  952. if (hrtimeBigintPresent) {
  953. hrtime.bigint = function () {
  954. const parts = hrtime();
  955. return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]); // eslint-disable-line
  956. };
  957. }
  958. clock.requestIdleCallback = function requestIdleCallback(
  959. func,
  960. timeout
  961. ) {
  962. let timeToNextIdlePeriod = 0;
  963. if (clock.countTimers() > 0) {
  964. timeToNextIdlePeriod = 50; // const for now
  965. }
  966. const result = addTimer(clock, {
  967. func: func,
  968. args: Array.prototype.slice.call(arguments, 2),
  969. delay:
  970. typeof timeout === "undefined"
  971. ? timeToNextIdlePeriod
  972. : Math.min(timeout, timeToNextIdlePeriod),
  973. idleCallback: true,
  974. });
  975. return Number(result);
  976. };
  977. clock.cancelIdleCallback = function cancelIdleCallback(timerId) {
  978. return clearTimer(clock, timerId, "IdleCallback");
  979. };
  980. clock.setTimeout = function setTimeout(func, timeout) {
  981. return addTimer(clock, {
  982. func: func,
  983. args: Array.prototype.slice.call(arguments, 2),
  984. delay: timeout,
  985. });
  986. };
  987. if (typeof _global.Promise !== "undefined" && utilPromisify) {
  988. clock.setTimeout[
  989. utilPromisify.custom
  990. ] = function promisifiedSetTimeout(timeout, arg) {
  991. return new _global.Promise(function setTimeoutExecutor(
  992. resolve
  993. ) {
  994. addTimer(clock, {
  995. func: resolve,
  996. args: [arg],
  997. delay: timeout,
  998. });
  999. });
  1000. };
  1001. }
  1002. clock.clearTimeout = function clearTimeout(timerId) {
  1003. return clearTimer(clock, timerId, "Timeout");
  1004. };
  1005. clock.nextTick = function nextTick(func) {
  1006. return enqueueJob(clock, {
  1007. func: func,
  1008. args: Array.prototype.slice.call(arguments, 1),
  1009. error: isNearInfiniteLimit ? new Error() : null,
  1010. });
  1011. };
  1012. clock.queueMicrotask = function queueMicrotask(func) {
  1013. return clock.nextTick(func); // explicitly drop additional arguments
  1014. };
  1015. clock.setInterval = function setInterval(func, timeout) {
  1016. // eslint-disable-next-line no-param-reassign
  1017. timeout = parseInt(timeout, 10);
  1018. return addTimer(clock, {
  1019. func: func,
  1020. args: Array.prototype.slice.call(arguments, 2),
  1021. delay: timeout,
  1022. interval: timeout,
  1023. });
  1024. };
  1025. clock.clearInterval = function clearInterval(timerId) {
  1026. return clearTimer(clock, timerId, "Interval");
  1027. };
  1028. if (setImmediatePresent) {
  1029. clock.setImmediate = function setImmediate(func) {
  1030. return addTimer(clock, {
  1031. func: func,
  1032. args: Array.prototype.slice.call(arguments, 1),
  1033. immediate: true,
  1034. });
  1035. };
  1036. if (typeof _global.Promise !== "undefined" && utilPromisify) {
  1037. clock.setImmediate[
  1038. utilPromisify.custom
  1039. ] = function promisifiedSetImmediate(arg) {
  1040. return new _global.Promise(function setImmediateExecutor(
  1041. resolve
  1042. ) {
  1043. addTimer(clock, {
  1044. func: resolve,
  1045. args: [arg],
  1046. immediate: true,
  1047. });
  1048. });
  1049. };
  1050. }
  1051. clock.clearImmediate = function clearImmediate(timerId) {
  1052. return clearTimer(clock, timerId, "Immediate");
  1053. };
  1054. }
  1055. clock.countTimers = function countTimers() {
  1056. return (
  1057. Object.keys(clock.timers || {}).length +
  1058. (clock.jobs || []).length
  1059. );
  1060. };
  1061. clock.requestAnimationFrame = function requestAnimationFrame(func) {
  1062. const result = addTimer(clock, {
  1063. func: func,
  1064. delay: getTimeToNextFrame(),
  1065. args: [clock.now + getTimeToNextFrame()],
  1066. animation: true,
  1067. });
  1068. return Number(result);
  1069. };
  1070. clock.cancelAnimationFrame = function cancelAnimationFrame(timerId) {
  1071. return clearTimer(clock, timerId, "AnimationFrame");
  1072. };
  1073. clock.runMicrotasks = function runMicrotasks() {
  1074. runJobs(clock);
  1075. };
  1076. /**
  1077. * @param {number|string} tickValue milliseconds or a string parseable by parseTime
  1078. * @param {boolean} isAsync
  1079. * @param {Function} resolve
  1080. * @param {Function} reject
  1081. * @returns {number|undefined} will return the new `now` value or nothing for async
  1082. */
  1083. function doTick(tickValue, isAsync, resolve, reject) {
  1084. const msFloat =
  1085. typeof tickValue === "number"
  1086. ? tickValue
  1087. : parseTime(tickValue);
  1088. const ms = Math.floor(msFloat);
  1089. const remainder = nanoRemainder(msFloat);
  1090. let nanosTotal = nanos + remainder;
  1091. let tickTo = clock.now + ms;
  1092. if (msFloat < 0) {
  1093. throw new TypeError("Negative ticks are not supported");
  1094. }
  1095. // adjust for positive overflow
  1096. if (nanosTotal >= 1e6) {
  1097. tickTo += 1;
  1098. nanosTotal -= 1e6;
  1099. }
  1100. nanos = nanosTotal;
  1101. let tickFrom = clock.now;
  1102. let previous = clock.now;
  1103. // ESLint fails to detect this correctly
  1104. /* eslint-disable prefer-const */
  1105. let timer,
  1106. firstException,
  1107. oldNow,
  1108. nextPromiseTick,
  1109. compensationCheck,
  1110. postTimerCall;
  1111. /* eslint-enable prefer-const */
  1112. clock.duringTick = true;
  1113. // perform microtasks
  1114. oldNow = clock.now;
  1115. runJobs(clock);
  1116. if (oldNow !== clock.now) {
  1117. // compensate for any setSystemTime() call during microtask callback
  1118. tickFrom += clock.now - oldNow;
  1119. tickTo += clock.now - oldNow;
  1120. }
  1121. //eslint-disable-next-line jsdoc/require-jsdoc
  1122. function doTickInner() {
  1123. // perform each timer in the requested range
  1124. timer = firstTimerInRange(clock, tickFrom, tickTo);
  1125. // eslint-disable-next-line no-unmodified-loop-condition
  1126. while (timer && tickFrom <= tickTo) {
  1127. if (clock.timers[timer.id]) {
  1128. tickFrom = timer.callAt;
  1129. clock.now = timer.callAt;
  1130. oldNow = clock.now;
  1131. try {
  1132. runJobs(clock);
  1133. callTimer(clock, timer);
  1134. } catch (e) {
  1135. firstException = firstException || e;
  1136. }
  1137. if (isAsync) {
  1138. // finish up after native setImmediate callback to allow
  1139. // all native es6 promises to process their callbacks after
  1140. // each timer fires.
  1141. originalSetTimeout(nextPromiseTick);
  1142. return;
  1143. }
  1144. compensationCheck();
  1145. }
  1146. postTimerCall();
  1147. }
  1148. // perform process.nextTick()s again
  1149. oldNow = clock.now;
  1150. runJobs(clock);
  1151. if (oldNow !== clock.now) {
  1152. // compensate for any setSystemTime() call during process.nextTick() callback
  1153. tickFrom += clock.now - oldNow;
  1154. tickTo += clock.now - oldNow;
  1155. }
  1156. clock.duringTick = false;
  1157. // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo]
  1158. timer = firstTimerInRange(clock, tickFrom, tickTo);
  1159. if (timer) {
  1160. try {
  1161. clock.tick(tickTo - clock.now); // do it all again - for the remainder of the requested range
  1162. } catch (e) {
  1163. firstException = firstException || e;
  1164. }
  1165. } else {
  1166. // no timers remaining in the requested range: move the clock all the way to the end
  1167. clock.now = tickTo;
  1168. // update nanos
  1169. nanos = nanosTotal;
  1170. }
  1171. if (firstException) {
  1172. throw firstException;
  1173. }
  1174. if (isAsync) {
  1175. resolve(clock.now);
  1176. } else {
  1177. return clock.now;
  1178. }
  1179. }
  1180. nextPromiseTick =
  1181. isAsync &&
  1182. function () {
  1183. try {
  1184. compensationCheck();
  1185. postTimerCall();
  1186. doTickInner();
  1187. } catch (e) {
  1188. reject(e);
  1189. }
  1190. };
  1191. compensationCheck = function () {
  1192. // compensate for any setSystemTime() call during timer callback
  1193. if (oldNow !== clock.now) {
  1194. tickFrom += clock.now - oldNow;
  1195. tickTo += clock.now - oldNow;
  1196. previous += clock.now - oldNow;
  1197. }
  1198. };
  1199. postTimerCall = function () {
  1200. timer = firstTimerInRange(clock, previous, tickTo);
  1201. previous = tickFrom;
  1202. };
  1203. return doTickInner();
  1204. }
  1205. /**
  1206. * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
  1207. * @returns {number} will return the new `now` value
  1208. */
  1209. clock.tick = function tick(tickValue) {
  1210. return doTick(tickValue, false);
  1211. };
  1212. if (typeof _global.Promise !== "undefined") {
  1213. /**
  1214. * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
  1215. * @returns {Promise}
  1216. */
  1217. clock.tickAsync = function tickAsync(tickValue) {
  1218. return new _global.Promise(function (resolve, reject) {
  1219. originalSetTimeout(function () {
  1220. try {
  1221. doTick(tickValue, true, resolve, reject);
  1222. } catch (e) {
  1223. reject(e);
  1224. }
  1225. });
  1226. });
  1227. };
  1228. }
  1229. clock.next = function next() {
  1230. runJobs(clock);
  1231. const timer = firstTimer(clock);
  1232. if (!timer) {
  1233. return clock.now;
  1234. }
  1235. clock.duringTick = true;
  1236. try {
  1237. clock.now = timer.callAt;
  1238. callTimer(clock, timer);
  1239. runJobs(clock);
  1240. return clock.now;
  1241. } finally {
  1242. clock.duringTick = false;
  1243. }
  1244. };
  1245. if (typeof _global.Promise !== "undefined") {
  1246. clock.nextAsync = function nextAsync() {
  1247. return new _global.Promise(function (resolve, reject) {
  1248. originalSetTimeout(function () {
  1249. try {
  1250. const timer = firstTimer(clock);
  1251. if (!timer) {
  1252. resolve(clock.now);
  1253. return;
  1254. }
  1255. let err;
  1256. clock.duringTick = true;
  1257. clock.now = timer.callAt;
  1258. try {
  1259. callTimer(clock, timer);
  1260. } catch (e) {
  1261. err = e;
  1262. }
  1263. clock.duringTick = false;
  1264. originalSetTimeout(function () {
  1265. if (err) {
  1266. reject(err);
  1267. } else {
  1268. resolve(clock.now);
  1269. }
  1270. });
  1271. } catch (e) {
  1272. reject(e);
  1273. }
  1274. });
  1275. });
  1276. };
  1277. }
  1278. clock.runAll = function runAll() {
  1279. let numTimers, i;
  1280. runJobs(clock);
  1281. for (i = 0; i < clock.loopLimit; i++) {
  1282. if (!clock.timers) {
  1283. resetIsNearInfiniteLimit();
  1284. return clock.now;
  1285. }
  1286. numTimers = Object.keys(clock.timers).length;
  1287. if (numTimers === 0) {
  1288. resetIsNearInfiniteLimit();
  1289. return clock.now;
  1290. }
  1291. clock.next();
  1292. checkIsNearInfiniteLimit(clock, i);
  1293. }
  1294. const excessJob = firstTimer(clock);
  1295. throw getInfiniteLoopError(clock, excessJob);
  1296. };
  1297. clock.runToFrame = function runToFrame() {
  1298. return clock.tick(getTimeToNextFrame());
  1299. };
  1300. if (typeof _global.Promise !== "undefined") {
  1301. clock.runAllAsync = function runAllAsync() {
  1302. return new _global.Promise(function (resolve, reject) {
  1303. let i = 0;
  1304. /**
  1305. *
  1306. */
  1307. function doRun() {
  1308. originalSetTimeout(function () {
  1309. try {
  1310. let numTimers;
  1311. if (i < clock.loopLimit) {
  1312. if (!clock.timers) {
  1313. resetIsNearInfiniteLimit();
  1314. resolve(clock.now);
  1315. return;
  1316. }
  1317. numTimers = Object.keys(clock.timers)
  1318. .length;
  1319. if (numTimers === 0) {
  1320. resetIsNearInfiniteLimit();
  1321. resolve(clock.now);
  1322. return;
  1323. }
  1324. clock.next();
  1325. i++;
  1326. doRun();
  1327. checkIsNearInfiniteLimit(clock, i);
  1328. return;
  1329. }
  1330. const excessJob = firstTimer(clock);
  1331. reject(getInfiniteLoopError(clock, excessJob));
  1332. } catch (e) {
  1333. reject(e);
  1334. }
  1335. });
  1336. }
  1337. doRun();
  1338. });
  1339. };
  1340. }
  1341. clock.runToLast = function runToLast() {
  1342. const timer = lastTimer(clock);
  1343. if (!timer) {
  1344. runJobs(clock);
  1345. return clock.now;
  1346. }
  1347. return clock.tick(timer.callAt - clock.now);
  1348. };
  1349. if (typeof _global.Promise !== "undefined") {
  1350. clock.runToLastAsync = function runToLastAsync() {
  1351. return new _global.Promise(function (resolve, reject) {
  1352. originalSetTimeout(function () {
  1353. try {
  1354. const timer = lastTimer(clock);
  1355. if (!timer) {
  1356. resolve(clock.now);
  1357. }
  1358. resolve(clock.tickAsync(timer.callAt));
  1359. } catch (e) {
  1360. reject(e);
  1361. }
  1362. });
  1363. });
  1364. };
  1365. }
  1366. clock.reset = function reset() {
  1367. nanos = 0;
  1368. clock.timers = {};
  1369. clock.jobs = [];
  1370. clock.now = start;
  1371. };
  1372. clock.setSystemTime = function setSystemTime(systemTime) {
  1373. // determine time difference
  1374. const newNow = getEpoch(systemTime);
  1375. const difference = newNow - clock.now;
  1376. let id, timer;
  1377. adjustedSystemTime[0] = adjustedSystemTime[0] + difference;
  1378. adjustedSystemTime[1] = adjustedSystemTime[1] + nanos;
  1379. // update 'system clock'
  1380. clock.now = newNow;
  1381. nanos = 0;
  1382. // update timers and intervals to keep them stable
  1383. for (id in clock.timers) {
  1384. if (clock.timers.hasOwnProperty(id)) {
  1385. timer = clock.timers[id];
  1386. timer.createdAt += difference;
  1387. timer.callAt += difference;
  1388. }
  1389. }
  1390. };
  1391. if (performancePresent) {
  1392. clock.performance = Object.create(null);
  1393. if (hasPerformancePrototype) {
  1394. const proto = _global.Performance.prototype;
  1395. Object.getOwnPropertyNames(proto).forEach(function (name) {
  1396. if (name.indexOf("getEntries") === 0) {
  1397. // match expected return type for getEntries functions
  1398. clock.performance[name] = NOOP_ARRAY;
  1399. } else {
  1400. clock.performance[name] = NOOP;
  1401. }
  1402. });
  1403. }
  1404. clock.performance.now = function FakeTimersNow() {
  1405. const hrt = hrtime();
  1406. const millis = hrt[0] * 1000 + hrt[1] / 1e6;
  1407. return millis;
  1408. };
  1409. }
  1410. if (hrtimePresent) {
  1411. clock.hrtime = hrtime;
  1412. }
  1413. return clock;
  1414. }
  1415. /* eslint-disable complexity */
  1416. /**
  1417. * @param {Config=} [config] Optional config
  1418. * @returns {Clock}
  1419. */
  1420. function install(config) {
  1421. if (
  1422. arguments.length > 1 ||
  1423. config instanceof Date ||
  1424. Array.isArray(config) ||
  1425. typeof config === "number"
  1426. ) {
  1427. throw new TypeError(
  1428. `FakeTimers.install called with ${String(
  1429. config
  1430. )} install requires an object parameter`
  1431. );
  1432. }
  1433. // eslint-disable-next-line no-param-reassign
  1434. config = typeof config !== "undefined" ? config : {};
  1435. config.shouldAdvanceTime = config.shouldAdvanceTime || false;
  1436. config.advanceTimeDelta = config.advanceTimeDelta || 20;
  1437. config.shouldClearNativeTimers =
  1438. config.shouldClearNativeTimers || false;
  1439. if (config.target) {
  1440. throw new TypeError(
  1441. "config.target is no longer supported. Use `withGlobal(target)` instead."
  1442. );
  1443. }
  1444. let i, l;
  1445. const clock = createClock(config.now, config.loopLimit);
  1446. clock.shouldClearNativeTimers = config.shouldClearNativeTimers;
  1447. clock.uninstall = function () {
  1448. return uninstall(clock, config);
  1449. };
  1450. clock.methods = config.toFake || [];
  1451. if (clock.methods.length === 0) {
  1452. // do not fake nextTick by default - GitHub#126
  1453. clock.methods = Object.keys(timers).filter(function (key) {
  1454. return key !== "nextTick" && key !== "queueMicrotask";
  1455. });
  1456. }
  1457. if (config.shouldAdvanceTime === true) {
  1458. const intervalTick = doIntervalTick.bind(
  1459. null,
  1460. clock,
  1461. config.advanceTimeDelta
  1462. );
  1463. const intervalId = _global.setInterval(
  1464. intervalTick,
  1465. config.advanceTimeDelta
  1466. );
  1467. clock.attachedInterval = intervalId;
  1468. }
  1469. for (i = 0, l = clock.methods.length; i < l; i++) {
  1470. const nameOfMethodToReplace = clock.methods[i];
  1471. if (nameOfMethodToReplace === "hrtime") {
  1472. if (
  1473. _global.process &&
  1474. typeof _global.process.hrtime === "function"
  1475. ) {
  1476. hijackMethod(_global.process, nameOfMethodToReplace, clock);
  1477. }
  1478. } else if (nameOfMethodToReplace === "nextTick") {
  1479. if (
  1480. _global.process &&
  1481. typeof _global.process.nextTick === "function"
  1482. ) {
  1483. hijackMethod(_global.process, nameOfMethodToReplace, clock);
  1484. }
  1485. } else {
  1486. hijackMethod(_global, nameOfMethodToReplace, clock);
  1487. }
  1488. }
  1489. return clock;
  1490. }
  1491. /* eslint-enable complexity */
  1492. return {
  1493. timers: timers,
  1494. createClock: createClock,
  1495. install: install,
  1496. withGlobal: withGlobal,
  1497. };
  1498. }
  1499. /**
  1500. * @typedef {object} FakeTimers
  1501. * @property {Timers} timers
  1502. * @property {createClock} createClock
  1503. * @property {Function} install
  1504. * @property {withGlobal} withGlobal
  1505. */
  1506. /* eslint-enable complexity */
  1507. /** @type {FakeTimers} */
  1508. const defaultImplementation = withGlobal(globalObject);
  1509. exports.timers = defaultImplementation.timers;
  1510. exports.createClock = defaultImplementation.createClock;
  1511. exports.install = defaultImplementation.install;
  1512. exports.withGlobal = withGlobal;