1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 |
|
8 | function _util() {
|
9 | const data = _interopRequireDefault(require('util'));
|
10 |
|
11 | _util = function () {
|
12 | return data;
|
13 | };
|
14 |
|
15 | return data;
|
16 | }
|
17 |
|
18 | function _jestMessageUtil() {
|
19 | const data = require('jest-message-util');
|
20 |
|
21 | _jestMessageUtil = function () {
|
22 | return data;
|
23 | };
|
24 |
|
25 | return data;
|
26 | }
|
27 |
|
28 | function _jestUtil() {
|
29 | const data = require('jest-util');
|
30 |
|
31 | _jestUtil = function () {
|
32 | return data;
|
33 | };
|
34 |
|
35 | return data;
|
36 | }
|
37 |
|
38 | function _interopRequireDefault(obj) {
|
39 | return obj && obj.__esModule ? obj : {default: obj};
|
40 | }
|
41 |
|
42 | function _defineProperty(obj, key, value) {
|
43 | if (key in obj) {
|
44 | Object.defineProperty(obj, key, {
|
45 | value: value,
|
46 | enumerable: true,
|
47 | configurable: true,
|
48 | writable: true
|
49 | });
|
50 | } else {
|
51 | obj[key] = value;
|
52 | }
|
53 | return obj;
|
54 | }
|
55 |
|
56 | const MS_IN_A_YEAR = 31536000000;
|
57 |
|
58 | class FakeTimers {
|
59 | constructor({global, moduleMocker, timerConfig, config, maxLoops}) {
|
60 | _defineProperty(this, '_cancelledTicks', void 0);
|
61 |
|
62 | _defineProperty(this, '_config', void 0);
|
63 |
|
64 | _defineProperty(this, '_disposed', void 0);
|
65 |
|
66 | _defineProperty(this, '_fakeTimerAPIs', void 0);
|
67 |
|
68 | _defineProperty(this, '_global', void 0);
|
69 |
|
70 | _defineProperty(this, '_immediates', void 0);
|
71 |
|
72 | _defineProperty(this, '_maxLoops', void 0);
|
73 |
|
74 | _defineProperty(this, '_moduleMocker', void 0);
|
75 |
|
76 | _defineProperty(this, '_now', void 0);
|
77 |
|
78 | _defineProperty(this, '_ticks', void 0);
|
79 |
|
80 | _defineProperty(this, '_timerAPIs', void 0);
|
81 |
|
82 | _defineProperty(this, '_timers', void 0);
|
83 |
|
84 | _defineProperty(this, '_uuidCounter', void 0);
|
85 |
|
86 | _defineProperty(this, '_timerConfig', void 0);
|
87 |
|
88 | this._global = global;
|
89 | this._timerConfig = timerConfig;
|
90 | this._config = config;
|
91 | this._maxLoops = maxLoops || 100000;
|
92 | this._uuidCounter = 1;
|
93 | this._moduleMocker = moduleMocker;
|
94 |
|
95 | this._timerAPIs = {
|
96 | cancelAnimationFrame: global.cancelAnimationFrame,
|
97 | clearImmediate: global.clearImmediate,
|
98 | clearInterval: global.clearInterval,
|
99 | clearTimeout: global.clearTimeout,
|
100 | nextTick: global.process && global.process.nextTick,
|
101 | requestAnimationFrame: global.requestAnimationFrame,
|
102 | setImmediate: global.setImmediate,
|
103 | setInterval: global.setInterval,
|
104 | setTimeout: global.setTimeout
|
105 | };
|
106 | this.reset();
|
107 | }
|
108 |
|
109 | clearAllTimers() {
|
110 | this._immediates = [];
|
111 |
|
112 | this._timers.clear();
|
113 | }
|
114 |
|
115 | dispose() {
|
116 | this._disposed = true;
|
117 | this.clearAllTimers();
|
118 | }
|
119 |
|
120 | reset() {
|
121 | this._cancelledTicks = {};
|
122 | this._now = 0;
|
123 | this._ticks = [];
|
124 | this._immediates = [];
|
125 | this._timers = new Map();
|
126 | }
|
127 |
|
128 | runAllTicks() {
|
129 | this._checkFakeTimers();
|
130 |
|
131 |
|
132 | let i;
|
133 |
|
134 | for (i = 0; i < this._maxLoops; i++) {
|
135 | const tick = this._ticks.shift();
|
136 |
|
137 | if (tick === undefined) {
|
138 | break;
|
139 | }
|
140 |
|
141 | if (!this._cancelledTicks.hasOwnProperty(tick.uuid)) {
|
142 |
|
143 | this._cancelledTicks[tick.uuid] = true;
|
144 | tick.callback();
|
145 | }
|
146 | }
|
147 |
|
148 | if (i === this._maxLoops) {
|
149 | throw new Error(
|
150 | 'Ran ' +
|
151 | this._maxLoops +
|
152 | ' ticks, and there are still more! ' +
|
153 | "Assuming we've hit an infinite recursion and bailing out..."
|
154 | );
|
155 | }
|
156 | }
|
157 |
|
158 | runAllImmediates() {
|
159 | this._checkFakeTimers();
|
160 |
|
161 | let i;
|
162 |
|
163 | for (i = 0; i < this._maxLoops; i++) {
|
164 | const immediate = this._immediates.shift();
|
165 |
|
166 | if (immediate === undefined) {
|
167 | break;
|
168 | }
|
169 |
|
170 | this._runImmediate(immediate);
|
171 | }
|
172 |
|
173 | if (i === this._maxLoops) {
|
174 | throw new Error(
|
175 | 'Ran ' +
|
176 | this._maxLoops +
|
177 | ' immediates, and there are still more! Assuming ' +
|
178 | "we've hit an infinite recursion and bailing out..."
|
179 | );
|
180 | }
|
181 | }
|
182 |
|
183 | _runImmediate(immediate) {
|
184 | try {
|
185 | immediate.callback();
|
186 | } finally {
|
187 | this._fakeClearImmediate(immediate.uuid);
|
188 | }
|
189 | }
|
190 |
|
191 | runAllTimers() {
|
192 | this._checkFakeTimers();
|
193 |
|
194 | this.runAllTicks();
|
195 | this.runAllImmediates();
|
196 |
|
197 |
|
198 | let i;
|
199 |
|
200 | for (i = 0; i < this._maxLoops; i++) {
|
201 | const nextTimerHandle = this._getNextTimerHandle();
|
202 |
|
203 | if (nextTimerHandle === null) {
|
204 | break;
|
205 | }
|
206 |
|
207 | this._runTimerHandle(nextTimerHandle);
|
208 |
|
209 |
|
210 |
|
211 | if (this._immediates.length) {
|
212 | this.runAllImmediates();
|
213 | }
|
214 |
|
215 | if (this._ticks.length) {
|
216 | this.runAllTicks();
|
217 | }
|
218 | }
|
219 |
|
220 | if (i === this._maxLoops) {
|
221 | throw new Error(
|
222 | 'Ran ' +
|
223 | this._maxLoops +
|
224 | ' timers, and there are still more! ' +
|
225 | "Assuming we've hit an infinite recursion and bailing out..."
|
226 | );
|
227 | }
|
228 | }
|
229 |
|
230 | runOnlyPendingTimers() {
|
231 |
|
232 |
|
233 |
|
234 | const timerEntries = Array.from(this._timers.entries());
|
235 |
|
236 | this._checkFakeTimers();
|
237 |
|
238 | this._immediates.forEach(this._runImmediate, this);
|
239 |
|
240 | timerEntries
|
241 | .sort(([, left], [, right]) => left.expiry - right.expiry)
|
242 | .forEach(([timerHandle]) => this._runTimerHandle(timerHandle));
|
243 | }
|
244 |
|
245 | advanceTimersToNextTimer(steps = 1) {
|
246 | if (steps < 1) {
|
247 | return;
|
248 | }
|
249 |
|
250 | const nextExpiry = Array.from(this._timers.values()).reduce(
|
251 | (minExpiry, timer) => {
|
252 | if (minExpiry === null || timer.expiry < minExpiry) return timer.expiry;
|
253 | return minExpiry;
|
254 | },
|
255 | null
|
256 | );
|
257 |
|
258 | if (nextExpiry !== null) {
|
259 | this.advanceTimersByTime(nextExpiry - this._now);
|
260 | this.advanceTimersToNextTimer(steps - 1);
|
261 | }
|
262 | }
|
263 |
|
264 | advanceTimersByTime(msToRun) {
|
265 | this._checkFakeTimers();
|
266 |
|
267 |
|
268 | let i;
|
269 |
|
270 | for (i = 0; i < this._maxLoops; i++) {
|
271 | const timerHandle = this._getNextTimerHandle();
|
272 |
|
273 | if (timerHandle === null) {
|
274 | break;
|
275 | }
|
276 |
|
277 | const timerValue = this._timers.get(timerHandle);
|
278 |
|
279 | if (timerValue === undefined) {
|
280 | break;
|
281 | }
|
282 |
|
283 | const nextTimerExpiry = timerValue.expiry;
|
284 |
|
285 | if (this._now + msToRun < nextTimerExpiry) {
|
286 |
|
287 |
|
288 | this._now += msToRun;
|
289 | break;
|
290 | } else {
|
291 | msToRun -= nextTimerExpiry - this._now;
|
292 | this._now = nextTimerExpiry;
|
293 |
|
294 | this._runTimerHandle(timerHandle);
|
295 | }
|
296 | }
|
297 |
|
298 | if (i === this._maxLoops) {
|
299 | throw new Error(
|
300 | 'Ran ' +
|
301 | this._maxLoops +
|
302 | ' timers, and there are still more! ' +
|
303 | "Assuming we've hit an infinite recursion and bailing out..."
|
304 | );
|
305 | }
|
306 | }
|
307 |
|
308 | runWithRealTimers(cb) {
|
309 | const prevClearImmediate = this._global.clearImmediate;
|
310 | const prevClearInterval = this._global.clearInterval;
|
311 | const prevClearTimeout = this._global.clearTimeout;
|
312 | const prevNextTick = this._global.process.nextTick;
|
313 | const prevSetImmediate = this._global.setImmediate;
|
314 | const prevSetInterval = this._global.setInterval;
|
315 | const prevSetTimeout = this._global.setTimeout;
|
316 | this.useRealTimers();
|
317 | let cbErr = null;
|
318 | let errThrown = false;
|
319 |
|
320 | try {
|
321 | cb();
|
322 | } catch (e) {
|
323 | errThrown = true;
|
324 | cbErr = e;
|
325 | }
|
326 |
|
327 | this._global.clearImmediate = prevClearImmediate;
|
328 | this._global.clearInterval = prevClearInterval;
|
329 | this._global.clearTimeout = prevClearTimeout;
|
330 | this._global.process.nextTick = prevNextTick;
|
331 | this._global.setImmediate = prevSetImmediate;
|
332 | this._global.setInterval = prevSetInterval;
|
333 | this._global.setTimeout = prevSetTimeout;
|
334 |
|
335 | if (errThrown) {
|
336 | throw cbErr;
|
337 | }
|
338 | }
|
339 |
|
340 | useRealTimers() {
|
341 | const global = this._global;
|
342 |
|
343 | if (typeof global.cancelAnimationFrame === 'function') {
|
344 | (0, _jestUtil().setGlobal)(
|
345 | global,
|
346 | 'cancelAnimationFrame',
|
347 | this._timerAPIs.cancelAnimationFrame
|
348 | );
|
349 | }
|
350 |
|
351 | if (typeof global.clearImmediate === 'function') {
|
352 | (0, _jestUtil().setGlobal)(
|
353 | global,
|
354 | 'clearImmediate',
|
355 | this._timerAPIs.clearImmediate
|
356 | );
|
357 | }
|
358 |
|
359 | (0, _jestUtil().setGlobal)(
|
360 | global,
|
361 | 'clearInterval',
|
362 | this._timerAPIs.clearInterval
|
363 | );
|
364 | (0, _jestUtil().setGlobal)(
|
365 | global,
|
366 | 'clearTimeout',
|
367 | this._timerAPIs.clearTimeout
|
368 | );
|
369 |
|
370 | if (typeof global.requestAnimationFrame === 'function') {
|
371 | (0, _jestUtil().setGlobal)(
|
372 | global,
|
373 | 'requestAnimationFrame',
|
374 | this._timerAPIs.requestAnimationFrame
|
375 | );
|
376 | }
|
377 |
|
378 | if (typeof global.setImmediate === 'function') {
|
379 | (0, _jestUtil().setGlobal)(
|
380 | global,
|
381 | 'setImmediate',
|
382 | this._timerAPIs.setImmediate
|
383 | );
|
384 | }
|
385 |
|
386 | (0, _jestUtil().setGlobal)(
|
387 | global,
|
388 | 'setInterval',
|
389 | this._timerAPIs.setInterval
|
390 | );
|
391 | (0, _jestUtil().setGlobal)(
|
392 | global,
|
393 | 'setTimeout',
|
394 | this._timerAPIs.setTimeout
|
395 | );
|
396 | global.process.nextTick = this._timerAPIs.nextTick;
|
397 | }
|
398 |
|
399 | useFakeTimers() {
|
400 | this._createMocks();
|
401 |
|
402 | const global = this._global;
|
403 |
|
404 | if (typeof global.cancelAnimationFrame === 'function') {
|
405 | (0, _jestUtil().setGlobal)(
|
406 | global,
|
407 | 'cancelAnimationFrame',
|
408 | this._fakeTimerAPIs.cancelAnimationFrame
|
409 | );
|
410 | }
|
411 |
|
412 | if (typeof global.clearImmediate === 'function') {
|
413 | (0, _jestUtil().setGlobal)(
|
414 | global,
|
415 | 'clearImmediate',
|
416 | this._fakeTimerAPIs.clearImmediate
|
417 | );
|
418 | }
|
419 |
|
420 | (0, _jestUtil().setGlobal)(
|
421 | global,
|
422 | 'clearInterval',
|
423 | this._fakeTimerAPIs.clearInterval
|
424 | );
|
425 | (0, _jestUtil().setGlobal)(
|
426 | global,
|
427 | 'clearTimeout',
|
428 | this._fakeTimerAPIs.clearTimeout
|
429 | );
|
430 |
|
431 | if (typeof global.requestAnimationFrame === 'function') {
|
432 | (0, _jestUtil().setGlobal)(
|
433 | global,
|
434 | 'requestAnimationFrame',
|
435 | this._fakeTimerAPIs.requestAnimationFrame
|
436 | );
|
437 | }
|
438 |
|
439 | if (typeof global.setImmediate === 'function') {
|
440 | (0, _jestUtil().setGlobal)(
|
441 | global,
|
442 | 'setImmediate',
|
443 | this._fakeTimerAPIs.setImmediate
|
444 | );
|
445 | }
|
446 |
|
447 | (0, _jestUtil().setGlobal)(
|
448 | global,
|
449 | 'setInterval',
|
450 | this._fakeTimerAPIs.setInterval
|
451 | );
|
452 | (0, _jestUtil().setGlobal)(
|
453 | global,
|
454 | 'setTimeout',
|
455 | this._fakeTimerAPIs.setTimeout
|
456 | );
|
457 | global.process.nextTick = this._fakeTimerAPIs.nextTick;
|
458 | }
|
459 |
|
460 | getTimerCount() {
|
461 | this._checkFakeTimers();
|
462 |
|
463 | return this._timers.size + this._immediates.length + this._ticks.length;
|
464 | }
|
465 |
|
466 | _checkFakeTimers() {
|
467 | var _this$_fakeTimerAPIs;
|
468 |
|
469 | if (
|
470 | this._global.setTimeout !==
|
471 | ((_this$_fakeTimerAPIs = this._fakeTimerAPIs) === null ||
|
472 | _this$_fakeTimerAPIs === void 0
|
473 | ? void 0
|
474 | : _this$_fakeTimerAPIs.setTimeout)
|
475 | ) {
|
476 | this._global.console.warn(
|
477 | `A function to advance timers was called but the timers API is not ` +
|
478 | `mocked with fake timers. Call \`jest.useFakeTimers()\` in this ` +
|
479 | `test or enable fake timers globally by setting ` +
|
480 | `\`"timers": "fake"\` in ` +
|
481 | `the configuration file. This warning is likely a result of a ` +
|
482 | `default configuration change in Jest 15.\n\n` +
|
483 | `Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15\n` +
|
484 | `Stack Trace:\n` +
|
485 | (0, _jestMessageUtil().formatStackTrace)(
|
486 | new Error().stack,
|
487 | this._config,
|
488 | {
|
489 | noStackTrace: false
|
490 | }
|
491 | )
|
492 | );
|
493 | }
|
494 | }
|
495 |
|
496 | _createMocks() {
|
497 | const fn = (
|
498 | impl
|
499 | ) => this._moduleMocker.fn().mockImplementation(impl);
|
500 |
|
501 | const promisifiableFakeSetTimeout = fn(this._fakeSetTimeout.bind(this));
|
502 |
|
503 | promisifiableFakeSetTimeout[_util().default.promisify.custom] = (
|
504 | delay,
|
505 | arg
|
506 | ) =>
|
507 | new Promise(resolve => promisifiableFakeSetTimeout(resolve, delay, arg));
|
508 |
|
509 | this._fakeTimerAPIs = {
|
510 | cancelAnimationFrame: fn(this._fakeClearTimer.bind(this)),
|
511 | clearImmediate: fn(this._fakeClearImmediate.bind(this)),
|
512 | clearInterval: fn(this._fakeClearTimer.bind(this)),
|
513 | clearTimeout: fn(this._fakeClearTimer.bind(this)),
|
514 | nextTick: fn(this._fakeNextTick.bind(this)),
|
515 |
|
516 | requestAnimationFrame: fn(this._fakeRequestAnimationFrame.bind(this)),
|
517 |
|
518 | setImmediate: fn(this._fakeSetImmediate.bind(this)),
|
519 |
|
520 | setInterval: fn(this._fakeSetInterval.bind(this)),
|
521 |
|
522 | setTimeout: promisifiableFakeSetTimeout
|
523 | };
|
524 | }
|
525 |
|
526 | _fakeClearTimer(timerRef) {
|
527 | const uuid = this._timerConfig.refToId(timerRef);
|
528 |
|
529 | if (uuid) {
|
530 | this._timers.delete(String(uuid));
|
531 | }
|
532 | }
|
533 |
|
534 | _fakeClearImmediate(uuid) {
|
535 | this._immediates = this._immediates.filter(
|
536 | immediate => immediate.uuid !== uuid
|
537 | );
|
538 | }
|
539 |
|
540 | _fakeNextTick(callback, ...args) {
|
541 | if (this._disposed) {
|
542 | return;
|
543 | }
|
544 |
|
545 | const uuid = String(this._uuidCounter++);
|
546 |
|
547 | this._ticks.push({
|
548 | callback: () => callback.apply(null, args),
|
549 | uuid
|
550 | });
|
551 |
|
552 | const cancelledTicks = this._cancelledTicks;
|
553 |
|
554 | this._timerAPIs.nextTick(() => {
|
555 | if (!cancelledTicks.hasOwnProperty(uuid)) {
|
556 |
|
557 | cancelledTicks[uuid] = true;
|
558 | callback.apply(null, args);
|
559 | }
|
560 | });
|
561 | }
|
562 |
|
563 | _fakeRequestAnimationFrame(callback) {
|
564 | return this._fakeSetTimeout(() => {
|
565 |
|
566 | callback(this._now);
|
567 | }, 1000 / 60);
|
568 | }
|
569 |
|
570 | _fakeSetImmediate(callback, ...args) {
|
571 | if (this._disposed) {
|
572 | return null;
|
573 | }
|
574 |
|
575 | const uuid = String(this._uuidCounter++);
|
576 |
|
577 | this._immediates.push({
|
578 | callback: () => callback.apply(null, args),
|
579 | uuid
|
580 | });
|
581 |
|
582 | this._timerAPIs.setImmediate(() => {
|
583 | if (this._immediates.find(x => x.uuid === uuid)) {
|
584 | try {
|
585 | callback.apply(null, args);
|
586 | } finally {
|
587 | this._fakeClearImmediate(uuid);
|
588 | }
|
589 | }
|
590 | });
|
591 |
|
592 | return uuid;
|
593 | }
|
594 |
|
595 | _fakeSetInterval(callback, intervalDelay, ...args) {
|
596 | if (this._disposed) {
|
597 | return null;
|
598 | }
|
599 |
|
600 | if (intervalDelay == null) {
|
601 | intervalDelay = 0;
|
602 | }
|
603 |
|
604 | const uuid = this._uuidCounter++;
|
605 |
|
606 | this._timers.set(String(uuid), {
|
607 | callback: () => callback.apply(null, args),
|
608 | expiry: this._now + intervalDelay,
|
609 | interval: intervalDelay,
|
610 | type: 'interval'
|
611 | });
|
612 |
|
613 | return this._timerConfig.idToRef(uuid);
|
614 | }
|
615 |
|
616 | _fakeSetTimeout(callback, delay, ...args) {
|
617 | if (this._disposed) {
|
618 | return null;
|
619 | }
|
620 |
|
621 | delay = Number(delay) | 0;
|
622 | const uuid = this._uuidCounter++;
|
623 |
|
624 | this._timers.set(String(uuid), {
|
625 | callback: () => callback.apply(null, args),
|
626 | expiry: this._now + delay,
|
627 | interval: undefined,
|
628 | type: 'timeout'
|
629 | });
|
630 |
|
631 | return this._timerConfig.idToRef(uuid);
|
632 | }
|
633 |
|
634 | _getNextTimerHandle() {
|
635 | let nextTimerHandle = null;
|
636 | let soonestTime = MS_IN_A_YEAR;
|
637 |
|
638 | this._timers.forEach((timer, uuid) => {
|
639 | if (timer.expiry < soonestTime) {
|
640 | soonestTime = timer.expiry;
|
641 | nextTimerHandle = uuid;
|
642 | }
|
643 | });
|
644 |
|
645 | return nextTimerHandle;
|
646 | }
|
647 |
|
648 | _runTimerHandle(timerHandle) {
|
649 | const timer = this._timers.get(timerHandle);
|
650 |
|
651 | if (!timer) {
|
652 | return;
|
653 | }
|
654 |
|
655 | switch (timer.type) {
|
656 | case 'timeout':
|
657 | this._timers.delete(timerHandle);
|
658 |
|
659 | timer.callback();
|
660 | break;
|
661 |
|
662 | case 'interval':
|
663 | timer.expiry = this._now + (timer.interval || 0);
|
664 | timer.callback();
|
665 | break;
|
666 |
|
667 | default:
|
668 | throw new Error('Unexpected timer type: ' + timer.type);
|
669 | }
|
670 | }
|
671 | }
|
672 |
|
673 | exports.default = FakeTimers;
|