fake-async-test.js 30 KB

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