From 523e2d70d28cd590bab3a7980b1f341d347fc203 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Wed, 9 May 2018 18:14:21 +0300 Subject: [PATCH] feat(zone): Update zonejs to 0.8.26 --- .../zone-js/dist/zone-nativescript.jasmine.js | 718 +++++++-- .../zone-js/dist/zone-nativescript.js | 1408 ++++++++--------- .../zone-js/dist/zone-nativescript.mocha.js | 571 ++++++- 3 files changed, 1802 insertions(+), 895 deletions(-) diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js index ac1cb7093..98bac8f12 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js @@ -27,8 +27,8 @@ var IGNORE_FRAMES = {}; var creationTrace = '__creationTrace__'; var ERROR_TAG = 'STACKTRACE TRACKING'; var SEP_TAG = '__SEP_TAG__'; -var sepTemplate = ''; -var LongStackTrace = (function () { +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = /** @class */ (function () { function LongStackTrace() { this.error = getStacktrace(); this.timestamp = new Date(); @@ -67,7 +67,7 @@ function addErrorStack(lines, error) { } } function renderLongStackTrace(frames, stack) { - var longTrace = [stack.trim()]; + var longTrace = [stack ? stack.trim() : '']; if (frames) { var timestamp = new Date().getTime(); for (var i = 0; i < frames.length; i++) { @@ -91,33 +91,44 @@ Zone['longStackTraceZoneSpec'] = { if (!error) { return undefined; } - var task = error[Zone.__symbol__('currentTask')]; - var trace = task && task.data && task.data[creationTrace]; + var trace = error[Zone.__symbol__('currentTaskTrace')]; if (!trace) { return error.stack; } return renderLongStackTrace(trace, error.stack); }, onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - var currentTask = Zone.currentTask; - var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; - trace = [new LongStackTrace()].concat(trace); - if (trace.length > this.longStackTraceLimit) { - trace.length = this.longStackTraceLimit; - } - if (!task.data) - task.data = {}; - task.data[creationTrace] = trace; + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var currentTask = Zone.currentTask; + var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) + task.data = {}; + task.data[creationTrace] = trace; + } return parentZoneDelegate.scheduleTask(targetZone, task); }, onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) { - var parentTask = Zone.currentTask || error.task; - if (error instanceof Error && parentTask) { - var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); - try { - error.stack = error.longStack = longStack; - } - catch (err) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = error.longStack = longStack; + } + catch (err) { + } } } return parentZoneDelegate.handleError(targetZone, error); @@ -130,27 +141,32 @@ function captureStackTraces(stackTraces, count) { } } function computeIgnoreFrames() { + if (Error.stackTraceLimit <= 0) { + return; + } var frames = []; captureStackTraces(frames, 2); var frames1 = frames[0]; var frames2 = frames[1]; for (var i = 0; i < frames1.length; i++) { var frame1 = frames1[i]; - var frame2 = frames2[i]; - if (!sepTemplate && frame1.indexOf(ERROR_TAG) == -1) { - sepTemplate = frame1.replace(/^(\s*(at)?\s*)([\w\/\<]+)/, '$1' + SEP_TAG); + if (frame1.indexOf(ERROR_TAG) == -1) { + var match = frame1.match(/^\s*at\s+/); + if (match) { + sepTemplate = match[0] + SEP_TAG + ' (http://localhost)'; + break; + } } + } + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + var frame2 = frames2[i]; if (frame1 === frame2) { IGNORE_FRAMES[frame1] = true; } else { break; } - console.log('>>>>>>', sepTemplate, frame1); - } - if (!sepTemplate) { - // If we could not find it default to this text. - sepTemplate = SEP_TAG + '@[native code]'; } } computeIgnoreFrames(); @@ -162,13 +178,16 @@ computeIgnoreFrames(); * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var ProxyZoneSpec = (function () { +var ProxyZoneSpec = /** @class */ (function () { function ProxyZoneSpec(defaultSpecDelegate) { if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } this.defaultSpecDelegate = defaultSpecDelegate; this.name = 'ProxyZone'; this.properties = { 'ProxyZoneSpec': this }; this.propertyKeys = null; + this.lastTaskState = null; + this.isNeedToTriggerHasTask = false; + this.tasks = []; this.setDelegate(defaultSpecDelegate); } ProxyZoneSpec.get = function () { @@ -178,13 +197,14 @@ var ProxyZoneSpec = (function () { return ProxyZoneSpec.get() instanceof ProxyZoneSpec; }; ProxyZoneSpec.assertPresent = function () { - if (!this.isLoaded()) { + if (!ProxyZoneSpec.isLoaded()) { throw new Error("Expected to be running in 'ProxyZone', but it was not found."); } return ProxyZoneSpec.get(); }; ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { var _this = this; + var isNewDelegate = this._delegateSpec !== delegateSpec; this._delegateSpec = delegateSpec; this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); this.propertyKeys = null; @@ -192,13 +212,57 @@ var ProxyZoneSpec = (function () { this.propertyKeys = Object.keys(delegateSpec.properties); this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); } + // if set a new delegateSpec, shoulde check whether need to + // trigger hasTask or not + if (isNewDelegate && this.lastTaskState && + (this.lastTaskState.macroTask || this.lastTaskState.microTask)) { + this.isNeedToTriggerHasTask = true; + } }; ProxyZoneSpec.prototype.getDelegate = function () { return this._delegateSpec; }; ProxyZoneSpec.prototype.resetDelegate = function () { + var delegateSpec = this.getDelegate(); this.setDelegate(this.defaultSpecDelegate); }; + ProxyZoneSpec.prototype.tryTriggerHasTask = function (parentZoneDelegate, currentZone, targetZone) { + if (this.isNeedToTriggerHasTask && this.lastTaskState) { + // last delegateSpec has microTask or macroTask + // should call onHasTask in current delegateSpec + this.isNeedToTriggerHasTask = false; + this.onHasTask(parentZoneDelegate, currentZone, targetZone, this.lastTaskState); + } + }; + ProxyZoneSpec.prototype.removeFromTasks = function (task) { + if (!this.tasks) { + return; + } + for (var i = 0; i < this.tasks.length; i++) { + if (this.tasks[i] === task) { + this.tasks.splice(i, 1); + return; + } + } + }; + ProxyZoneSpec.prototype.getAndClearPendingTasksInfo = function () { + if (this.tasks.length === 0) { + return ''; + } + var taskInfo = this.tasks.map(function (task) { + var dataInfo = task.data && + Object.keys(task.data) + .map(function (key) { + return key + ':' + task.data[key]; + }) + .join(','); + return "type: " + task.type + ", source: " + task.source + ", args: {" + dataInfo + "}"; + }); + var pendingTasksInfo = '--Pendng async tasks are: [' + taskInfo + ']'; + // clear tasks + this.tasks = []; + return pendingTasksInfo; + }; ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { if (this._delegateSpec && this._delegateSpec.onFork) { return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); @@ -216,6 +280,7 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onInvoke) { return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); } @@ -232,6 +297,9 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.tasks.push(task); + } if (this._delegateSpec && this._delegateSpec.onScheduleTask) { return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); } @@ -240,7 +308,11 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { - if (this._delegateSpec && this._delegateSpec.onFork) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onInvokeTask) { return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); } else { @@ -248,6 +320,10 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onCancelTask) { return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); } @@ -256,6 +332,7 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + this.lastTaskState = hasTaskState; if (this._delegateSpec && this._delegateSpec.onHasTask) { this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); } @@ -276,7 +353,7 @@ Zone['ProxyZoneSpec'] = ProxyZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var SyncTestZoneSpec = (function () { +var SyncTestZoneSpec = /** @class */ (function () { function SyncTestZoneSpec(namePrefix) { this.runZone = Zone.current; this.name = 'syncTestZone for ' + namePrefix; @@ -305,45 +382,105 @@ Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var AsyncTestZoneSpec = (function () { +var _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; +var AsyncTestZoneSpec = /** @class */ (function () { function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this.finishCallback = finishCallback; + this.failCallback = failCallback; this._pendingMicroTasks = false; this._pendingMacroTasks = false; this._alreadyErrored = false; + this._isSync = false; this.runZone = Zone.current; - this._finishCallback = finishCallback; - this._failCallback = failCallback; + this.unresolvedChainedPromiseCount = 0; + this.supportWaitUnresolvedChainedPromise = false; this.name = 'asyncTestZone for ' + namePrefix; + this.properties = { 'AsyncTestZoneSpec': this }; + this.supportWaitUnresolvedChainedPromise = + _global[Zone.__symbol__('supportWaitUnResolvedChainedPromise')] === true; } + AsyncTestZoneSpec.prototype.isUnresolvedChainedPromisePending = function () { + return this.unresolvedChainedPromiseCount > 0; + }; AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { var _this = this; - if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + if (!(this._pendingMicroTasks || this._pendingMacroTasks || + (this.supportWaitUnresolvedChainedPromise && this.isUnresolvedChainedPromisePending()))) { // We do this because we would like to catch unhandled rejected promises. this.runZone.run(function () { setTimeout(function () { if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { - _this._finishCallback(); + _this.finishCallback(); } }, 0); }); } }; + AsyncTestZoneSpec.prototype.patchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var patchPromiseForTest = Promise[Zone.__symbol__('patchPromiseForTest')]; + if (patchPromiseForTest) { + patchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.unPatchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var unPatchPromiseForTest = Promise[Zone.__symbol__('unPatchPromiseForTest')]; + if (unPatchPromiseForTest) { + unPatchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + if (task.type === 'microTask' && task.data && task.data instanceof Promise) { + // check whether the promise is a chained promise + if (task.data[AsyncTestZoneSpec.symbolParentUnresolved] === true) { + // chained promise is being scheduled + this.unresolvedChainedPromiseCount--; + } + } + return delegate.scheduleTask(target, task); + }; + AsyncTestZoneSpec.prototype.onInvokeTask = function (delegate, current, target, task, applyThis, applyArgs) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.invokeTask(target, task, applyThis, applyArgs); + }; + AsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.cancelTask(target, task); + }; // Note - we need to use onInvoke at the moment to call finish when a test is // fully synchronous. TODO(juliemr): remove this when the logic for // onHasTask changes and it calls whenever the task queues are dirty. + // updated by(JiaLiPassion), only call finish callback when no task + // was scheduled/invoked/canceled. AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { try { + this._isSync = true; return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); } finally { - this._finishCallbackIfDone(); + var afterTaskCounts = parentZoneDelegate._taskCounts; + if (this._isSync) { + this._finishCallbackIfDone(); + } } }; AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { // Let the parent try to handle the error. var result = parentZoneDelegate.handleError(targetZone, error); if (result) { - this._failCallback(error); + this.failCallback(error); this._alreadyErrored = true; } return false; @@ -359,6 +496,7 @@ var AsyncTestZoneSpec = (function () { this._finishCallbackIfDone(); } }; + AsyncTestZoneSpec.symbolParentUnresolved = Zone.__symbol__('parentUnresolved'); return AsyncTestZoneSpec; }()); // Export the class so that new instances can be created with proper @@ -372,23 +510,95 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +var __read = (undefined && undefined.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spread = (undefined && undefined.__spread) || function () { + for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); + return ar; +}; (function (global) { - var Scheduler = (function () { + var OriginalDate = global.Date; + var FakeDate = /** @class */ (function () { + function FakeDate() { + if (arguments.length === 0) { + var d = new OriginalDate(); + d.setTime(FakeDate.now()); + return d; + } + else { + var args = Array.prototype.slice.call(arguments); + return new (OriginalDate.bind.apply(OriginalDate, __spread([void 0], args)))(); + } + } + FakeDate.now = function () { + var fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncTestZoneSpec) { + return fakeAsyncTestZoneSpec.getCurrentRealTime() + fakeAsyncTestZoneSpec.getCurrentTime(); + } + return OriginalDate.now.apply(this, arguments); + }; + return FakeDate; + }()); + FakeDate.UTC = OriginalDate.UTC; + FakeDate.parse = OriginalDate.parse; + // keep a reference for zone patched timer function + var timers = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval + }; + var Scheduler = /** @class */ (function () { function Scheduler() { // Next scheduler id. - this.nextId = 0; + this.nextId = 1; // Scheduler queue with the tuple of end time and callback function - sorted by end time. this._schedulerQueue = []; // Current simulated time in millis. this._currentTime = 0; + // Current real time in millis. + this._currentRealTime = OriginalDate.now(); } - Scheduler.prototype.scheduleFunction = function (cb, delay, args, id) { + Scheduler.prototype.getCurrentTime = function () { + return this._currentTime; + }; + Scheduler.prototype.getCurrentRealTime = function () { + return this._currentRealTime; + }; + Scheduler.prototype.setCurrentRealTime = function (realTime) { + this._currentRealTime = realTime; + }; + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } if (id === void 0) { id = -1; } var currentId = id < 0 ? this.nextId++ : id; var endTime = this._currentTime + delay; // Insert so that scheduler queue remains sorted by end time. - var newEntry = { endTime: endTime, id: currentId, func: cb, args: args, delay: delay }; + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; var i = 0; for (; i < this._schedulerQueue.length; i++) { var currentEntry = this._schedulerQueue[i]; @@ -407,9 +617,14 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } } }; - Scheduler.prototype.tick = function (millis) { + Scheduler.prototype.tick = function (millis, doTick) { if (millis === void 0) { millis = 0; } var finalTime = this._currentTime + millis; + var lastCurrentTime = 0; + if (this._schedulerQueue.length === 0 && doTick) { + doTick(millis); + return; + } while (this._schedulerQueue.length > 0) { var current = this._schedulerQueue[0]; if (finalTime < current.endTime) { @@ -419,7 +634,11 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; else { // Time to run scheduled function. Remove it from the head of queue. var current_1 = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; this._currentTime = current_1.endTime; + if (doTick) { + doTick(this._currentTime - lastCurrentTime); + } var retval = current_1.func.apply(global, current_1.args); if (!retval) { // Uncaught exception in the current scheduled function. Stop processing the queue. @@ -429,18 +648,79 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } this._currentTime = finalTime; }; + Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) { + if (limit === void 0) { limit = 20; } + if (flushPeriodic === void 0) { flushPeriodic = false; } + if (flushPeriodic) { + return this.flushPeriodic(doTick); + } + else { + return this.flushNonPeriodic(limit, doTick); + } + }; + Scheduler.prototype.flushPeriodic = function (doTick) { + if (this._schedulerQueue.length === 0) { + return 0; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + var startTime = this._currentTime; + var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; + this.tick(lastTask.endTime - startTime, doTick); + return this._currentTime - startTime; + }; + Scheduler.prototype.flushNonPeriodic = function (limit, doTick) { + var startTime = this._currentTime; + var lastCurrentTime = 0; + var count = 0; + while (this._schedulerQueue.length > 0) { + count++; + if (count > limit) { + throw new Error('flush failed after reaching the limit of ' + limit + + ' tasks. Does your code use a polling timeout?'); + } + // flush only non-periodic timers. + // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. + if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; }) + .length === 0) { + break; + } + var current = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current.endTime; + if (doTick) { + // Update any secondary schedulers like Jasmine mock Date. + doTick(this._currentTime - lastCurrentTime); + } + var retval = current.func.apply(global, current.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + return this._currentTime - startTime; + }; return Scheduler; }()); - var FakeAsyncTestZoneSpec = (function () { - function FakeAsyncTestZoneSpec(namePrefix) { + var FakeAsyncTestZoneSpec = /** @class */ (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this.macroTaskOptions = macroTaskOptions; this._scheduler = new Scheduler(); this._microtasks = []; this._lastError = null; this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; this.pendingPeriodicTimers = []; this.pendingTimers = []; + this.patchDateLocked = false; this.properties = { 'FakeAsyncTestZoneSpec': this }; this.name = 'fakeAsyncTestZone for ' + namePrefix; + // in case user can't access the construction of FakeAsyncTestSpec + // user can also define macroTaskOptions by define a global variable. + if (!this.macroTaskOptions) { + this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')]; + } } FakeAsyncTestZoneSpec.assertInZone = function () { if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { @@ -488,7 +768,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; return function () { // Requeue the timer callback if it's not been canceled. if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { - _this._scheduler.scheduleFunction(fn, interval, args, id); + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); } }; }; @@ -498,30 +778,29 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); }; }; - FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args) { + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { + if (isTimer === void 0) { isTimer = true; } var removeTimerFn = this._dequeueTimer(this._scheduler.nextId); // Queue the callback and dequeue the timer on success and error. var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn }); - var id = this._scheduler.scheduleFunction(cb, delay, args); - this.pendingTimers.push(id); + var id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer); + if (isTimer) { + this.pendingTimers.push(id); + } return id; }; FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id); this._scheduler.removeScheduledFunctionWithId(id); }; - FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } + FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval, args) { var id = this._scheduler.nextId; var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; var cb = this._fnAndFlush(fn, completers); // Use the callback created above to requeue on success. completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); // Queue the callback and dequeue the periodic timer only on error. - this._scheduler.scheduleFunction(cb, interval, args); + this._scheduler.scheduleFunction(cb, interval, args, true); this.pendingPeriodicTimers.push(id); return id; }; @@ -535,11 +814,55 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._lastError = null; throw error; }; - FakeAsyncTestZoneSpec.prototype.tick = function (millis) { + FakeAsyncTestZoneSpec.prototype.getCurrentTime = function () { + return this._scheduler.getCurrentTime(); + }; + FakeAsyncTestZoneSpec.prototype.getCurrentRealTime = function () { + return this._scheduler.getCurrentRealTime(); + }; + FakeAsyncTestZoneSpec.prototype.setCurrentRealTime = function (realTime) { + this._scheduler.setCurrentRealTime(realTime); + }; + FakeAsyncTestZoneSpec.patchDate = function () { + if (global['Date'] === FakeDate) { + // already patched + return; + } + global['Date'] = FakeDate; + FakeDate.prototype = OriginalDate.prototype; + // try check and reset timers + // because jasmine.clock().install() may + // have replaced the global timer + FakeAsyncTestZoneSpec.checkTimerPatch(); + }; + FakeAsyncTestZoneSpec.resetDate = function () { + if (global['Date'] === FakeDate) { + global['Date'] = OriginalDate; + } + }; + FakeAsyncTestZoneSpec.checkTimerPatch = function () { + if (global.setTimeout !== timers.setTimeout) { + global.setTimeout = timers.setTimeout; + global.clearTimeout = timers.clearTimeout; + } + if (global.setInterval !== timers.setInterval) { + global.setInterval = timers.setInterval; + global.clearInterval = timers.clearInterval; + } + }; + FakeAsyncTestZoneSpec.prototype.lockDatePatch = function () { + this.patchDateLocked = true; + FakeAsyncTestZoneSpec.patchDate(); + }; + FakeAsyncTestZoneSpec.prototype.unlockDatePatch = function () { + this.patchDateLocked = false; + FakeAsyncTestZoneSpec.resetDate(); + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { if (millis === void 0) { millis = 0; } FakeAsyncTestZoneSpec.assertInZone(); this.flushMicrotasks(); - this._scheduler.tick(millis); + this._scheduler.tick(millis, doTick); if (this._lastError !== null) { this._resetLastErrorAndThrow(); } @@ -555,29 +878,80 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; }; while (this._microtasks.length > 0) { var microtask = this._microtasks.shift(); - microtask(); + microtask.func.apply(microtask.target, microtask.args); } flushErrors(); }; + FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) { + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + return elapsed; + }; FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { switch (task.type) { case 'microTask': - this._microtasks.push(task.invoke); + var args = task.data && task.data.args; + // should pass additional arguments to callback if have any + // currently we know process.nextTick will have such additional + // arguments + var additionalArgs = void 0; + if (args) { + var callbackIndex = task.data.cbIdx; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + additionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: additionalArgs, + target: task.data && task.data.target + }); break; case 'macroTask': switch (task.source) { case 'setTimeout': - task.data['handleId'] = - this._setTimeout(task.invoke, task.data['delay'], task.data['args']); + task.data['handleId'] = this._setTimeout(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + break; + case 'setImmediate': + task.data['handleId'] = this._setTimeout(task.invoke, 0, Array.prototype.slice.call(task.data['args'], 1)); break; case 'setInterval': - task.data['handleId'] = - this._setInterval(task.invoke, task.data['delay'], task.data['args']); + task.data['handleId'] = this._setInterval(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); break; case 'XMLHttpRequest.send': - throw new Error('Cannot make XHRs from within a fake async test.'); + throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' + + task.data['url']); + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + // Simulate a requestAnimationFrame by using a setTimeout with 16 ms. + // (60 frames per second) + task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame); + break; default: - task = delegate.scheduleTask(target, task); + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var args_1 = task.data && task.data['args']; + var delay = args_1 && args_1.length > 1 ? args_1[1] : 0; + var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1; + if (!!macroTaskOption.isPeriodic) { + // periodic macroTask, use setInterval to simulate + task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs); + task.data.isPeriodic = true; + } + else { + // not periodic, use setTimeout to simulate + task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); + } + break; + } + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); } break; case 'eventTask': @@ -589,13 +963,47 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { switch (task.source) { case 'setTimeout': + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': return this._clearTimeout(task.data['handleId']); case 'setInterval': return this._clearInterval(task.data['handleId']); default: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var handleId = task.data['handleId']; + return macroTaskOption.isPeriodic ? this._clearInterval(handleId) : + this._clearTimeout(handleId); + } return delegate.cancelTask(target, task); } }; + FakeAsyncTestZoneSpec.prototype.onInvoke = function (delegate, current, target, callback, applyThis, applyArgs, source) { + try { + FakeAsyncTestZoneSpec.patchDate(); + return delegate.invoke(target, callback, applyThis, applyArgs, source); + } + finally { + if (!this.patchDateLocked) { + FakeAsyncTestZoneSpec.resetDate(); + } + } + }; + FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) { + if (!this.macroTaskOptions) { + return null; + } + for (var i = 0; i < this.macroTaskOptions.length; i++) { + var macroTaskOption = this.macroTaskOptions[i]; + if (macroTaskOption.source === task.source) { + return macroTaskOption; + } + } + return null; + }; FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { this._lastError = error; return false; // Don't propagate error to parent zone. @@ -620,7 +1028,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * This is useful in tests. For example to see which tasks are preventing a test from completing * or an automated way of releasing all of the event listeners at the end of the test. */ -var TaskTrackingZoneSpec = (function () { +var TaskTrackingZoneSpec = /** @class */ (function () { function TaskTrackingZoneSpec() { this.name = 'TaskTrackingZone'; this.microTasks = []; @@ -688,6 +1096,10 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +/** + * @fileoverview + * @suppress {missingRequire} + */ (function (global) { // Detect and setup WTF. var wtfTrace = null; @@ -703,7 +1115,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } return false; })(); - var WtfZoneSpec = (function () { + var WtfZoneSpec = /** @class */ (function () { function WtfZoneSpec() { this.name = 'WTF'; } @@ -754,14 +1166,13 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; instance(zonePathName(targetZone), shallowObj(task.data, 2)); return retValue; }; - + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; return WtfZoneSpec; }()); - WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); - WtfZoneSpec.scheduleInstance = {}; - WtfZoneSpec.cancelInstance = {}; - WtfZoneSpec.invokeScope = {}; - WtfZoneSpec.invokeTaskScope = {}; function shallowObj(obj, depth) { if (!depth) return null; @@ -802,7 +1213,6 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -'use strict'; (function () { var __extends = function (d, b) { for (var p in b) @@ -813,6 +1223,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; + var _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503) if (!Zone) @@ -820,7 +1231,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; if (typeof jasmine == 'undefined') throw new Error('Missing: jasmine.js'); if (jasmine['__zone_patch__']) - throw new Error('\'jasmine\' has already been patched with \'Zone\'.'); + throw new Error("'jasmine' has already been patched with 'Zone'."); jasmine['__zone_patch__'] = true; var SyncTestZoneSpec = Zone['SyncTestZoneSpec']; var ProxyZoneSpec = Zone['ProxyZoneSpec']; @@ -833,16 +1244,9 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; // error if any asynchronous operations are attempted inside of a `describe` but outside of // a `beforeEach` or `it`. var syncZone = ambientZone.fork(new SyncTestZoneSpec('jasmine.describe')); - // This is the zone which will be used for running individual tests. - // It will be a proxy zone, so that the tests function can retroactively install - // different zones. - // Example: - // - In beforeEach() do childZone = Zone.current.fork(...); - // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the - // zone outside of fakeAsync it will be able to escope the fakeAsync rules. - // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add - // fakeAsync behavior to the childZone. - var testProxyZone = null; + var symbol = Zone.__symbol__; + // whether patch jasmine clock when in fakeAsync + var enableClockPatch = _global[symbol('fakeAsyncPatchLock')] === true; // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone. var jasmineEnv = jasmine.getEnv(); ['describe', 'xdescribe', 'fdescribe'].forEach(function (methodName) { @@ -853,6 +1257,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; }); ['it', 'xit', 'fit'].forEach(function (methodName) { var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[symbol(methodName)] = originalJasmineFn; jasmineEnv[methodName] = function (description, specDefinitions, timeout) { arguments[1] = wrapTestInZone(specDefinitions); return originalJasmineFn.apply(this, arguments); @@ -860,11 +1265,54 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; }); ['beforeEach', 'afterEach'].forEach(function (methodName) { var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[symbol(methodName)] = originalJasmineFn; jasmineEnv[methodName] = function (specDefinitions, timeout) { arguments[0] = wrapTestInZone(specDefinitions); return originalJasmineFn.apply(this, arguments); }; }); + // need to patch jasmine.clock().mockDate and jasmine.clock().tick() so + // they can work properly in FakeAsyncTest + var originalClockFn = (jasmine[symbol('clock')] = jasmine['clock']); + jasmine['clock'] = function () { + var clock = originalClockFn.apply(this, arguments); + if (!clock[symbol('patched')]) { + clock[symbol('patched')] = symbol('patched'); + var originalTick_1 = (clock[symbol('tick')] = clock.tick); + clock.tick = function () { + var fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments); + } + return originalTick_1.apply(this, arguments); + }; + var originalMockDate_1 = (clock[symbol('mockDate')] = clock.mockDate); + clock.mockDate = function () { + var fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + var dateTime = arguments.length > 0 ? arguments[0] : new Date(); + return fakeAsyncZoneSpec.setCurrentRealTime.apply(fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] : + arguments); + } + return originalMockDate_1.apply(this, arguments); + }; + // for auto go into fakeAsync feature, we need the flag to enable it + if (enableClockPatch) { + ['install', 'uninstall'].forEach(function (methodName) { + var originalClockFn = (clock[symbol(methodName)] = clock[methodName]); + clock[methodName] = function () { + var FakeAsyncTestZoneSpec = Zone['FakeAsyncTestZoneSpec']; + if (FakeAsyncTestZoneSpec) { + jasmine[symbol('clockInstalled')] = 'install' === methodName; + return; + } + return originalClockFn.apply(this, arguments); + }; + }); + } + } + return clock; + }; /** * Gets a function wrapping the body of a Jasmine `describe` block to execute in a * synchronous-only zone. @@ -874,6 +1322,24 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; return syncZone.run(describeBody, this, arguments); }; } + function runInTestZone(testBody, applyThis, queueRunner, done) { + var isClockInstalled = !!jasmine[symbol('clockInstalled')]; + var testProxyZoneSpec = queueRunner.testProxyZoneSpec; + var testProxyZone = queueRunner.testProxyZone; + if (isClockInstalled && enableClockPatch) { + // auto run a fakeAsync + var fakeAsyncModule = Zone[Zone.__symbol__('fakeAsyncTest')]; + if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') { + testBody = fakeAsyncModule.fakeAsync(testBody); + } + } + if (done) { + return testProxyZone.run(testBody, applyThis, [done]); + } + else { + return testProxyZone.run(testBody, applyThis); + } + } /** * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to * execute in a ProxyZone zone. @@ -883,28 +1349,90 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; // The `done` callback is only passed through if the function expects at least one argument. // Note we have to make a function with correct number of arguments, otherwise jasmine will // think that all functions are sync or async. - return testBody && (testBody.length ? function (done) { - return testProxyZone.run(testBody, this, [done]); + return (testBody && (testBody.length ? function (done) { + return runInTestZone(testBody, this, this.queueRunner, done); } : function () { - return testProxyZone.run(testBody, this); - }); + return runInTestZone(testBody, this, this.queueRunner); + })); } var QueueRunner = jasmine.QueueRunner; jasmine.QueueRunner = (function (_super) { __extends(ZoneQueueRunner, _super); function ZoneQueueRunner(attrs) { + var _this = this; attrs.onComplete = (function (fn) { return function () { // All functions are done, clear the test zone. - testProxyZone = null; + _this.testProxyZone = null; + _this.testProxyZoneSpec = null; ambientZone.scheduleMicroTask('jasmine.onComplete', fn); }; })(attrs.onComplete); + var nativeSetTimeout = _global['__zone_symbol__setTimeout']; + var nativeClearTimeout = _global['__zone_symbol__clearTimeout']; + if (nativeSetTimeout) { + // should run setTimeout inside jasmine outside of zone + attrs.timeout = { + setTimeout: nativeSetTimeout ? nativeSetTimeout : _global.setTimeout, + clearTimeout: nativeClearTimeout ? nativeClearTimeout : _global.clearTimeout + }; + } + // create a userContext to hold the queueRunner itself + // so we can access the testProxy in it/xit/beforeEach ... + if (jasmine.UserContext) { + if (!attrs.userContext) { + attrs.userContext = new jasmine.UserContext(); + } + attrs.userContext.queueRunner = this; + } + else { + if (!attrs.userContext) { + attrs.userContext = {}; + } + attrs.userContext.queueRunner = this; + } + // patch attrs.onException + var onException = attrs.onException; + attrs.onException = function (error) { + if (error && + error.message === + 'Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.') { + // jasmine timeout, we can make the error message more + // reasonable to tell what tasks are pending + var proxyZoneSpec = this && this.testProxyZoneSpec; + if (proxyZoneSpec) { + var pendingTasksInfo = proxyZoneSpec.getAndClearPendingTasksInfo(); + error.message += pendingTasksInfo; + } + } + if (onException) { + onException.call(this, error); + } + }; _super.call(this, attrs); } ZoneQueueRunner.prototype.execute = function () { var _this = this; - if (Zone.current !== ambientZone) + var zone = Zone.current; + var isChildOfAmbientZone = false; + while (zone) { + if (zone === ambientZone) { + isChildOfAmbientZone = true; + break; + } + zone = zone.parent; + } + if (!isChildOfAmbientZone) throw new Error('Unexpected Zone: ' + Zone.current.name); - testProxyZone = ambientZone.fork(new ProxyZoneSpec()); + // This is the zone which will be used for running individual tests. + // It will be a proxy zone, so that the tests function can retroactively install + // different zones. + // Example: + // - In beforeEach() do childZone = Zone.current.fork(...); + // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the + // zone outside of fakeAsync it will be able to escape the fakeAsync rules. + // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add + // fakeAsync behavior to the childZone. + this.testProxyZoneSpec = new ProxyZoneSpec(); + this.testProxyZone = ambientZone.fork(this.testProxyZoneSpec); if (!Zone.currentTask) { // if we are not running in a task then if someone would register a // element.addEventListener and then calling element.click() the @@ -918,7 +1446,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } }; return ZoneQueueRunner; - }(QueueRunner)); + })(QueueRunner); })(); }))); diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.js b/nativescript-angular/zone-js/dist/zone-nativescript.js index 38616df9f..7eb6799a8 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.js @@ -19,13 +19,19 @@ * found in the LICENSE file at https://angular.io/license */ var Zone$1 = (function (global) { + var FUNCTION = 'function'; + var performance = global['performance']; + function mark(name) { + performance && performance['mark'] && performance['mark'](name); + } + function performanceMeasure(name, label) { + performance && performance['measure'] && performance['measure'](name, label); + } + mark('Zone'); if (global['Zone']) { throw new Error('Zone already loaded.'); } - var NO_ZONE = { name: 'NO ZONE' }; - var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; - var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; - var Zone = (function () { + var Zone = /** @class */ (function () { function Zone(parent, zoneSpec) { this._properties = null; this._parent = parent; @@ -35,7 +41,7 @@ var Zone$1 = (function (global) { new ZoneDelegate(this, this._parent && this._parent._zoneDelegate, zoneSpec); } Zone.assertZonePatched = function () { - if (global.Promise !== ZoneAwarePromise) { + if (global['Promise'] !== patches['ZoneAwarePromise']) { throw new Error('Zone.js has detected that ZoneAwarePromise `(window|global).Promise` ' + 'has been overwritten.\n' + 'Most likely cause is that a Promise polyfill has been loaded ' + @@ -61,7 +67,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Object.defineProperty(Zone, "currentTask", { get: function () { return _currentTask; @@ -69,7 +74,17 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - + Zone.__load_patch = function (name, fn) { + if (patches.hasOwnProperty(name)) { + throw Error('Already loaded patch: ' + name); + } + else if (!global['__Zone_disable_' + name]) { + var perfName = 'Zone:' + name; + mark(perfName); + patches[name] = fn(global, Zone, _api); + performanceMeasure(perfName, perfName); + } + }; Object.defineProperty(Zone.prototype, "parent", { get: function () { return this._parent; @@ -77,7 +92,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Object.defineProperty(Zone.prototype, "name", { get: function () { return this._name; @@ -85,7 +99,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Zone.prototype.get = function (key) { var zone = this.getZoneWith(key); if (zone) @@ -107,7 +120,7 @@ var Zone$1 = (function (global) { return this._zoneDelegate.fork(this, zoneSpec); }; Zone.prototype.wrap = function (callback, source) { - if (typeof callback !== 'function') { + if (typeof callback !== FUNCTION) { throw new Error('Expecting function got: ' + callback); } var _callback = this._zoneDelegate.intercept(this, callback, source); @@ -120,7 +133,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = undefined; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); } @@ -132,7 +145,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = null; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); @@ -148,15 +161,25 @@ var Zone$1 = (function (global) { } }; Zone.prototype.runTask = function (task, applyThis, applyArgs) { - if (task.zone != this) + if (task.zone != this) { throw new Error('A task can only be run in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); + } + // https://github.com/angular/zone.js/issues/778, sometimes eventTask + // will run in notScheduled(canceled) state, we should not try to + // run such kind of task but just return + // we have to define an variable here, if not + // typescript compiler will complain below + var isNotScheduled = task.state === notScheduled; + if (isNotScheduled && task.type === eventTask) { + return; + } var reEntryGuard = task.state != running; reEntryGuard && task._transitionTo(running, scheduled); task.runCount++; var previousTask = _currentTask; _currentTask = task; - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { if (task.type == macroTask && task.data && !task.data.isPeriodic) { task.cancelFn = null; @@ -262,9 +285,9 @@ var Zone$1 = (function (global) { zoneDelegates[i]._updateTaskCount(task.type, count); } }; + Zone.__symbol__ = __symbol__; return Zone; }()); - Zone.__symbol__ = __symbol__; var DELEGATE_ZS = { name: '', onHasTask: function (delegate, _, target, hasTaskState) { @@ -278,7 +301,7 @@ var Zone$1 = (function (global) { return delegate.cancelTask(target, task); } }; - var ZoneDelegate = (function () { + var ZoneDelegate = /** @class */ (function () { function ZoneDelegate(zone, parentDelegate, zoneSpec) { this._taskCounts = { 'microTask': 0, 'macroTask': 0, 'eventTask': 0 }; this.zone = zone; @@ -418,6 +441,7 @@ var Zone$1 = (function (global) { this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty); } catch (err) { + this.handleError(targetZone, err); } }; ZoneDelegate.prototype._updateTaskCount = function (type, count) { @@ -429,18 +453,17 @@ var Zone$1 = (function (global) { } if (prev == 0 || next == 0) { var isEmpty = { - microTask: counts.microTask > 0, - macroTask: counts.macroTask > 0, - eventTask: counts.eventTask > 0, + microTask: counts['microTask'] > 0, + macroTask: counts['macroTask'] > 0, + eventTask: counts['eventTask'] > 0, change: type }; - // TODO(misko): what should happen if it throws? this.hasTask(this.zone, isEmpty); } }; return ZoneDelegate; }()); - var ZoneTask = (function () { + var ZoneTask = /** @class */ (function () { function ZoneTask(type, source, callback, options, scheduleFn, cancelFn) { this._zone = null; this.runCount = 0; @@ -453,20 +476,32 @@ var Zone$1 = (function (global) { this.cancelFn = cancelFn; this.callback = callback; var self = this; - this.invoke = function () { - _numberOfNestedTaskFrames++; - try { - self.runCount++; - return self.zone.runTask(self, this, arguments); - } - finally { - if (_numberOfNestedTaskFrames == 1) { - drainMicroTaskQueue(); - } - _numberOfNestedTaskFrames--; - } - }; + // TODO: @JiaLiPassion options should have interface + if (type === eventTask && options && options.useG) { + this.invoke = ZoneTask.invokeTask; + } + else { + this.invoke = function () { + return ZoneTask.invokeTask.call(global, self, this, arguments); + }; + } } + ZoneTask.invokeTask = function (task, target, args) { + if (!task) { + task = this; + } + _numberOfNestedTaskFrames++; + try { + task.runCount++; + return task.zone.runTask(task, target, args); + } + finally { + if (_numberOfNestedTaskFrames == 1) { + drainMicroTaskQueue(); + } + _numberOfNestedTaskFrames--; + } + }; Object.defineProperty(ZoneTask.prototype, "zone", { get: function () { return this._zone; @@ -512,74 +547,41 @@ var Zone$1 = (function (global) { type: this.type, state: this.state, source: this.source, - data: this.data, zone: this.zone.name, - invoke: this.invoke, - scheduleFn: this.scheduleFn, - cancelFn: this.cancelFn, - runCount: this.runCount, - callback: this.callback + runCount: this.runCount }; }; return ZoneTask; }()); - var ZoneFrame = (function () { - function ZoneFrame(parent, zone) { - this.parent = parent; - this.zone = zone; - } - return ZoneFrame; - }()); - function __symbol__(name) { - return '__zone_symbol__' + name; - } - + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// MICROTASK QUEUE + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// var symbolSetTimeout = __symbol__('setTimeout'); var symbolPromise = __symbol__('Promise'); var symbolThen = __symbol__('then'); - var _currentZoneFrame = new ZoneFrame(null, new Zone(null, null)); - var _currentTask = null; var _microTaskQueue = []; var _isDrainingMicrotaskQueue = false; - var _uncaughtPromiseErrors = []; - var _numberOfNestedTaskFrames = 0; - function scheduleQueueDrain() { + var nativeMicroTaskQueuePromise; + function scheduleMicroTask(task) { // if we are not running in any task, and there has not been anything scheduled // we must bootstrap the initial task creation by manually scheduling the drain if (_numberOfNestedTaskFrames === 0 && _microTaskQueue.length === 0) { // We are not running in Task, so we need to kickstart the microtask queue. - if (global[symbolPromise]) { - global[symbolPromise].resolve(0)[symbolThen](drainMicroTaskQueue); + if (!nativeMicroTaskQueuePromise) { + if (global[symbolPromise]) { + nativeMicroTaskQueuePromise = global[symbolPromise].resolve(0); + } + } + if (nativeMicroTaskQueuePromise) { + nativeMicroTaskQueuePromise[symbolThen](drainMicroTaskQueue); } else { global[symbolSetTimeout](drainMicroTaskQueue, 0); } } - } - function scheduleMicroTask(task) { - scheduleQueueDrain(); - _microTaskQueue.push(task); - } - function consoleError(e) { - if (Zone[__symbol__('ignoreConsoleErrorUncaughtError')]) { - return; - } - var rejection = e && e.rejection; - if (rejection) { - console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); - } - console.error(e); - } - function handleUnhandledRejection(e) { - consoleError(e); - try { - var handler = Zone[__symbol__('unhandledPromiseRejectionHandler')]; - if (handler && typeof handler === 'function') { - handler.apply(this, [e]); - } - } - catch (err) { - } + task && _microTaskQueue.push(task); } function drainMicroTaskQueue() { if (!_isDrainingMicrotaskQueue) { @@ -593,30 +595,121 @@ var Zone$1 = (function (global) { task.zone.runTask(task, null, null); } catch (error) { - consoleError(error); + _api.onUnhandledError(error); } } } - while (_uncaughtPromiseErrors.length) { - var _loop_1 = function () { - var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); - try { - uncaughtPromiseError.zone.runGuarded(function () { - throw uncaughtPromiseError; - }); - } - catch (error) { - handleUnhandledRejection(error); - } - }; - while (_uncaughtPromiseErrors.length) { - _loop_1(); - } - } + _api.microtaskDrainDone(); _isDrainingMicrotaskQueue = false; } } Zone.drainMicroTaskQueue = drainMicroTaskQueue; + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// BOOTSTRAP + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + var NO_ZONE = { name: 'NO ZONE' }; + var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; + var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; + var patches = {}; + var _api = { + symbol: __symbol__, + currentZoneFrame: function () { return _currentZoneFrame; }, + onUnhandledError: noop, + microtaskDrainDone: noop, + scheduleMicroTask: scheduleMicroTask, + showUncaughtError: function () { return !Zone[__symbol__('ignoreConsoleErrorUncaughtError')]; }, + patchEventTarget: function () { return []; }, + patchOnProperties: noop, + patchMethod: function () { return noop; }, + bindArguments: function () { return null; }, + setNativePromise: function (NativePromise) { + // sometimes NativePromise.resolve static function + // is not ready yet, (such as core-js/es6.promise) + // so we need to check here. + if (NativePromise && typeof NativePromise.resolve === FUNCTION) { + nativeMicroTaskQueuePromise = NativePromise.resolve(0); + } + }, + }; + var _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + var _currentTask = null; + var _numberOfNestedTaskFrames = 0; + function noop() { } + function __symbol__(name) { + return '__zone_symbol__' + name; + } + performanceMeasure('Zone', 'Zone'); + return global['Zone'] = Zone; +})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); + +var __values = (undefined && undefined.__values) || function (o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +}; +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + var ObjectDefineProperty = Object.defineProperty; + function readableObjectToString(obj) { + if (obj && obj.toString === Object.prototype.toString) { + var className = obj.constructor && obj.constructor.name; + return (className ? className : '') + ': ' + JSON.stringify(obj); + } + return obj ? obj.toString() : Object.prototype.toString.call(obj); + } + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + var creationTrace = '__creationTrace__'; + api.onUnhandledError = function (e) { + if (api.showUncaughtError()) { + var rejection = e && e.rejection; + if (rejection) { + console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); + } + else { + console.error(e); + } + } + }; + api.microtaskDrainDone = function () { + while (_uncaughtPromiseErrors.length) { + var _loop_1 = function () { + var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); + try { + uncaughtPromiseError.zone.runGuarded(function () { + throw uncaughtPromiseError; + }); + } + catch (error) { + handleUnhandledRejection(error); + } + }; + while (_uncaughtPromiseErrors.length) { + _loop_1(); + } + } + }; + var UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); + function handleUnhandledRejection(e) { + api.onUnhandledError(e); + try { + var handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; + if (handler && typeof handler === 'function') { + handler.call(this, e); + } + } + catch (err) { + } + } function isThenable(value) { return value && value.then; } @@ -628,6 +721,9 @@ var Zone$1 = (function (global) { } var symbolState = __symbol__('state'); var symbolValue = __symbol__('value'); + var symbolFinally = __symbol__('finally'); + var symbolParentPromiseValue = __symbol__('parentPromiseValue'); + var symbolParentPromiseState = __symbol__('parentPromiseState'); var source = 'Promise.then'; var UNRESOLVED = null; var RESOLVED = true; @@ -656,11 +752,13 @@ var Zone$1 = (function (global) { }; }; }; + var TYPE_ERROR = 'Promise resolved with itself'; + var CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace'); // Promise Resolution function resolvePromise(promise, state, value) { var onceWrapper = once(); if (promise === value) { - throw new TypeError('Promise resolved with itself'); + throw new TypeError(TYPE_ERROR); } if (promise[symbolState] === UNRESOLVED) { // should only get value.then once based on promise spec. @@ -685,9 +783,7 @@ var Zone$1 = (function (global) { } else if (state !== REJECTED && typeof then === 'function') { try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); + then.call(value, onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false))); } catch (err) { onceWrapper(function () { @@ -699,10 +795,25 @@ var Zone$1 = (function (global) { promise[symbolState] = state; var queue = promise[symbolValue]; promise[symbolValue] = value; + if (promise[symbolFinally] === symbolFinally) { + // the promise is generated by Promise.prototype.finally + if (state === RESOLVED) { + // the state is resolved, should ignore the value + // and use parent promise value + promise[symbolState] = promise[symbolParentPromiseState]; + promise[symbolValue] = promise[symbolParentPromiseValue]; + } + } // record task information in value when error occurs, so we can // do some additional work such as render longStackTrace if (state === REJECTED && value instanceof Error) { - value[__symbol__('currentTask')] = Zone.currentTask; + // check if longStackTraceZone is here + var trace = Zone.currentTask && Zone.currentTask.data && + Zone.currentTask.data[creationTrace]; + if (trace) { + // only keep the long stack trace into error when in longStackTraceZone + ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { configurable: true, enumerable: false, writable: true, value: trace }); + } } for (var i = 0; i < queue.length;) { scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); @@ -710,7 +821,8 @@ var Zone$1 = (function (global) { if (queue.length == 0 && state == REJECTED) { promise[symbolState] = REJECTED_NO_CATCH; try { - throw new Error('Uncaught (in promise): ' + value + + // try to print more readable error log + throw new Error('Uncaught (in promise): ' + readableObjectToString(value) + (value && value.stack ? '\n' + value.stack : '')); } catch (err) { @@ -720,7 +832,7 @@ var Zone$1 = (function (global) { error_1.zone = Zone.current; error_1.task = Zone.currentTask; _uncaughtPromiseErrors.push(error_1); - scheduleQueueDrain(); + api.scheduleMicroTask(); // to make sure that it is running } } } @@ -728,6 +840,7 @@ var Zone$1 = (function (global) { // Resolving an already resolved promise is a noop. return promise; } + var REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); function clearRejectedNoCatch(promise) { if (promise[symbolState] === REJECTED_NO_CATCH) { // if the promise is rejected no catch status @@ -736,9 +849,9 @@ var Zone$1 = (function (global) { // windows.rejectionhandled eventHandler or nodejs rejectionHandled // eventHandler try { - var handler = Zone[__symbol__('rejectionHandledHandler')]; + var handler = Zone[REJECTION_HANDLED_HANDLER]; if (handler && typeof handler === 'function') { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + handler.call(this, { rejection: promise[symbolValue], promise: promise }); } } catch (err) { @@ -753,19 +866,31 @@ var Zone$1 = (function (global) { } function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? + var promiseState = promise[symbolState]; + var delegate = promiseState ? (typeof onFulfilled === 'function') ? onFulfilled : forwardResolution : (typeof onRejected === 'function') ? onRejected : forwardRejection; zone.scheduleMicroTask(source, function () { try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + var parentPromiseValue = promise[symbolValue]; + var isFinallyPromise = chainPromise && symbolFinally === chainPromise[symbolFinally]; + if (isFinallyPromise) { + // if the promise is generated from finally call, keep parent promise's state and value + chainPromise[symbolParentPromiseValue] = parentPromiseValue; + chainPromise[symbolParentPromiseState] = promiseState; + } + // should not pass value to finally callback + var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]); + resolvePromise(chainPromise, true, value); } catch (error) { + // if error occurs, should always return this error resolvePromise(chainPromise, false, error); } - }); + }, chainPromise); } - var ZoneAwarePromise = (function () { + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = /** @class */ (function () { function ZoneAwarePromise(executor) { var promise = this; if (!(promise instanceof ZoneAwarePromise)) { @@ -781,7 +906,7 @@ var Zone$1 = (function (global) { } } ZoneAwarePromise.toString = function () { - return 'function ZoneAwarePromise() { [native code] }'; + return ZONE_AWARE_PROMISE_TO_STRING; }; ZoneAwarePromise.resolve = function (value) { return resolvePromise(new this(null), RESOLVED, value); @@ -793,8 +918,8 @@ var Zone$1 = (function (global) { var resolve; var reject; var promise = new this(function (res, rej) { - _a = [res, rej], resolve = _a[0], reject = _a[1]; - var _a; + resolve = res; + reject = rej; }); function onResolve(value) { promise && (promise = null || resolve(value)); @@ -802,14 +927,24 @@ var Zone$1 = (function (global) { function onReject(error) { promise && (promise = null || reject(error)); } - for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { - var value = values_1[_i]; - if (!isThenable(value)) { - value = this.resolve(value); + try { + for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { + var value = values_1_1.value; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1); } - value.then(onResolve, onReject); + finally { if (e_1) throw e_1.error; } } return promise; + var e_1, _a; }; ZoneAwarePromise.all = function (values) { var resolve; @@ -820,23 +955,33 @@ var Zone$1 = (function (global) { }); var count = 0; var resolvedValues = []; - for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { - var value = values_2[_i]; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then((function (index) { return function (value) { - resolvedValues[index] = value; - count--; - if (!count) { - resolve(resolvedValues); + try { + for (var values_2 = __values(values), values_2_1 = values_2.next(); !values_2_1.done; values_2_1 = values_2.next()) { + var value = values_2_1.value; + if (!isThenable(value)) { + value = this.resolve(value); } - }; })(count), reject); - count++; + value.then((function (index) { return function (value) { + resolvedValues[index] = value; + count--; + if (!count) { + resolve(resolvedValues); + } + }; })(count), reject); + count++; + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (values_2_1 && !values_2_1.done && (_a = values_2.return)) _a.call(values_2); + } + finally { if (e_2) throw e_2.error; } } if (!count) resolve(resolvedValues); return promise; + var e_2, _a; }; ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { var chainPromise = new this.constructor(null); @@ -852,6 +997,18 @@ var Zone$1 = (function (global) { ZoneAwarePromise.prototype.catch = function (onRejected) { return this.then(null, onRejected); }; + ZoneAwarePromise.prototype.finally = function (onFinally) { + var chainPromise = new this.constructor(null); + chainPromise[symbolFinally] = symbolFinally; + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFinally, onFinally); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally); + } + return chainPromise; + }; return ZoneAwarePromise; }()); // Protect against aggressive optimizers dropping seemingly unused properties. @@ -861,10 +1018,52 @@ var Zone$1 = (function (global) { ZoneAwarePromise['race'] = ZoneAwarePromise.race; ZoneAwarePromise['all'] = ZoneAwarePromise.all; var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + var desc = ObjectGetOwnPropertyDescriptor(global, 'Promise'); + if (!desc || desc.configurable) { + desc && delete desc.writable; + desc && delete desc.value; + if (!desc) { + desc = { configurable: true, enumerable: true }; + } + desc.get = function () { + // if we already set ZoneAwarePromise, use patched one + // otherwise return native one. + return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; + }; + desc.set = function (NewNativePromise) { + if (NewNativePromise === ZoneAwarePromise) { + // if the NewNativePromise is ZoneAwarePromise + // save to global + global[ZONE_AWARE_PROMISE] = NewNativePromise; + } + else { + // if the NewNativePromise is not ZoneAwarePromise + // for example: after load zone.js, some library just + // set es6-promise to global, if we set it to global + // directly, assertZonePatched will fail and angular + // will not loaded, so we just set the NewNativePromise + // to global[symbolPromise], so the result is just like + // we load ES6 Promise before zone.js + global[symbolPromise] = NewNativePromise; + if (!NewNativePromise.prototype[symbolThen]) { + patchThen(NewNativePromise); + } + api.setNativePromise(NewNativePromise); + } + }; + ObjectDefineProperty(global, 'Promise', desc); + } global['Promise'] = ZoneAwarePromise; var symbolThenPatched = __symbol__('thenPatched'); function patchThen(Ctor) { var proto = Ctor.prototype; + var prop = ObjectGetOwnPropertyDescriptor(proto, 'then'); + if (prop && (prop.writable === false || !prop.configurable)) { + // check Ctor.prototype.then propertyDescriptor is writable or not + // in meteor env, writable is false, we should ignore such case + return; + } var originalThen = proto.then; // Keep a reference to the original method. proto[symbolThen] = originalThen; @@ -883,9 +1082,9 @@ var Zone$1 = (function (global) { if (resultPromise instanceof ZoneAwarePromise) { return resultPromise; } - var Ctor = resultPromise.constructor; - if (!Ctor[symbolThenPatched]) { - patchThen(Ctor); + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); } return resultPromise; }; @@ -897,120 +1096,269 @@ var Zone$1 = (function (global) { global['fetch'] = zoneify(fetch_1); } } - // This is not part of public API, but it is usefull for tests, so we expose it. + // This is not part of public API, but it is useful for tests, so we expose it. Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; + return ZoneAwarePromise; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * Suppress closure compiler errors about unknown 'Zone' variable + * @fileoverview + * @suppress {undefinedVars,globalThis,missingRequire} + */ +// issue #989, to reduce bundle size, use short name +/** Object.getOwnPropertyDescriptor */ +var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +/** Object.defineProperty */ + +/** Object.getPrototypeOf */ +var ObjectGetPrototypeOf = Object.getPrototypeOf; +/** Object.create */ + +/** Array.prototype.slice */ + +/** addEventListener string const */ +var ADD_EVENT_LISTENER_STR = 'addEventListener'; +/** removeEventListener string const */ +var REMOVE_EVENT_LISTENER_STR = 'removeEventListener'; +/** zoneSymbol addEventListener */ +var ZONE_SYMBOL_ADD_EVENT_LISTENER = Zone.__symbol__(ADD_EVENT_LISTENER_STR); +/** zoneSymbol removeEventListener */ +var ZONE_SYMBOL_REMOVE_EVENT_LISTENER = Zone.__symbol__(REMOVE_EVENT_LISTENER_STR); +/** true string const */ + +/** false string const */ + +/** __zone_symbol__ string const */ + + +function scheduleMacroTaskWithCurrentZone(source, callback, data, customSchedule, customCancel) { + return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel); +} +var zoneSymbol = Zone.__symbol__; +var isWindowExists = typeof window !== 'undefined'; +var internalWindow = isWindowExists ? window : undefined; +var _global = isWindowExists && internalWindow || typeof self === 'object' && self || global; + + +function isPropertyWritable(propertyDesc) { + if (!propertyDesc) { + return true; + } + if (propertyDesc.writable === false) { + return false; + } + return !(typeof propertyDesc.get === 'function' && typeof propertyDesc.set === 'undefined'); +} +var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); +// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify +// this code. +var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]'); +var isBrowser = !isNode && !isWebWorker && !!(isWindowExists && internalWindow['HTMLElement']); +// we are in electron of nw, so we are both browser and nodejs +// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify +// this code. +var isMix = typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]' && !isWebWorker && + !!(isWindowExists && internalWindow['HTMLElement']); + + +var originalInstanceKey = zoneSymbol('originalInstance'); +// wrap some native API on `window` + +function patchMethod(target, name, patchFn) { + var proto = target; + while (proto && !proto.hasOwnProperty(name)) { + proto = ObjectGetPrototypeOf(proto); + } + if (!proto && target[name]) { + // somehow we did not find it, but we can see it. This happens on IE for Window properties. + proto = target; + } + var delegateName = zoneSymbol(name); + var delegate; + if (proto && !(delegate = proto[delegateName])) { + delegate = proto[delegateName] = proto[name]; + // check whether proto[name] is writable + // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob + var desc = proto && ObjectGetOwnPropertyDescriptor(proto, name); + if (isPropertyWritable(desc)) { + var patchDelegate_1 = patchFn(delegate, delegateName, name); + proto[name] = function () { + return patchDelegate_1(this, arguments); + }; + attachOriginToPatched(proto[name], delegate); + } + } + return delegate; +} +// TODO: @JiaLiPassion, support cancel task later if necessary + + +function attachOriginToPatched(patched, original) { + patched[zoneSymbol('OriginalDelegate')] = original; +} + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +// override Function.prototype.toString to make zone.js patched function +// look like native function +Zone.__load_patch('toString', function (global) { + // patch Func.prototype.toString to let them look like native + var originalFunctionToString = Function.prototype.toString; + var ORIGINAL_DELEGATE_SYMBOL = zoneSymbol('OriginalDelegate'); + var PROMISE_SYMBOL = zoneSymbol('Promise'); + var ERROR_SYMBOL = zoneSymbol('Error'); + var newFunctionToString = function toString() { + if (typeof this === 'function') { + var originalDelegate = this[ORIGINAL_DELEGATE_SYMBOL]; + if (originalDelegate) { + if (typeof originalDelegate === 'function') { + return originalFunctionToString.apply(this[ORIGINAL_DELEGATE_SYMBOL], arguments); + } + else { + return Object.prototype.toString.call(originalDelegate); + } + } + if (this === Promise) { + var nativePromise = global[PROMISE_SYMBOL]; + if (nativePromise) { + return originalFunctionToString.apply(nativePromise, arguments); + } + } + if (this === Error) { + var nativeError = global[ERROR_SYMBOL]; + if (nativeError) { + return originalFunctionToString.apply(nativeError, arguments); + } + } + } + return originalFunctionToString.apply(this, arguments); + }; + newFunctionToString[ORIGINAL_DELEGATE_SYMBOL] = originalFunctionToString; + Function.prototype.toString = newFunctionToString; + // patch Object.prototype.toString to let them look like native + var originalObjectToString = Object.prototype.toString; + var PROMISE_OBJECT_TO_STRING = '[object Promise]'; + Object.prototype.toString = function () { + if (this instanceof Promise) { + return PROMISE_OBJECT_TO_STRING; + } + return originalObjectToString.apply(this, arguments); + }; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {globalThis,undefinedVars} + */ +Zone.__load_patch('Error', function (global, Zone, api) { /* * This code patches Error so that: * - It ignores un-needed stack frames. * - It Shows the associated Zone for reach frame. */ - var FrameType; - (function (FrameType) { - /// Skip this frame when printing out stack - FrameType[FrameType["blackList"] = 0] = "blackList"; - /// This frame marks zone transition - FrameType[FrameType["transition"] = 1] = "transition"; - })(FrameType || (FrameType = {})); - var NativeError = global[__symbol__('Error')] = global.Error; + var blacklistedStackFramesSymbol = api.symbol('blacklistedStackFrames'); + var NativeError = global[api.symbol('Error')] = global['Error']; // Store the frames which should be removed from the stack frames var blackListedStackFrames = {}; // We must find the frame where Error was created, otherwise we assume we don't understand stack - // the frame will be an array, because Error with new or without new will - // have different stack frames. - var zoneAwareErrorStartFrames = []; - global.Error = ZoneAwareError; + var zoneAwareFrame1; + var zoneAwareFrame2; + global['Error'] = ZoneAwareError; var stackRewrite = 'stackRewrite'; - // some functions are not easily to be detected here, - // for example Timeout.ZoneTask.invoke, if we want to detect those functions - // by detect zone, we have to run all patched APIs, it is too risky - // so for those functions, just check whether the stack contains the string or not. - var otherZoneAwareFunctionNames = [ - 'ZoneTask.invoke', 'ZoneAware', 'getStacktraceWithUncaughtError', 'new LongStackTrace', - 'long-stack-trace' - ]; - function attachZoneAndRemoveInternalZoneFrames(error) { - // Save original stack trace - error.originalStack = error.stack; - // Process the stack trace and rewrite the frames. - if (ZoneAwareError[stackRewrite] && error.originalStack) { - var frames_1 = error.originalStack.split('\n'); - var zoneFrame = _currentZoneFrame; - var i_1 = 0; - // Find the first frame - while (i_1 < frames_1.length && - zoneAwareErrorStartFrames.filter(function (zf) { return zf.trim() === frames_1[i_1].trim(); }).length === 0) { - i_1++; - } - var _loop_2 = function () { - // trim here because blackListedStackFrames store the trimmed frames - var frame = frames_1[i_1].trim(); - if (frame) { - var frameType = blackListedStackFrames.hasOwnProperty(frame) && blackListedStackFrames[frame]; - if (frameType === FrameType.blackList) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (otherZoneAwareFunctionNames - .filter(function (f) { return frame.toLowerCase().indexOf(f.toLowerCase()) !== -1; }) - .length > 0) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (frameType === FrameType.transition) { - if (zoneFrame.parent) { - // This is the special frame where zone changed. Print and process it accordingly - zoneFrame = zoneFrame.parent; - } - else { - zoneFrame = null; - } - frames_1.splice(i_1, 1); - i_1--; - } - else { - frames_1[i_1] += " [" + zoneFrame.zone.name + "]"; - } - } - }; - for (; i_1 < frames_1.length && zoneFrame; i_1++) { - _loop_2(); - } - var finalStack = frames_1.join('\n'); - try { - error.stack = error.zoneAwareStack = finalStack; - } - catch (nonWritableErr) { - // in some browser, the error.stack is readonly such as PhantomJS - // so we need to store the stack frames to zoneAwareError directly - } - } - } /** * This is ZoneAwareError which processes the stack frame and cleans up extra frames as well as * adds zone information to it. */ function ZoneAwareError() { + var _this = this; // We always have to return native error otherwise the browser console will not work. var error = NativeError.apply(this, arguments); - if (!error.stack) { - // in IE, the error.stack will be undefined - // when error was constructed, it will only - // be available when throw + // Save original stack trace + var originalStack = error['originalStack'] = error.stack; + // Process the stack trace and rewrite the frames. + if (ZoneAwareError[stackRewrite] && originalStack) { + var frames_1 = originalStack.split('\n'); + var zoneFrame = api.currentZoneFrame(); + var i = 0; + // Find the first frame + while (!(frames_1[i] === zoneAwareFrame1 || frames_1[i] === zoneAwareFrame2) && + i < frames_1.length) { + i++; + } + for (; i < frames_1.length && zoneFrame; i++) { + var frame = frames_1[i]; + if (frame.trim()) { + switch (blackListedStackFrames[frame]) { + case 0 /* blackList */: + frames_1.splice(i, 1); + i--; + break; + case 1 /* transition */: + if (zoneFrame.parent) { + // This is the special frame where zone changed. Print and process it accordingly + zoneFrame = zoneFrame.parent; + } + else { + zoneFrame = null; + } + frames_1.splice(i, 1); + i--; + break; + default: + frames_1[i] += " [" + zoneFrame.zone.name + "]"; + } + } + } try { - throw error; + error.stack = error.zoneAwareStack = frames_1.join('\n'); } - catch (err) { - error = err; + catch (e) { + // ignore as some browsers don't allow overriding of stack } } - // 1. attach zone information to stack frame - // 2. remove zone internal stack frames - attachZoneAndRemoveInternalZoneFrames(error); + if (this instanceof NativeError && this.constructor != NativeError) { + // We got called with a `new` operator AND we are subclass of ZoneAwareError + // in that case we have to copy all of our properties to `this`. + Object.keys(error).concat('stack', 'message').forEach(function (key) { + var value = error[key]; + if (value !== undefined) { + try { + _this[key] = value; + } + catch (e) { + // ignore the assignment in case it is a setter and it throws. + } + } + }); + return this; + } return error; } // Copy the prototype so that instanceof operator works as expected ZoneAwareError.prototype = NativeError.prototype; - ZoneAwareError[Zone.__symbol__('blacklistedStackFrames')] = blackListedStackFrames; + ZoneAwareError[blacklistedStackFramesSymbol] = blackListedStackFrames; ZoneAwareError[stackRewrite] = false; // those properties need special handling var specialPropertyNames = ['stackTraceLimit', 'captureStackTrace', 'prepareStackTrace']; @@ -1052,6 +1400,7 @@ var Zone$1 = (function (global) { } }); } + var ZONE_CAPTURESTACKTRACE = 'zoneCaptureStackTrace'; Object.defineProperty(ZoneAwareError, 'prepareStackTrace', { get: function () { return NativeError.prepareStackTrace; @@ -1066,26 +1415,30 @@ var Zone$1 = (function (global) { for (var i = 0; i < structuredStackTrace.length; i++) { var st = structuredStackTrace[i]; // remove the first function which name is zoneCaptureStackTrace - if (st.getFunctionName() === 'zoneCaptureStackTrace') { + if (st.getFunctionName() === ZONE_CAPTURESTACKTRACE) { structuredStackTrace.splice(i, 1); break; } } } - return value.apply(this, [error, structuredStackTrace]); + return value.call(this, error, structuredStackTrace); }; } }); // Now we need to populate the `blacklistedStackFrames` as well as find the - // run/runGuraded/runTask frames. This is done by creating a detect zone and then threading + // run/runGuarded/runTask frames. This is done by creating a detect zone and then threading // the execution through all of the above methods so that we can look at the stack trace and // find the frames of interest. + var ZONE_AWARE_ERROR = 'ZoneAwareError'; + var ERROR_DOT = 'Error.'; + var EMPTY = ''; + var RUN_GUARDED = 'runGuarded'; + var RUN_TASK = 'runTask'; + var RUN = 'run'; + var BRACKETS = '('; + var AT = '@'; var detectZone = Zone.current.fork({ name: 'detect', - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - // Here only so that it will show up in the stack frame so that it can be black listed. - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); - }, onHandleError: function (parentZD, current, target, error) { if (error.originalStack && Error === ZoneAwareError) { var frames_2 = error.originalStack.split(/\n/); @@ -1094,7 +1447,7 @@ var Zone$1 = (function (global) { var frame = frames_2.shift(); // On safari it is possible to have stack frame with no line number. // This check makes sure that we don't filter frames on name only (must have - // linenumber) + // line number) if (/:\d+:\d+/.test(frame)) { // Get rid of the path so that we don't accidentally find function name in path. // In chrome the separator is `(` and `@` in FF and safari @@ -1102,27 +1455,26 @@ var Zone$1 = (function (global) { // Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24) // FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24 // Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24 - var fnName = frame.split('(')[0].split('@')[0]; - var frameType = FrameType.transition; - if (fnName.indexOf('ZoneAwareError') !== -1) { - // we found the ZoneAwareError start frame - // the frame will be different when call Error(...) - // and new Error(...), so we store them both - zoneAwareErrorStartFrames.push(frame); + var fnName = frame.split(BRACKETS)[0].split(AT)[0]; + var frameType = 1; + if (fnName.indexOf(ZONE_AWARE_ERROR) !== -1) { + zoneAwareFrame1 = frame; + zoneAwareFrame2 = frame.replace(ERROR_DOT, EMPTY); + blackListedStackFrames[zoneAwareFrame2] = 0 /* blackList */; } - if (fnName.indexOf('runGuarded') !== -1) { + if (fnName.indexOf(RUN_GUARDED) !== -1) { runGuardedFrame = true; } - else if (fnName.indexOf('runTask') !== -1) { + else if (fnName.indexOf(RUN_TASK) !== -1) { runTaskFrame = true; } - else if (fnName.indexOf('run') !== -1) { + else if (fnName.indexOf(RUN) !== -1) { runFrame = true; } else { - frameType = FrameType.blackList; + frameType = 0 /* blackList */; } - blackListedStackFrames[frame.trim()] = frameType; + blackListedStackFrames[frame] = frameType; // Once we find all of the frames we can stop looking. if (runFrame && runGuardedFrame && runTaskFrame) { ZoneAwareError[stackRewrite] = true; @@ -1136,190 +1488,52 @@ var Zone$1 = (function (global) { }); // carefully constructor a stack frame which contains all of the frames of interest which // need to be detected and blacklisted. - // use this method to handle - // 1. IE issue, the error.stack can only be not undefined after throw - // 2. handle Error(...) without new options - var throwError = function (message, withNew) { - try { - if (withNew) { - throw new Error(message); - } - else { - throw Error(message); - } - } - catch (err) { - return err; - } - }; - var nativeStackTraceLimit = NativeError.stackTraceLimit; - // in some system/browser, some additional stack frames - // will be generated (such as inline function) - // so the the stack frame to check ZoneAwareError Start - // maybe ignored because the frame's number will exceed - // stackTraceLimit, so we just set stackTraceLimit to 100 - // and reset after all detect work is done. - NativeError.stackTraceLimit = 100; - var detectRunFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames', true); - }); - }); - }; - var detectRunWithoutNewFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames'); - }); - }); - }; - // Cause the error to extract the stack frames. - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunFn, null, function () { return null; }, null)); - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunWithoutNewFn, null, function () { return null; }, null)); - function handleDetectError(error) { - var frames = error.stack ? error.stack.split(/\n/) : []; - while (frames.length) { - var frame = frames.shift(); - // On safari it is possible to have stack frame with no line number. - // This check makes sure that we don't filter frames on name only (must have - // linenumber) - var trimmedFrame = frame.trim().split('[')[0].trim(); - if (/:\d+:\d+/.test(trimmedFrame) && !blackListedStackFrames.hasOwnProperty(trimmedFrame)) { - blackListedStackFrames[trimmedFrame] = FrameType.blackList; - } - // when we found runGuarded or runTask, we should stop - // otherwise we will store some stack frames like - // module.load, require and something like that - var fnName = frame.split('(')[0].split('@')[0]; - if (fnName.indexOf('runGuarded') !== -1) { - break; - } - else if (fnName.indexOf('runTask') !== -1) { - break; - } - } - } - var detectEmptyZone = Zone.root.fork({ - name: 'detectEmptyZone', - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; - } - }); - var detectZoneWithCallbacks = Zone.root.fork({ - name: 'detectCallbackZone', - onFork: function (parentDelegate, currentZone, targetZone, zoneSpec) { - // we need to generate Error with or without new - handleDetectError(throwError('onFork')); - handleDetectError(throwError('onFork', false)); - return parentDelegate.fork(targetZone, zoneSpec); - }, - onIntercept: function (parentDelegate, currentZone, targetZone, delegate, source) { - handleDetectError(throwError('onIntercept')); - handleDetectError(throwError('onIntercept', false)); - return parentDelegate.intercept(targetZone, delegate, source); - }, - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - handleDetectError(throwError('onInvoke')); - handleDetectError(throwError('onInvoke', false)); - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); - }, - onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onScheduleTask')); - handleDetectError(throwError('onScheduleTask', false)); - return parentZoneDelegate.scheduleTask(targetZone, task); - }, - onInvokeTask: function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { - handleDetectError(throwError('onInvokeTask')); - handleDetectError(throwError('onInvokeTask', false)); - return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var childDetectZone = detectZone.fork({ + name: 'child', + onScheduleTask: function (delegate, curr, target, task) { + return delegate.scheduleTask(target, task); }, - onCancelTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onCancelTask')); - handleDetectError(throwError('onCancelTask', false)); - return parentZoneDelegate.cancelTask(targetZone, task); + onInvokeTask: function (delegate, curr, target, task, applyThis, applyArgs) { + return delegate.invokeTask(target, task, applyThis, applyArgs); }, - onHasTask: function (delegate, current, target, hasTaskState) { - handleDetectError(throwError('onHasTask')); - handleDetectError(throwError('onHasTask', false)); - return delegate.hasTask(target, hasTaskState); + onCancelTask: function (delegate, curr, target, task) { + return delegate.cancelTask(target, task); }, - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; + onInvoke: function (delegate, curr, target, callback, applyThis, applyArgs, source) { + return delegate.invoke(target, callback, applyThis, applyArgs, source); } }); - var detectFn = function () { - throw throwError('zoneAwareFrames'); - }; - var detectWithoutNewFn = function () { - throw throwError('zoneAwareFrames', false); - }; - var detectPromiseFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); + // we need to detect all zone related frames, it will + // exceed default stackTraceLimit, so we set it to + // larger number here, and restore it after detect finish. + var originalStackTraceLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 100; + // we schedule event/micro/macro task, and invoke them + // when onSchedule, so we can get all stack traces for + // all kinds of tasks with one error thrown. + childDetectZone.run(function () { + childDetectZone.runGuarded(function () { + var fakeTransitionTo = function () { }; + childDetectZone.scheduleEventTask(blacklistedStackFramesSymbol, function () { + childDetectZone.scheduleMacroTask(blacklistedStackFramesSymbol, function () { + childDetectZone.scheduleMicroTask(blacklistedStackFramesSymbol, function () { + throw new ZoneAwareError(ZoneAwareError, NativeError); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }, function () { }); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }, function () { }); }); - }; - var detectPromiseWithoutNewFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); - }); - }; - var detectPromiseCaughtFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - p.catch(function (err) { - throw err; - }); - }; - var detectPromiseCaughtWithoutNewFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); - }); - p.catch(function (err) { - throw err; - }); - }; - // Cause the error to extract the stack frames. - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectFn); - }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectFn); }); - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectEmptyZone.runGuarded(detectPromiseFn); - detectZoneWithCallbacks.runGuarded(detectPromiseFn); - detectEmptyZone.runGuarded(detectPromiseWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseWithoutNewFn); - detectEmptyZone.runGuarded(detectPromiseCaughtFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtFn); - detectEmptyZone.runGuarded(detectPromiseCaughtWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtWithoutNewFn); - NativeError.stackTraceLimit = nativeStackTraceLimit; - return global['Zone'] = Zone; -})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); + Error.stackTraceLimit = originalStackTraceLimit; +}); /** * @license @@ -1329,300 +1543,10 @@ var Zone$1 = (function (global) { * found in the LICENSE file at https://angular.io/license */ /** - * Suppress closure compiler errors about unknown 'Zone' variable * @fileoverview - * @suppress {undefinedVars,globalThis} - */ -var zoneSymbol = function (n) { return "__zone_symbol__" + n; }; -var _global = typeof window === 'object' && window || typeof self === 'object' && self || global; - - -var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); -var isNode = (!('nw' in _global) && typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]'); - -// we are in electron of nw, so we are both browser and nodejs -var isMix = typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]' && !isWebWorker && - !!(typeof window !== 'undefined' && window['HTMLElement']); -function patchProperty(obj, prop) { - var desc = Object.getOwnPropertyDescriptor(obj, prop) || { enumerable: true, configurable: true }; - var originalDesc = Object.getOwnPropertyDescriptor(obj, 'original' + prop); - if (!originalDesc && desc.get) { - Object.defineProperty(obj, 'original' + prop, { enumerable: false, configurable: true, get: desc.get }); - } - // A property descriptor cannot have getter/setter and be writable - // deleting the writable and value properties avoids this error: - // - // TypeError: property descriptors must not specify a value or be writable when a - // getter or setter has been specified - delete desc.writable; - delete desc.value; - // substr(2) cuz 'onclick' -> 'click', etc - var eventName = prop.substr(2); - var _prop = zoneSymbol('_' + prop); - desc.set = function (fn) { - if (this[_prop]) { - this.removeEventListener(eventName, this[_prop]); - } - if (typeof fn === 'function') { - var wrapFn = function (event) { - var result; - result = fn.apply(this, arguments); - if (result != undefined && !result) - event.preventDefault(); - }; - this[_prop] = wrapFn; - this.addEventListener(eventName, wrapFn, false); - } - else { - this[_prop] = null; - } - }; - // The getter would return undefined for unassigned properties but the default value of an - // unassigned property is null - desc.get = function () { - var r = this[_prop] || null; - // result will be null when use inline event attribute, - // such as - // because the onclick function is internal raw uncompiled handler - // the onclick will be evaluated when first time event was triggered or - // the property is accessed, https://github.com/angular/zone.js/issues/525 - // so we should use original native get to retrieve the handler - if (r === null) { - if (originalDesc && originalDesc.get) { - r = originalDesc.get.apply(this, arguments); - if (r) { - desc.set.apply(this, [r]); - if (typeof this['removeAttribute'] === 'function') { - this.removeAttribute(prop); - } - } - } - } - return this[_prop] || null; - }; - Object.defineProperty(obj, prop, desc); -} - -function patchOnProperties(obj, properties) { - var onProperties = []; - for (var prop in obj) { - if (prop.substr(0, 2) == 'on') { - onProperties.push(prop); - } - } - for (var j = 0; j < onProperties.length; j++) { - patchProperty(obj, onProperties[j]); - } - if (properties) { - for (var i = 0; i < properties.length; i++) { - patchProperty(obj, 'on' + properties[i]); - } - } -} - -var EVENT_TASKS = zoneSymbol('eventTasks'); -// For EventTarget -var ADD_EVENT_LISTENER = 'addEventListener'; -var REMOVE_EVENT_LISTENER = 'removeEventListener'; -function findExistingRegisteredTask(target, handler, name, capture, remove) { - var eventTasks = target[EVENT_TASKS]; - if (eventTasks) { - for (var i = 0; i < eventTasks.length; i++) { - var eventTask = eventTasks[i]; - var data = eventTask.data; - var listener = data.handler; - if ((data.handler === handler || listener.listener === handler) && - data.useCapturing === capture && data.eventName === name) { - if (remove) { - eventTasks.splice(i, 1); - } - return eventTask; - } - } - } - return null; -} -function attachRegisteredEvent(target, eventTask, isPrepend) { - var eventTasks = target[EVENT_TASKS]; - if (!eventTasks) { - eventTasks = target[EVENT_TASKS] = []; - } - if (isPrepend) { - eventTasks.unshift(eventTask); - } - else { - eventTasks.push(eventTask); - } -} -var defaultListenerMetaCreator = function (self, args) { - return { - useCapturing: args[2], - eventName: args[0], - handler: args[1], - target: self || _global, - name: args[0], - invokeAddFunc: function (addFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[addFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[addFnSymbol](this.eventName, delegate, this.useCapturing); - } - }, - invokeRemoveFunc: function (removeFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[removeFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[removeFnSymbol](this.eventName, delegate, this.useCapturing); - } - } - }; -}; -function makeZoneAwareAddListener(addFnName, removeFnName, useCapturingParam, allowDuplicates, isPrepend, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (allowDuplicates === void 0) { allowDuplicates = false; } - if (isPrepend === void 0) { isPrepend = false; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var addFnSymbol = zoneSymbol(addFnName); - var removeFnSymbol = zoneSymbol(removeFnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - function scheduleEventListener(eventTask) { - var meta = eventTask.data; - attachRegisteredEvent(meta.target, eventTask, isPrepend); - return meta.invokeAddFunc(addFnSymbol, eventTask); - } - function cancelEventListener(eventTask) { - var meta = eventTask.data; - findExistingRegisteredTask(meta.target, eventTask.invoke, meta.eventName, meta.useCapturing, true); - return meta.invokeRemoveFunc(removeFnSymbol, eventTask); - } - return function zoneAwareAddListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var delegate = null; - if (typeof data.handler == 'function') { - delegate = data.handler; - } - else if (data.handler && data.handler.handleEvent) { - delegate = function (event) { return data.handler.handleEvent(event); }; - } - var validZoneHandler = false; - try { - // In cross site contexts (such as WebDriver frameworks like Selenium), - // accessing the handler object here will cause an exception to be thrown which - // will fail tests prematurely. - validZoneHandler = data.handler && data.handler.toString() === '[object FunctionWrapper]'; - } - catch (error) { - // Returning nothing here is fine, because objects in a cross-site context are unusable - return; - } - // Ignore special listeners of IE11 & Edge dev tools, see - // https://github.com/angular/zone.js/issues/150 - if (!delegate || validZoneHandler) { - return data.invokeAddFunc(addFnSymbol, data.handler); - } - if (!allowDuplicates) { - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, false); - if (eventTask) { - // we already registered, so this will have noop. - return data.invokeAddFunc(addFnSymbol, eventTask); - } - } - var zone = Zone.current; - var source = data.target.constructor['name'] + '.' + addFnName + ':' + data.eventName; - zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener); - }; -} -function makeZoneAwareRemoveListener(fnName, useCapturingParam, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var symbol = zoneSymbol(fnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - return function zoneAwareRemoveListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, true); - if (eventTask) { - eventTask.zone.cancelTask(eventTask); - } - else { - data.invokeRemoveFunc(symbol, data.handler); - } - }; -} - - -var zoneAwareAddEventListener = makeZoneAwareAddListener(ADD_EVENT_LISTENER, REMOVE_EVENT_LISTENER); -var zoneAwareRemoveEventListener = makeZoneAwareRemoveListener(REMOVE_EVENT_LISTENER); -function patchEventTargetMethods(obj, addFnName, removeFnName, metaCreator) { - if (addFnName === void 0) { addFnName = ADD_EVENT_LISTENER; } - if (removeFnName === void 0) { removeFnName = REMOVE_EVENT_LISTENER; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - if (obj && obj[addFnName]) { - patchMethod(obj, addFnName, function () { return makeZoneAwareAddListener(addFnName, removeFnName, true, false, false, metaCreator); }); - patchMethod(obj, removeFnName, function () { return makeZoneAwareRemoveListener(removeFnName, true, metaCreator); }); - return true; - } - else { - return false; - } -} -var originalInstanceKey = zoneSymbol('originalInstance'); -// wrap some native API on `window` - - -function createNamedFn(name, delegate) { - try { - return (Function('f', "return function " + name + "(){return f(this, arguments)}"))(delegate); - } - catch (error) { - // if we fail, we must be CSP, just return delegate. - return function () { - return delegate(this, arguments); - }; - } -} -function patchMethod(target, name, patchFn) { - var proto = target; - while (proto && Object.getOwnPropertyNames(proto).indexOf(name) === -1) { - proto = Object.getPrototypeOf(proto); - } - if (!proto && target[name]) { - // somehow we did not find it, but we can see it. This happens on IE for Window properties. - proto = target; - } - var delegateName = zoneSymbol(name); - var delegate; - if (proto && !(delegate = proto[delegateName])) { - delegate = proto[delegateName] = proto[name]; - proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); - } - return delegate; -} -// TODO: @JiaLiPassion, support cancel task later if necessary - - - -Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; -Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; - -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * @suppress {missingRequire} */ +var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; @@ -1636,35 +1560,55 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - delete tasksByHandleId[data.handleId]; + // issue-934, task will be cancelled + // even it is a periodic task such as + // setInterval + if (!(task.data && task.data.isPeriodic)) { + if (typeof data.handleId === 'number') { + // in non-nodejs env, we remove timerId + // from local cache + delete tasksByHandleId[data.handleId]; + } + else if (data.handleId) { + // Node returns complex objects as handleIds + // we remove task reference from timer object + data.handleId[taskSymbol] = null; + } + } } } - data.args[0] = timer; data.handleId = setNative.apply(window, data.args); - tasksByHandleId[data.handleId] = task; return task; } function clearTask(task) { - delete tasksByHandleId[task.data.handleId]; return clearNative(task.data.handleId); } setNative = patchMethod(window, setName, function (delegate) { return function (self, args) { if (typeof args[0] === 'function') { - var zone = Zone.current; var options = { handleId: null, isPeriodic: nameSuffix === 'Interval', delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, args: args }; - var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); + var task = scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask); if (!task) { return task; } // Node.js must additionally support the ref and unref functions. var handle = task.data.handleId; + if (typeof handle === 'number') { + // for non nodejs env, we save handleId: task + // mapping in local cache for clearTimeout + tasksByHandleId[handle] = task; + } + else if (handle) { + // for nodejs env, we save task + // reference in timerId Object for clearTimeout + handle[taskSymbol] = task; + } // check whether handle is null, because some polyfill or browser // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && @@ -1672,6 +1616,9 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } + if (typeof handle === 'number' || handle) { + return handle; + } return task; } else { @@ -1681,10 +1628,29 @@ function patchTimer(window, setName, cancelName, nameSuffix) { }; }); clearNative = patchMethod(window, cancelName, function (delegate) { return function (self, args) { - var task = typeof args[0] === 'number' ? tasksByHandleId[args[0]] : args[0]; + var id = args[0]; + var task; + if (typeof id === 'number') { + // non nodejs env. + task = tasksByHandleId[id]; + } + else { + // nodejs env. + task = id && id[taskSymbol]; + // other environments. + if (!task) { + task = id; + } + } if (task && typeof task.type === 'string') { if (task.state !== 'notScheduled' && (task.cancelFn && task.data.isPeriodic || task.runCount === 0)) { + if (typeof id === 'number') { + delete tasksByHandleId[id]; + } + else if (id) { + id[taskSymbol] = null; + } // Do not cancel already canceled functions task.zone.cancelTask(task); } diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js index 0aa13a09f..0bd1cf21f 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -27,8 +27,8 @@ var IGNORE_FRAMES = {}; var creationTrace = '__creationTrace__'; var ERROR_TAG = 'STACKTRACE TRACKING'; var SEP_TAG = '__SEP_TAG__'; -var sepTemplate = ''; -var LongStackTrace = (function () { +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = /** @class */ (function () { function LongStackTrace() { this.error = getStacktrace(); this.timestamp = new Date(); @@ -67,7 +67,7 @@ function addErrorStack(lines, error) { } } function renderLongStackTrace(frames, stack) { - var longTrace = [stack.trim()]; + var longTrace = [stack ? stack.trim() : '']; if (frames) { var timestamp = new Date().getTime(); for (var i = 0; i < frames.length; i++) { @@ -91,33 +91,44 @@ Zone['longStackTraceZoneSpec'] = { if (!error) { return undefined; } - var task = error[Zone.__symbol__('currentTask')]; - var trace = task && task.data && task.data[creationTrace]; + var trace = error[Zone.__symbol__('currentTaskTrace')]; if (!trace) { return error.stack; } return renderLongStackTrace(trace, error.stack); }, onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - var currentTask = Zone.currentTask; - var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; - trace = [new LongStackTrace()].concat(trace); - if (trace.length > this.longStackTraceLimit) { - trace.length = this.longStackTraceLimit; - } - if (!task.data) - task.data = {}; - task.data[creationTrace] = trace; + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var currentTask = Zone.currentTask; + var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) + task.data = {}; + task.data[creationTrace] = trace; + } return parentZoneDelegate.scheduleTask(targetZone, task); }, onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) { - var parentTask = Zone.currentTask || error.task; - if (error instanceof Error && parentTask) { - var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); - try { - error.stack = error.longStack = longStack; - } - catch (err) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = error.longStack = longStack; + } + catch (err) { + } } } return parentZoneDelegate.handleError(targetZone, error); @@ -130,27 +141,32 @@ function captureStackTraces(stackTraces, count) { } } function computeIgnoreFrames() { + if (Error.stackTraceLimit <= 0) { + return; + } var frames = []; captureStackTraces(frames, 2); var frames1 = frames[0]; var frames2 = frames[1]; for (var i = 0; i < frames1.length; i++) { var frame1 = frames1[i]; - var frame2 = frames2[i]; - if (!sepTemplate && frame1.indexOf(ERROR_TAG) == -1) { - sepTemplate = frame1.replace(/^(\s*(at)?\s*)([\w\/\<]+)/, '$1' + SEP_TAG); + if (frame1.indexOf(ERROR_TAG) == -1) { + var match = frame1.match(/^\s*at\s+/); + if (match) { + sepTemplate = match[0] + SEP_TAG + ' (http://localhost)'; + break; + } } + } + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + var frame2 = frames2[i]; if (frame1 === frame2) { IGNORE_FRAMES[frame1] = true; } else { break; } - console.log('>>>>>>', sepTemplate, frame1); - } - if (!sepTemplate) { - // If we could not find it default to this text. - sepTemplate = SEP_TAG + '@[native code]'; } } computeIgnoreFrames(); @@ -162,13 +178,16 @@ computeIgnoreFrames(); * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var ProxyZoneSpec = (function () { +var ProxyZoneSpec = /** @class */ (function () { function ProxyZoneSpec(defaultSpecDelegate) { if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } this.defaultSpecDelegate = defaultSpecDelegate; this.name = 'ProxyZone'; this.properties = { 'ProxyZoneSpec': this }; this.propertyKeys = null; + this.lastTaskState = null; + this.isNeedToTriggerHasTask = false; + this.tasks = []; this.setDelegate(defaultSpecDelegate); } ProxyZoneSpec.get = function () { @@ -178,13 +197,14 @@ var ProxyZoneSpec = (function () { return ProxyZoneSpec.get() instanceof ProxyZoneSpec; }; ProxyZoneSpec.assertPresent = function () { - if (!this.isLoaded()) { + if (!ProxyZoneSpec.isLoaded()) { throw new Error("Expected to be running in 'ProxyZone', but it was not found."); } return ProxyZoneSpec.get(); }; ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { var _this = this; + var isNewDelegate = this._delegateSpec !== delegateSpec; this._delegateSpec = delegateSpec; this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); this.propertyKeys = null; @@ -192,13 +212,57 @@ var ProxyZoneSpec = (function () { this.propertyKeys = Object.keys(delegateSpec.properties); this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); } + // if set a new delegateSpec, shoulde check whether need to + // trigger hasTask or not + if (isNewDelegate && this.lastTaskState && + (this.lastTaskState.macroTask || this.lastTaskState.microTask)) { + this.isNeedToTriggerHasTask = true; + } }; ProxyZoneSpec.prototype.getDelegate = function () { return this._delegateSpec; }; ProxyZoneSpec.prototype.resetDelegate = function () { + var delegateSpec = this.getDelegate(); this.setDelegate(this.defaultSpecDelegate); }; + ProxyZoneSpec.prototype.tryTriggerHasTask = function (parentZoneDelegate, currentZone, targetZone) { + if (this.isNeedToTriggerHasTask && this.lastTaskState) { + // last delegateSpec has microTask or macroTask + // should call onHasTask in current delegateSpec + this.isNeedToTriggerHasTask = false; + this.onHasTask(parentZoneDelegate, currentZone, targetZone, this.lastTaskState); + } + }; + ProxyZoneSpec.prototype.removeFromTasks = function (task) { + if (!this.tasks) { + return; + } + for (var i = 0; i < this.tasks.length; i++) { + if (this.tasks[i] === task) { + this.tasks.splice(i, 1); + return; + } + } + }; + ProxyZoneSpec.prototype.getAndClearPendingTasksInfo = function () { + if (this.tasks.length === 0) { + return ''; + } + var taskInfo = this.tasks.map(function (task) { + var dataInfo = task.data && + Object.keys(task.data) + .map(function (key) { + return key + ':' + task.data[key]; + }) + .join(','); + return "type: " + task.type + ", source: " + task.source + ", args: {" + dataInfo + "}"; + }); + var pendingTasksInfo = '--Pendng async tasks are: [' + taskInfo + ']'; + // clear tasks + this.tasks = []; + return pendingTasksInfo; + }; ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { if (this._delegateSpec && this._delegateSpec.onFork) { return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); @@ -216,6 +280,7 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onInvoke) { return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); } @@ -232,6 +297,9 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.tasks.push(task); + } if (this._delegateSpec && this._delegateSpec.onScheduleTask) { return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); } @@ -240,7 +308,11 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { - if (this._delegateSpec && this._delegateSpec.onFork) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onInvokeTask) { return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); } else { @@ -248,6 +320,10 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onCancelTask) { return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); } @@ -256,6 +332,7 @@ var ProxyZoneSpec = (function () { } }; ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + this.lastTaskState = hasTaskState; if (this._delegateSpec && this._delegateSpec.onHasTask) { this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); } @@ -276,7 +353,7 @@ Zone['ProxyZoneSpec'] = ProxyZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var SyncTestZoneSpec = (function () { +var SyncTestZoneSpec = /** @class */ (function () { function SyncTestZoneSpec(namePrefix) { this.runZone = Zone.current; this.name = 'syncTestZone for ' + namePrefix; @@ -305,45 +382,105 @@ Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var AsyncTestZoneSpec = (function () { +var _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; +var AsyncTestZoneSpec = /** @class */ (function () { function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this.finishCallback = finishCallback; + this.failCallback = failCallback; this._pendingMicroTasks = false; this._pendingMacroTasks = false; this._alreadyErrored = false; + this._isSync = false; this.runZone = Zone.current; - this._finishCallback = finishCallback; - this._failCallback = failCallback; + this.unresolvedChainedPromiseCount = 0; + this.supportWaitUnresolvedChainedPromise = false; this.name = 'asyncTestZone for ' + namePrefix; + this.properties = { 'AsyncTestZoneSpec': this }; + this.supportWaitUnresolvedChainedPromise = + _global[Zone.__symbol__('supportWaitUnResolvedChainedPromise')] === true; } + AsyncTestZoneSpec.prototype.isUnresolvedChainedPromisePending = function () { + return this.unresolvedChainedPromiseCount > 0; + }; AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { var _this = this; - if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + if (!(this._pendingMicroTasks || this._pendingMacroTasks || + (this.supportWaitUnresolvedChainedPromise && this.isUnresolvedChainedPromisePending()))) { // We do this because we would like to catch unhandled rejected promises. this.runZone.run(function () { setTimeout(function () { if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { - _this._finishCallback(); + _this.finishCallback(); } }, 0); }); } }; + AsyncTestZoneSpec.prototype.patchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var patchPromiseForTest = Promise[Zone.__symbol__('patchPromiseForTest')]; + if (patchPromiseForTest) { + patchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.unPatchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var unPatchPromiseForTest = Promise[Zone.__symbol__('unPatchPromiseForTest')]; + if (unPatchPromiseForTest) { + unPatchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + if (task.type === 'microTask' && task.data && task.data instanceof Promise) { + // check whether the promise is a chained promise + if (task.data[AsyncTestZoneSpec.symbolParentUnresolved] === true) { + // chained promise is being scheduled + this.unresolvedChainedPromiseCount--; + } + } + return delegate.scheduleTask(target, task); + }; + AsyncTestZoneSpec.prototype.onInvokeTask = function (delegate, current, target, task, applyThis, applyArgs) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.invokeTask(target, task, applyThis, applyArgs); + }; + AsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.cancelTask(target, task); + }; // Note - we need to use onInvoke at the moment to call finish when a test is // fully synchronous. TODO(juliemr): remove this when the logic for // onHasTask changes and it calls whenever the task queues are dirty. + // updated by(JiaLiPassion), only call finish callback when no task + // was scheduled/invoked/canceled. AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { try { + this._isSync = true; return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); } finally { - this._finishCallbackIfDone(); + var afterTaskCounts = parentZoneDelegate._taskCounts; + if (this._isSync) { + this._finishCallbackIfDone(); + } } }; AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { // Let the parent try to handle the error. var result = parentZoneDelegate.handleError(targetZone, error); if (result) { - this._failCallback(error); + this.failCallback(error); this._alreadyErrored = true; } return false; @@ -359,6 +496,7 @@ var AsyncTestZoneSpec = (function () { this._finishCallbackIfDone(); } }; + AsyncTestZoneSpec.symbolParentUnresolved = Zone.__symbol__('parentUnresolved'); return AsyncTestZoneSpec; }()); // Export the class so that new instances can be created with proper @@ -372,23 +510,95 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +var __read = (undefined && undefined.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spread = (undefined && undefined.__spread) || function () { + for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); + return ar; +}; (function (global) { - var Scheduler = (function () { + var OriginalDate = global.Date; + var FakeDate = /** @class */ (function () { + function FakeDate() { + if (arguments.length === 0) { + var d = new OriginalDate(); + d.setTime(FakeDate.now()); + return d; + } + else { + var args = Array.prototype.slice.call(arguments); + return new (OriginalDate.bind.apply(OriginalDate, __spread([void 0], args)))(); + } + } + FakeDate.now = function () { + var fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncTestZoneSpec) { + return fakeAsyncTestZoneSpec.getCurrentRealTime() + fakeAsyncTestZoneSpec.getCurrentTime(); + } + return OriginalDate.now.apply(this, arguments); + }; + return FakeDate; + }()); + FakeDate.UTC = OriginalDate.UTC; + FakeDate.parse = OriginalDate.parse; + // keep a reference for zone patched timer function + var timers = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval + }; + var Scheduler = /** @class */ (function () { function Scheduler() { // Next scheduler id. - this.nextId = 0; + this.nextId = 1; // Scheduler queue with the tuple of end time and callback function - sorted by end time. this._schedulerQueue = []; // Current simulated time in millis. this._currentTime = 0; + // Current real time in millis. + this._currentRealTime = OriginalDate.now(); } - Scheduler.prototype.scheduleFunction = function (cb, delay, args, id) { + Scheduler.prototype.getCurrentTime = function () { + return this._currentTime; + }; + Scheduler.prototype.getCurrentRealTime = function () { + return this._currentRealTime; + }; + Scheduler.prototype.setCurrentRealTime = function (realTime) { + this._currentRealTime = realTime; + }; + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } if (id === void 0) { id = -1; } var currentId = id < 0 ? this.nextId++ : id; var endTime = this._currentTime + delay; // Insert so that scheduler queue remains sorted by end time. - var newEntry = { endTime: endTime, id: currentId, func: cb, args: args, delay: delay }; + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; var i = 0; for (; i < this._schedulerQueue.length; i++) { var currentEntry = this._schedulerQueue[i]; @@ -407,9 +617,14 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } } }; - Scheduler.prototype.tick = function (millis) { + Scheduler.prototype.tick = function (millis, doTick) { if (millis === void 0) { millis = 0; } var finalTime = this._currentTime + millis; + var lastCurrentTime = 0; + if (this._schedulerQueue.length === 0 && doTick) { + doTick(millis); + return; + } while (this._schedulerQueue.length > 0) { var current = this._schedulerQueue[0]; if (finalTime < current.endTime) { @@ -419,7 +634,11 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; else { // Time to run scheduled function. Remove it from the head of queue. var current_1 = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; this._currentTime = current_1.endTime; + if (doTick) { + doTick(this._currentTime - lastCurrentTime); + } var retval = current_1.func.apply(global, current_1.args); if (!retval) { // Uncaught exception in the current scheduled function. Stop processing the queue. @@ -429,18 +648,79 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } this._currentTime = finalTime; }; + Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) { + if (limit === void 0) { limit = 20; } + if (flushPeriodic === void 0) { flushPeriodic = false; } + if (flushPeriodic) { + return this.flushPeriodic(doTick); + } + else { + return this.flushNonPeriodic(limit, doTick); + } + }; + Scheduler.prototype.flushPeriodic = function (doTick) { + if (this._schedulerQueue.length === 0) { + return 0; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + var startTime = this._currentTime; + var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; + this.tick(lastTask.endTime - startTime, doTick); + return this._currentTime - startTime; + }; + Scheduler.prototype.flushNonPeriodic = function (limit, doTick) { + var startTime = this._currentTime; + var lastCurrentTime = 0; + var count = 0; + while (this._schedulerQueue.length > 0) { + count++; + if (count > limit) { + throw new Error('flush failed after reaching the limit of ' + limit + + ' tasks. Does your code use a polling timeout?'); + } + // flush only non-periodic timers. + // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. + if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; }) + .length === 0) { + break; + } + var current = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current.endTime; + if (doTick) { + // Update any secondary schedulers like Jasmine mock Date. + doTick(this._currentTime - lastCurrentTime); + } + var retval = current.func.apply(global, current.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + return this._currentTime - startTime; + }; return Scheduler; }()); - var FakeAsyncTestZoneSpec = (function () { - function FakeAsyncTestZoneSpec(namePrefix) { + var FakeAsyncTestZoneSpec = /** @class */ (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this.macroTaskOptions = macroTaskOptions; this._scheduler = new Scheduler(); this._microtasks = []; this._lastError = null; this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; this.pendingPeriodicTimers = []; this.pendingTimers = []; + this.patchDateLocked = false; this.properties = { 'FakeAsyncTestZoneSpec': this }; this.name = 'fakeAsyncTestZone for ' + namePrefix; + // in case user can't access the construction of FakeAsyncTestSpec + // user can also define macroTaskOptions by define a global variable. + if (!this.macroTaskOptions) { + this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')]; + } } FakeAsyncTestZoneSpec.assertInZone = function () { if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { @@ -488,7 +768,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; return function () { // Requeue the timer callback if it's not been canceled. if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { - _this._scheduler.scheduleFunction(fn, interval, args, id); + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); } }; }; @@ -498,30 +778,29 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); }; }; - FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args) { + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { + if (isTimer === void 0) { isTimer = true; } var removeTimerFn = this._dequeueTimer(this._scheduler.nextId); // Queue the callback and dequeue the timer on success and error. var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn }); - var id = this._scheduler.scheduleFunction(cb, delay, args); - this.pendingTimers.push(id); + var id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer); + if (isTimer) { + this.pendingTimers.push(id); + } return id; }; FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id); this._scheduler.removeScheduledFunctionWithId(id); }; - FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } + FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval, args) { var id = this._scheduler.nextId; var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; var cb = this._fnAndFlush(fn, completers); // Use the callback created above to requeue on success. completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); // Queue the callback and dequeue the periodic timer only on error. - this._scheduler.scheduleFunction(cb, interval, args); + this._scheduler.scheduleFunction(cb, interval, args, true); this.pendingPeriodicTimers.push(id); return id; }; @@ -535,11 +814,55 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._lastError = null; throw error; }; - FakeAsyncTestZoneSpec.prototype.tick = function (millis) { + FakeAsyncTestZoneSpec.prototype.getCurrentTime = function () { + return this._scheduler.getCurrentTime(); + }; + FakeAsyncTestZoneSpec.prototype.getCurrentRealTime = function () { + return this._scheduler.getCurrentRealTime(); + }; + FakeAsyncTestZoneSpec.prototype.setCurrentRealTime = function (realTime) { + this._scheduler.setCurrentRealTime(realTime); + }; + FakeAsyncTestZoneSpec.patchDate = function () { + if (global['Date'] === FakeDate) { + // already patched + return; + } + global['Date'] = FakeDate; + FakeDate.prototype = OriginalDate.prototype; + // try check and reset timers + // because jasmine.clock().install() may + // have replaced the global timer + FakeAsyncTestZoneSpec.checkTimerPatch(); + }; + FakeAsyncTestZoneSpec.resetDate = function () { + if (global['Date'] === FakeDate) { + global['Date'] = OriginalDate; + } + }; + FakeAsyncTestZoneSpec.checkTimerPatch = function () { + if (global.setTimeout !== timers.setTimeout) { + global.setTimeout = timers.setTimeout; + global.clearTimeout = timers.clearTimeout; + } + if (global.setInterval !== timers.setInterval) { + global.setInterval = timers.setInterval; + global.clearInterval = timers.clearInterval; + } + }; + FakeAsyncTestZoneSpec.prototype.lockDatePatch = function () { + this.patchDateLocked = true; + FakeAsyncTestZoneSpec.patchDate(); + }; + FakeAsyncTestZoneSpec.prototype.unlockDatePatch = function () { + this.patchDateLocked = false; + FakeAsyncTestZoneSpec.resetDate(); + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { if (millis === void 0) { millis = 0; } FakeAsyncTestZoneSpec.assertInZone(); this.flushMicrotasks(); - this._scheduler.tick(millis); + this._scheduler.tick(millis, doTick); if (this._lastError !== null) { this._resetLastErrorAndThrow(); } @@ -555,29 +878,80 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; }; while (this._microtasks.length > 0) { var microtask = this._microtasks.shift(); - microtask(); + microtask.func.apply(microtask.target, microtask.args); } flushErrors(); }; + FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) { + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + return elapsed; + }; FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { switch (task.type) { case 'microTask': - this._microtasks.push(task.invoke); + var args = task.data && task.data.args; + // should pass additional arguments to callback if have any + // currently we know process.nextTick will have such additional + // arguments + var additionalArgs = void 0; + if (args) { + var callbackIndex = task.data.cbIdx; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + additionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: additionalArgs, + target: task.data && task.data.target + }); break; case 'macroTask': switch (task.source) { case 'setTimeout': - task.data['handleId'] = - this._setTimeout(task.invoke, task.data['delay'], task.data['args']); + task.data['handleId'] = this._setTimeout(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + break; + case 'setImmediate': + task.data['handleId'] = this._setTimeout(task.invoke, 0, Array.prototype.slice.call(task.data['args'], 1)); break; case 'setInterval': - task.data['handleId'] = - this._setInterval(task.invoke, task.data['delay'], task.data['args']); + task.data['handleId'] = this._setInterval(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); break; case 'XMLHttpRequest.send': - throw new Error('Cannot make XHRs from within a fake async test.'); + throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' + + task.data['url']); + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + // Simulate a requestAnimationFrame by using a setTimeout with 16 ms. + // (60 frames per second) + task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame); + break; default: - task = delegate.scheduleTask(target, task); + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var args_1 = task.data && task.data['args']; + var delay = args_1 && args_1.length > 1 ? args_1[1] : 0; + var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1; + if (!!macroTaskOption.isPeriodic) { + // periodic macroTask, use setInterval to simulate + task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs); + task.data.isPeriodic = true; + } + else { + // not periodic, use setTimeout to simulate + task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); + } + break; + } + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); } break; case 'eventTask': @@ -589,13 +963,47 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { switch (task.source) { case 'setTimeout': + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': return this._clearTimeout(task.data['handleId']); case 'setInterval': return this._clearInterval(task.data['handleId']); default: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var handleId = task.data['handleId']; + return macroTaskOption.isPeriodic ? this._clearInterval(handleId) : + this._clearTimeout(handleId); + } return delegate.cancelTask(target, task); } }; + FakeAsyncTestZoneSpec.prototype.onInvoke = function (delegate, current, target, callback, applyThis, applyArgs, source) { + try { + FakeAsyncTestZoneSpec.patchDate(); + return delegate.invoke(target, callback, applyThis, applyArgs, source); + } + finally { + if (!this.patchDateLocked) { + FakeAsyncTestZoneSpec.resetDate(); + } + } + }; + FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) { + if (!this.macroTaskOptions) { + return null; + } + for (var i = 0; i < this.macroTaskOptions.length; i++) { + var macroTaskOption = this.macroTaskOptions[i]; + if (macroTaskOption.source === task.source) { + return macroTaskOption; + } + } + return null; + }; FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { this._lastError = error; return false; // Don't propagate error to parent zone. @@ -620,7 +1028,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * This is useful in tests. For example to see which tasks are preventing a test from completing * or an automated way of releasing all of the event listeners at the end of the test. */ -var TaskTrackingZoneSpec = (function () { +var TaskTrackingZoneSpec = /** @class */ (function () { function TaskTrackingZoneSpec() { this.name = 'TaskTrackingZone'; this.microTasks = []; @@ -688,6 +1096,10 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +/** + * @fileoverview + * @suppress {missingRequire} + */ (function (global) { // Detect and setup WTF. var wtfTrace = null; @@ -703,7 +1115,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } return false; })(); - var WtfZoneSpec = (function () { + var WtfZoneSpec = /** @class */ (function () { function WtfZoneSpec() { this.name = 'WTF'; } @@ -754,14 +1166,13 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; instance(zonePathName(targetZone), shallowObj(task.data, 2)); return retValue; }; - + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; return WtfZoneSpec; }()); - WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); - WtfZoneSpec.scheduleInstance = {}; - WtfZoneSpec.cancelInstance = {}; - WtfZoneSpec.invokeScope = {}; - WtfZoneSpec.invokeTaskScope = {}; function shallowObj(obj, depth) { if (!depth) return null; @@ -802,7 +1213,6 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -'use strict'; (function (context) { var Mocha = context.Mocha; if (typeof Mocha === 'undefined') { @@ -927,11 +1337,14 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; }; Mocha.Runner.prototype.run = function (fn) { this.on('test', function (e) { - if (Zone.current !== rootZone) { - throw new Error('Unexpected zone: ' + Zone.current.name); - } testZone = rootZone.fork(new ProxyZoneSpec()); }); + this.on('fail', function (test, err) { + var proxyZoneSpec = testZone && testZone.get('ProxyZoneSpec'); + if (proxyZoneSpec && err) { + err.message += proxyZoneSpec.getAndClearPendingTasksInfo(); + } + }); return originalRun.call(this, fn); }; })(Mocha.Runner.prototype.runTest, Mocha.Runner.prototype.run);