fake-async-test.umd.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. 'use strict';
  2. var __assign = (this && this.__assign) || function () {
  3. __assign = Object.assign || function(t) {
  4. for (var s, i = 1, n = arguments.length; i < n; i++) {
  5. s = arguments[i];
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  7. t[p] = s[p];
  8. }
  9. return t;
  10. };
  11. return __assign.apply(this, arguments);
  12. };
  13. var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
  14. if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
  15. if (ar || !(i in from)) {
  16. if (!ar) ar = Array.prototype.slice.call(from, 0, i);
  17. ar[i] = from[i];
  18. }
  19. }
  20. return to.concat(ar || Array.prototype.slice.call(from));
  21. };
  22. /**
  23. * @license Angular v<unknown>
  24. * (c) 2010-2024 Google LLC. https://angular.io/
  25. * License: MIT
  26. */
  27. (function (factory) {
  28. typeof define === 'function' && define.amd ? define(factory) :
  29. factory();
  30. })((function () {
  31. 'use strict';
  32. var _a;
  33. var global = (typeof window === 'object' && window) || (typeof self === 'object' && self) || globalThis.global;
  34. var OriginalDate = global.Date;
  35. // Since when we compile this file to `es2015`, and if we define
  36. // this `FakeDate` as `class FakeDate`, and then set `FakeDate.prototype`
  37. // there will be an error which is `Cannot assign to read only property 'prototype'`
  38. // so we need to use function implementation here.
  39. function FakeDate() {
  40. if (arguments.length === 0) {
  41. var d = new OriginalDate();
  42. d.setTime(FakeDate.now());
  43. return d;
  44. }
  45. else {
  46. var args = Array.prototype.slice.call(arguments);
  47. return new (OriginalDate.bind.apply(OriginalDate, __spreadArray([void 0], args, false)))();
  48. }
  49. }
  50. FakeDate.now = function () {
  51. var fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
  52. if (fakeAsyncTestZoneSpec) {
  53. return fakeAsyncTestZoneSpec.getFakeSystemTime();
  54. }
  55. return OriginalDate.now.apply(this, arguments);
  56. };
  57. FakeDate.UTC = OriginalDate.UTC;
  58. FakeDate.parse = OriginalDate.parse;
  59. // keep a reference for zone patched timer function
  60. var patchedTimers;
  61. var timeoutCallback = function () { };
  62. var Scheduler = /** @class */ (function () {
  63. function Scheduler() {
  64. // Scheduler queue with the tuple of end time and callback function - sorted by end time.
  65. this._schedulerQueue = [];
  66. // Current simulated time in millis.
  67. this._currentTickTime = 0;
  68. // Current fake system base time in millis.
  69. this._currentFakeBaseSystemTime = OriginalDate.now();
  70. // track requeuePeriodicTimer
  71. this._currentTickRequeuePeriodicEntries = [];
  72. }
  73. Scheduler.getNextId = function () {
  74. var id = patchedTimers.nativeSetTimeout.call(global, timeoutCallback, 0);
  75. patchedTimers.nativeClearTimeout.call(global, id);
  76. if (typeof id === 'number') {
  77. return id;
  78. }
  79. // in NodeJS, we just use a number for fakeAsync, since it will not
  80. // conflict with native TimeoutId
  81. return _a.nextNodeJSId++;
  82. };
  83. Scheduler.prototype.getCurrentTickTime = function () {
  84. return this._currentTickTime;
  85. };
  86. Scheduler.prototype.getFakeSystemTime = function () {
  87. return this._currentFakeBaseSystemTime + this._currentTickTime;
  88. };
  89. Scheduler.prototype.setFakeBaseSystemTime = function (fakeBaseSystemTime) {
  90. this._currentFakeBaseSystemTime = fakeBaseSystemTime;
  91. };
  92. Scheduler.prototype.getRealSystemTime = function () {
  93. return OriginalDate.now();
  94. };
  95. Scheduler.prototype.scheduleFunction = function (cb, delay, options) {
  96. options = __assign({
  97. args: [],
  98. isPeriodic: false,
  99. isRequestAnimationFrame: false,
  100. id: -1,
  101. isRequeuePeriodic: false,
  102. }, options);
  103. var currentId = options.id < 0 ? _a.nextId : options.id;
  104. _a.nextId = _a.getNextId();
  105. var endTime = this._currentTickTime + delay;
  106. // Insert so that scheduler queue remains sorted by end time.
  107. var newEntry = {
  108. endTime: endTime,
  109. id: currentId,
  110. func: cb,
  111. args: options.args,
  112. delay: delay,
  113. isPeriodic: options.isPeriodic,
  114. isRequestAnimationFrame: options.isRequestAnimationFrame,
  115. };
  116. if (options.isRequeuePeriodic) {
  117. this._currentTickRequeuePeriodicEntries.push(newEntry);
  118. }
  119. var i = 0;
  120. for (; i < this._schedulerQueue.length; i++) {
  121. var currentEntry = this._schedulerQueue[i];
  122. if (newEntry.endTime < currentEntry.endTime) {
  123. break;
  124. }
  125. }
  126. this._schedulerQueue.splice(i, 0, newEntry);
  127. return currentId;
  128. };
  129. Scheduler.prototype.removeScheduledFunctionWithId = function (id) {
  130. for (var i = 0; i < this._schedulerQueue.length; i++) {
  131. if (this._schedulerQueue[i].id == id) {
  132. this._schedulerQueue.splice(i, 1);
  133. break;
  134. }
  135. }
  136. };
  137. Scheduler.prototype.removeAll = function () {
  138. this._schedulerQueue = [];
  139. };
  140. Scheduler.prototype.getTimerCount = function () {
  141. return this._schedulerQueue.length;
  142. };
  143. Scheduler.prototype.tickToNext = function (step, doTick, tickOptions) {
  144. if (step === void 0) { step = 1; }
  145. if (this._schedulerQueue.length < step) {
  146. return;
  147. }
  148. // Find the last task currently queued in the scheduler queue and tick
  149. // till that time.
  150. var startTime = this._currentTickTime;
  151. var targetTask = this._schedulerQueue[step - 1];
  152. this.tick(targetTask.endTime - startTime, doTick, tickOptions);
  153. };
  154. Scheduler.prototype.tick = function (millis, doTick, tickOptions) {
  155. if (millis === void 0) { millis = 0; }
  156. var finalTime = this._currentTickTime + millis;
  157. var lastCurrentTime = 0;
  158. tickOptions = Object.assign({ processNewMacroTasksSynchronously: true }, tickOptions);
  159. // we need to copy the schedulerQueue so nested timeout
  160. // will not be wrongly called in the current tick
  161. // https://github.com/angular/angular/issues/33799
  162. var schedulerQueue = tickOptions.processNewMacroTasksSynchronously
  163. ? this._schedulerQueue
  164. : this._schedulerQueue.slice();
  165. if (schedulerQueue.length === 0 && doTick) {
  166. doTick(millis);
  167. return;
  168. }
  169. while (schedulerQueue.length > 0) {
  170. // clear requeueEntries before each loop
  171. this._currentTickRequeuePeriodicEntries = [];
  172. var current = schedulerQueue[0];
  173. if (finalTime < current.endTime) {
  174. // Done processing the queue since it's sorted by endTime.
  175. break;
  176. }
  177. else {
  178. // Time to run scheduled function. Remove it from the head of queue.
  179. var current_1 = schedulerQueue.shift();
  180. if (!tickOptions.processNewMacroTasksSynchronously) {
  181. var idx = this._schedulerQueue.indexOf(current_1);
  182. if (idx >= 0) {
  183. this._schedulerQueue.splice(idx, 1);
  184. }
  185. }
  186. lastCurrentTime = this._currentTickTime;
  187. this._currentTickTime = current_1.endTime;
  188. if (doTick) {
  189. doTick(this._currentTickTime - lastCurrentTime);
  190. }
  191. var retval = current_1.func.apply(global, current_1.isRequestAnimationFrame ? [this._currentTickTime] : current_1.args);
  192. if (!retval) {
  193. // Uncaught exception in the current scheduled function. Stop processing the queue.
  194. break;
  195. }
  196. // check is there any requeue periodic entry is added in
  197. // current loop, if there is, we need to add to current loop
  198. if (!tickOptions.processNewMacroTasksSynchronously) {
  199. this._currentTickRequeuePeriodicEntries.forEach(function (newEntry) {
  200. var i = 0;
  201. for (; i < schedulerQueue.length; i++) {
  202. var currentEntry = schedulerQueue[i];
  203. if (newEntry.endTime < currentEntry.endTime) {
  204. break;
  205. }
  206. }
  207. schedulerQueue.splice(i, 0, newEntry);
  208. });
  209. }
  210. }
  211. }
  212. lastCurrentTime = this._currentTickTime;
  213. this._currentTickTime = finalTime;
  214. if (doTick) {
  215. doTick(this._currentTickTime - lastCurrentTime);
  216. }
  217. };
  218. Scheduler.prototype.flushOnlyPendingTimers = function (doTick) {
  219. if (this._schedulerQueue.length === 0) {
  220. return 0;
  221. }
  222. // Find the last task currently queued in the scheduler queue and tick
  223. // till that time.
  224. var startTime = this._currentTickTime;
  225. var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1];
  226. this.tick(lastTask.endTime - startTime, doTick, { processNewMacroTasksSynchronously: false });
  227. return this._currentTickTime - startTime;
  228. };
  229. Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) {
  230. if (limit === void 0) { limit = 20; }
  231. if (flushPeriodic === void 0) { flushPeriodic = false; }
  232. if (flushPeriodic) {
  233. return this.flushPeriodic(doTick);
  234. }
  235. else {
  236. return this.flushNonPeriodic(limit, doTick);
  237. }
  238. };
  239. Scheduler.prototype.flushPeriodic = function (doTick) {
  240. if (this._schedulerQueue.length === 0) {
  241. return 0;
  242. }
  243. // Find the last task currently queued in the scheduler queue and tick
  244. // till that time.
  245. var startTime = this._currentTickTime;
  246. var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1];
  247. this.tick(lastTask.endTime - startTime, doTick);
  248. return this._currentTickTime - startTime;
  249. };
  250. Scheduler.prototype.flushNonPeriodic = function (limit, doTick) {
  251. var startTime = this._currentTickTime;
  252. var lastCurrentTime = 0;
  253. var count = 0;
  254. while (this._schedulerQueue.length > 0) {
  255. count++;
  256. if (count > limit) {
  257. throw new Error('flush failed after reaching the limit of ' +
  258. limit +
  259. ' tasks. Does your code use a polling timeout?');
  260. }
  261. // flush only non-periodic timers.
  262. // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing.
  263. if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; })
  264. .length === 0) {
  265. break;
  266. }
  267. var current = this._schedulerQueue.shift();
  268. lastCurrentTime = this._currentTickTime;
  269. this._currentTickTime = current.endTime;
  270. if (doTick) {
  271. // Update any secondary schedulers like Jasmine mock Date.
  272. doTick(this._currentTickTime - lastCurrentTime);
  273. }
  274. var retval = current.func.apply(global, current.args);
  275. if (!retval) {
  276. // Uncaught exception in the current scheduled function. Stop processing the queue.
  277. break;
  278. }
  279. }
  280. return this._currentTickTime - startTime;
  281. };
  282. return Scheduler;
  283. }());
  284. _a = Scheduler;
  285. // Next scheduler id.
  286. (function () {
  287. _a.nextNodeJSId = 1;
  288. })();
  289. (function () {
  290. _a.nextId = -1;
  291. })();
  292. var FakeAsyncTestZoneSpec = /** @class */ (function () {
  293. function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) {
  294. if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; }
  295. this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame;
  296. this.macroTaskOptions = macroTaskOptions;
  297. this._scheduler = new Scheduler();
  298. this._microtasks = [];
  299. this._lastError = null;
  300. this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')];
  301. this.pendingPeriodicTimers = [];
  302. this.pendingTimers = [];
  303. this.patchDateLocked = false;
  304. this.properties = { 'FakeAsyncTestZoneSpec': this };
  305. this.name = 'fakeAsyncTestZone for ' + namePrefix;
  306. // in case user can't access the construction of FakeAsyncTestSpec
  307. // user can also define macroTaskOptions by define a global variable.
  308. if (!this.macroTaskOptions) {
  309. this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')];
  310. }
  311. }
  312. FakeAsyncTestZoneSpec.assertInZone = function () {
  313. if (Zone.current.get('FakeAsyncTestZoneSpec') == null) {
  314. throw new Error('The code should be running in the fakeAsync zone to call this function');
  315. }
  316. };
  317. FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) {
  318. var _this = this;
  319. return function () {
  320. var args = [];
  321. for (var _i = 0; _i < arguments.length; _i++) {
  322. args[_i] = arguments[_i];
  323. }
  324. fn.apply(global, args);
  325. if (_this._lastError === null) {
  326. // Success
  327. if (completers.onSuccess != null) {
  328. completers.onSuccess.apply(global);
  329. }
  330. // Flush microtasks only on success.
  331. _this.flushMicrotasks();
  332. }
  333. else {
  334. // Failure
  335. if (completers.onError != null) {
  336. completers.onError.apply(global);
  337. }
  338. }
  339. // Return true if there were no errors, false otherwise.
  340. return _this._lastError === null;
  341. };
  342. };
  343. FakeAsyncTestZoneSpec._removeTimer = function (timers, id) {
  344. var index = timers.indexOf(id);
  345. if (index > -1) {
  346. timers.splice(index, 1);
  347. }
  348. };
  349. FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) {
  350. var _this = this;
  351. return function () {
  352. FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id);
  353. };
  354. };
  355. FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) {
  356. var _this = this;
  357. return function () {
  358. // Requeue the timer callback if it's not been canceled.
  359. if (_this.pendingPeriodicTimers.indexOf(id) !== -1) {
  360. _this._scheduler.scheduleFunction(fn, interval, {
  361. args: args,
  362. isPeriodic: true,
  363. id: id,
  364. isRequeuePeriodic: true,
  365. });
  366. }
  367. };
  368. };
  369. FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) {
  370. var _this = this;
  371. return function () {
  372. FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id);
  373. };
  374. };
  375. FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) {
  376. if (isTimer === void 0) { isTimer = true; }
  377. var removeTimerFn = this._dequeueTimer(Scheduler.nextId);
  378. // Queue the callback and dequeue the timer on success and error.
  379. var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn });
  380. var id = this._scheduler.scheduleFunction(cb, delay, { args: args, isRequestAnimationFrame: !isTimer });
  381. if (isTimer) {
  382. this.pendingTimers.push(id);
  383. }
  384. return id;
  385. };
  386. FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) {
  387. FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id);
  388. this._scheduler.removeScheduledFunctionWithId(id);
  389. };
  390. FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval, args) {
  391. var id = Scheduler.nextId;
  392. var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) };
  393. var cb = this._fnAndFlush(fn, completers);
  394. // Use the callback created above to requeue on success.
  395. completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id);
  396. // Queue the callback and dequeue the periodic timer only on error.
  397. this._scheduler.scheduleFunction(cb, interval, { args: args, isPeriodic: true });
  398. this.pendingPeriodicTimers.push(id);
  399. return id;
  400. };
  401. FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) {
  402. FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id);
  403. this._scheduler.removeScheduledFunctionWithId(id);
  404. };
  405. FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () {
  406. var error = this._lastError || this._uncaughtPromiseErrors[0];
  407. this._uncaughtPromiseErrors.length = 0;
  408. this._lastError = null;
  409. throw error;
  410. };
  411. FakeAsyncTestZoneSpec.prototype.getCurrentTickTime = function () {
  412. return this._scheduler.getCurrentTickTime();
  413. };
  414. FakeAsyncTestZoneSpec.prototype.getFakeSystemTime = function () {
  415. return this._scheduler.getFakeSystemTime();
  416. };
  417. FakeAsyncTestZoneSpec.prototype.setFakeBaseSystemTime = function (realTime) {
  418. this._scheduler.setFakeBaseSystemTime(realTime);
  419. };
  420. FakeAsyncTestZoneSpec.prototype.getRealSystemTime = function () {
  421. return this._scheduler.getRealSystemTime();
  422. };
  423. FakeAsyncTestZoneSpec.patchDate = function () {
  424. if (!!global[Zone.__symbol__('disableDatePatching')]) {
  425. // we don't want to patch global Date
  426. // because in some case, global Date
  427. // is already being patched, we need to provide
  428. // an option to let user still use their
  429. // own version of Date.
  430. return;
  431. }
  432. if (global['Date'] === FakeDate) {
  433. // already patched
  434. return;
  435. }
  436. global['Date'] = FakeDate;
  437. FakeDate.prototype = OriginalDate.prototype;
  438. // try check and reset timers
  439. // because jasmine.clock().install() may
  440. // have replaced the global timer
  441. FakeAsyncTestZoneSpec.checkTimerPatch();
  442. };
  443. FakeAsyncTestZoneSpec.resetDate = function () {
  444. if (global['Date'] === FakeDate) {
  445. global['Date'] = OriginalDate;
  446. }
  447. };
  448. FakeAsyncTestZoneSpec.checkTimerPatch = function () {
  449. if (!patchedTimers) {
  450. throw new Error('Expected timers to have been patched.');
  451. }
  452. if (global.setTimeout !== patchedTimers.setTimeout) {
  453. global.setTimeout = patchedTimers.setTimeout;
  454. global.clearTimeout = patchedTimers.clearTimeout;
  455. }
  456. if (global.setInterval !== patchedTimers.setInterval) {
  457. global.setInterval = patchedTimers.setInterval;
  458. global.clearInterval = patchedTimers.clearInterval;
  459. }
  460. };
  461. FakeAsyncTestZoneSpec.prototype.lockDatePatch = function () {
  462. this.patchDateLocked = true;
  463. FakeAsyncTestZoneSpec.patchDate();
  464. };
  465. FakeAsyncTestZoneSpec.prototype.unlockDatePatch = function () {
  466. this.patchDateLocked = false;
  467. FakeAsyncTestZoneSpec.resetDate();
  468. };
  469. FakeAsyncTestZoneSpec.prototype.tickToNext = function (steps, doTick, tickOptions) {
  470. if (steps === void 0) { steps = 1; }
  471. if (tickOptions === void 0) { tickOptions = { processNewMacroTasksSynchronously: true }; }
  472. if (steps <= 0) {
  473. return;
  474. }
  475. FakeAsyncTestZoneSpec.assertInZone();
  476. this.flushMicrotasks();
  477. this._scheduler.tickToNext(steps, doTick, tickOptions);
  478. if (this._lastError !== null) {
  479. this._resetLastErrorAndThrow();
  480. }
  481. };
  482. FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick, tickOptions) {
  483. if (millis === void 0) { millis = 0; }
  484. if (tickOptions === void 0) { tickOptions = { processNewMacroTasksSynchronously: true }; }
  485. FakeAsyncTestZoneSpec.assertInZone();
  486. this.flushMicrotasks();
  487. this._scheduler.tick(millis, doTick, tickOptions);
  488. if (this._lastError !== null) {
  489. this._resetLastErrorAndThrow();
  490. }
  491. };
  492. FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () {
  493. var _this = this;
  494. FakeAsyncTestZoneSpec.assertInZone();
  495. var flushErrors = function () {
  496. if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) {
  497. // If there is an error stop processing the microtask queue and rethrow the error.
  498. _this._resetLastErrorAndThrow();
  499. }
  500. };
  501. while (this._microtasks.length > 0) {
  502. var microtask = this._microtasks.shift();
  503. microtask.func.apply(microtask.target, microtask.args);
  504. }
  505. flushErrors();
  506. };
  507. FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) {
  508. FakeAsyncTestZoneSpec.assertInZone();
  509. this.flushMicrotasks();
  510. var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick);
  511. if (this._lastError !== null) {
  512. this._resetLastErrorAndThrow();
  513. }
  514. return elapsed;
  515. };
  516. FakeAsyncTestZoneSpec.prototype.flushOnlyPendingTimers = function (doTick) {
  517. FakeAsyncTestZoneSpec.assertInZone();
  518. this.flushMicrotasks();
  519. var elapsed = this._scheduler.flushOnlyPendingTimers(doTick);
  520. if (this._lastError !== null) {
  521. this._resetLastErrorAndThrow();
  522. }
  523. return elapsed;
  524. };
  525. FakeAsyncTestZoneSpec.prototype.removeAllTimers = function () {
  526. FakeAsyncTestZoneSpec.assertInZone();
  527. this._scheduler.removeAll();
  528. this.pendingPeriodicTimers = [];
  529. this.pendingTimers = [];
  530. };
  531. FakeAsyncTestZoneSpec.prototype.getTimerCount = function () {
  532. return this._scheduler.getTimerCount() + this._microtasks.length;
  533. };
  534. FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) {
  535. switch (task.type) {
  536. case 'microTask':
  537. var args = task.data && task.data.args;
  538. // should pass additional arguments to callback if have any
  539. // currently we know process.nextTick will have such additional
  540. // arguments
  541. var additionalArgs = void 0;
  542. if (args) {
  543. var callbackIndex = task.data.cbIdx;
  544. if (typeof args.length === 'number' && args.length > callbackIndex + 1) {
  545. additionalArgs = Array.prototype.slice.call(args, callbackIndex + 1);
  546. }
  547. }
  548. this._microtasks.push({
  549. func: task.invoke,
  550. args: additionalArgs,
  551. target: task.data && task.data.target,
  552. });
  553. break;
  554. case 'macroTask':
  555. switch (task.source) {
  556. case 'setTimeout':
  557. task.data['handleId'] = this._setTimeout(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2));
  558. break;
  559. case 'setImmediate':
  560. task.data['handleId'] = this._setTimeout(task.invoke, 0, Array.prototype.slice.call(task.data['args'], 1));
  561. break;
  562. case 'setInterval':
  563. task.data['handleId'] = this._setInterval(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2));
  564. break;
  565. case 'XMLHttpRequest.send':
  566. throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' +
  567. task.data['url']);
  568. case 'requestAnimationFrame':
  569. case 'webkitRequestAnimationFrame':
  570. case 'mozRequestAnimationFrame':
  571. // Simulate a requestAnimationFrame by using a setTimeout with 16 ms.
  572. // (60 frames per second)
  573. task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame);
  574. break;
  575. default:
  576. // user can define which macroTask they want to support by passing
  577. // macroTaskOptions
  578. var macroTaskOption = this.findMacroTaskOption(task);
  579. if (macroTaskOption) {
  580. var args_1 = task.data && task.data['args'];
  581. var delay = args_1 && args_1.length > 1 ? args_1[1] : 0;
  582. var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1;
  583. if (!!macroTaskOption.isPeriodic) {
  584. // periodic macroTask, use setInterval to simulate
  585. task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs);
  586. task.data.isPeriodic = true;
  587. }
  588. else {
  589. // not periodic, use setTimeout to simulate
  590. task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs);
  591. }
  592. break;
  593. }
  594. throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source);
  595. }
  596. break;
  597. case 'eventTask':
  598. task = delegate.scheduleTask(target, task);
  599. break;
  600. }
  601. return task;
  602. };
  603. FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) {
  604. switch (task.source) {
  605. case 'setTimeout':
  606. case 'requestAnimationFrame':
  607. case 'webkitRequestAnimationFrame':
  608. case 'mozRequestAnimationFrame':
  609. return this._clearTimeout(task.data['handleId']);
  610. case 'setInterval':
  611. return this._clearInterval(task.data['handleId']);
  612. default:
  613. // user can define which macroTask they want to support by passing
  614. // macroTaskOptions
  615. var macroTaskOption = this.findMacroTaskOption(task);
  616. if (macroTaskOption) {
  617. var handleId = task.data['handleId'];
  618. return macroTaskOption.isPeriodic
  619. ? this._clearInterval(handleId)
  620. : this._clearTimeout(handleId);
  621. }
  622. return delegate.cancelTask(target, task);
  623. }
  624. };
  625. FakeAsyncTestZoneSpec.prototype.onInvoke = function (delegate, current, target, callback, applyThis, applyArgs, source) {
  626. try {
  627. FakeAsyncTestZoneSpec.patchDate();
  628. return delegate.invoke(target, callback, applyThis, applyArgs, source);
  629. }
  630. finally {
  631. if (!this.patchDateLocked) {
  632. FakeAsyncTestZoneSpec.resetDate();
  633. }
  634. }
  635. };
  636. FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) {
  637. if (!this.macroTaskOptions) {
  638. return null;
  639. }
  640. for (var i = 0; i < this.macroTaskOptions.length; i++) {
  641. var macroTaskOption = this.macroTaskOptions[i];
  642. if (macroTaskOption.source === task.source) {
  643. return macroTaskOption;
  644. }
  645. }
  646. return null;
  647. };
  648. FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) {
  649. this._lastError = error;
  650. return false; // Don't propagate error to parent zone.
  651. };
  652. return FakeAsyncTestZoneSpec;
  653. }());
  654. var _fakeAsyncTestZoneSpec = null;
  655. function getProxyZoneSpec() {
  656. return Zone && Zone['ProxyZoneSpec'];
  657. }
  658. /**
  659. * Clears out the shared fake async zone for a test.
  660. * To be called in a global `beforeEach`.
  661. *
  662. * @experimental
  663. */
  664. function resetFakeAsyncZone() {
  665. if (_fakeAsyncTestZoneSpec) {
  666. _fakeAsyncTestZoneSpec.unlockDatePatch();
  667. }
  668. _fakeAsyncTestZoneSpec = null;
  669. // in node.js testing we may not have ProxyZoneSpec in which case there is nothing to reset.
  670. getProxyZoneSpec() && getProxyZoneSpec().assertPresent().resetDelegate();
  671. }
  672. /**
  673. * Wraps a function to be executed in the fakeAsync zone:
  674. * - microtasks are manually executed by calling `flushMicrotasks()`,
  675. * - timers are synchronous, `tick()` simulates the asynchronous passage of time.
  676. *
  677. * When flush is `false`, if there are any pending timers at the end of the function,
  678. * an exception will be thrown.
  679. *
  680. * Can be used to wrap inject() calls.
  681. *
  682. * ## Example
  683. *
  684. * {@example core/testing/ts/fake_async.ts region='basic'}
  685. *
  686. * @param fn
  687. * @param options
  688. * flush: when true, will drain the macrotask queue after the test function completes.
  689. * @returns The function wrapped to be executed in the fakeAsync zone
  690. *
  691. * @experimental
  692. */
  693. function fakeAsync(fn, options) {
  694. if (options === void 0) { options = {}; }
  695. var _b = options.flush, flush = _b === void 0 ? true : _b;
  696. // Not using an arrow function to preserve context passed from call site
  697. var fakeAsyncFn = function () {
  698. var args = [];
  699. for (var _i = 0; _i < arguments.length; _i++) {
  700. args[_i] = arguments[_i];
  701. }
  702. var ProxyZoneSpec = getProxyZoneSpec();
  703. if (!ProxyZoneSpec) {
  704. throw new Error('ProxyZoneSpec is needed for the async() test helper but could not be found. ' +
  705. 'Please make sure that your environment includes zone.js/plugins/proxy');
  706. }
  707. var proxyZoneSpec = ProxyZoneSpec.assertPresent();
  708. if (Zone.current.get('FakeAsyncTestZoneSpec')) {
  709. throw new Error('fakeAsync() calls can not be nested');
  710. }
  711. try {
  712. // in case jasmine.clock init a fakeAsyncTestZoneSpec
  713. if (!_fakeAsyncTestZoneSpec) {
  714. var FakeAsyncTestZoneSpec_1 = Zone && Zone['FakeAsyncTestZoneSpec'];
  715. if (proxyZoneSpec.getDelegate() instanceof FakeAsyncTestZoneSpec_1) {
  716. throw new Error('fakeAsync() calls can not be nested');
  717. }
  718. _fakeAsyncTestZoneSpec = new FakeAsyncTestZoneSpec_1();
  719. }
  720. var res = void 0;
  721. var lastProxyZoneSpec = proxyZoneSpec.getDelegate();
  722. proxyZoneSpec.setDelegate(_fakeAsyncTestZoneSpec);
  723. _fakeAsyncTestZoneSpec.lockDatePatch();
  724. try {
  725. res = fn.apply(this, args);
  726. if (flush) {
  727. _fakeAsyncTestZoneSpec.flush(20, true);
  728. }
  729. else {
  730. flushMicrotasks();
  731. }
  732. }
  733. finally {
  734. proxyZoneSpec.setDelegate(lastProxyZoneSpec);
  735. }
  736. if (!flush) {
  737. if (_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length > 0) {
  738. throw new Error("".concat(_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length, " ") +
  739. "periodic timer(s) still in the queue.");
  740. }
  741. if (_fakeAsyncTestZoneSpec.pendingTimers.length > 0) {
  742. throw new Error("".concat(_fakeAsyncTestZoneSpec.pendingTimers.length, " timer(s) still in the queue."));
  743. }
  744. }
  745. return res;
  746. }
  747. finally {
  748. resetFakeAsyncZone();
  749. }
  750. };
  751. fakeAsyncFn.isFakeAsync = true;
  752. return fakeAsyncFn;
  753. }
  754. function _getFakeAsyncZoneSpec() {
  755. if (_fakeAsyncTestZoneSpec == null) {
  756. _fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
  757. if (_fakeAsyncTestZoneSpec == null) {
  758. throw new Error('The code should be running in the fakeAsync zone to call this function');
  759. }
  760. }
  761. return _fakeAsyncTestZoneSpec;
  762. }
  763. /**
  764. * Simulates the asynchronous passage of time for the timers in the fakeAsync zone.
  765. *
  766. * The microtasks queue is drained at the very start of this function and after any timer
  767. * callback has been executed.
  768. *
  769. * ## Example
  770. *
  771. * {@example core/testing/ts/fake_async.ts region='basic'}
  772. *
  773. * @experimental
  774. */
  775. function tick(millis, ignoreNestedTimeout) {
  776. if (millis === void 0) { millis = 0; }
  777. if (ignoreNestedTimeout === void 0) { ignoreNestedTimeout = false; }
  778. _getFakeAsyncZoneSpec().tick(millis, null, ignoreNestedTimeout);
  779. }
  780. /**
  781. * Simulates the asynchronous passage of time for the timers in the fakeAsync zone by
  782. * draining the macrotask queue until it is empty. The returned value is the milliseconds
  783. * of time that would have been elapsed.
  784. *
  785. * @param maxTurns
  786. * @returns The simulated time elapsed, in millis.
  787. *
  788. * @experimental
  789. */
  790. function flush(maxTurns) {
  791. return _getFakeAsyncZoneSpec().flush(maxTurns);
  792. }
  793. /**
  794. * Discard all remaining periodic tasks.
  795. *
  796. * @experimental
  797. */
  798. function discardPeriodicTasks() {
  799. var zoneSpec = _getFakeAsyncZoneSpec();
  800. zoneSpec.pendingPeriodicTimers;
  801. zoneSpec.pendingPeriodicTimers.length = 0;
  802. }
  803. /**
  804. * Flush any pending microtasks.
  805. *
  806. * @experimental
  807. */
  808. function flushMicrotasks() {
  809. _getFakeAsyncZoneSpec().flushMicrotasks();
  810. }
  811. function patchFakeAsyncTest(Zone) {
  812. // Export the class so that new instances can be created with proper
  813. // constructor params.
  814. Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec;
  815. Zone.__load_patch('fakeasync', function (global, Zone, api) {
  816. Zone[api.symbol('fakeAsyncTest')] = {
  817. resetFakeAsyncZone: resetFakeAsyncZone,
  818. flushMicrotasks: flushMicrotasks,
  819. discardPeriodicTasks: discardPeriodicTasks,
  820. tick: tick,
  821. flush: flush,
  822. fakeAsync: fakeAsync,
  823. };
  824. }, true);
  825. patchedTimers = {
  826. setTimeout: global.setTimeout,
  827. setInterval: global.setInterval,
  828. clearTimeout: global.clearTimeout,
  829. clearInterval: global.clearInterval,
  830. nativeSetTimeout: global[Zone.__symbol__('setTimeout')],
  831. nativeClearTimeout: global[Zone.__symbol__('clearTimeout')],
  832. };
  833. Scheduler.nextId = Scheduler.getNextId();
  834. }
  835. patchFakeAsyncTest(Zone);
  836. }));