From f74f38ae328fc94126f7d46251cc178057ea8131 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Mon, 23 Oct 2017 07:31:21 -0700 Subject: [PATCH 01/12] feat(testing): support TestBed and export jasmine/mocha zone scripts - update zone build and add jasmine/mocha test scripts, see: https://github.com/NativeScript/zone.js/pull/1 - refactor testing and zone-js modules to work with each other - add generous TestBed setup helpers in testing/src/util.ts - add single import test helpers for zone-js patches in zone-js/testing.[framework].ts files - update peer deps to reflect updated zone (could peer dep be dropped since prebuilts are exported?) chore: disable most tests and enable ones that use TestApp one-by-one - add test entry point script that inits the test environment for TestBed - list view, modal dialog pass - detached-loader and platform-filter-component could use feedback. see todos chore: replace the remaining TestApp usages in test suite - xdescribe the failing tests. - I think the remaining problems boil down to `dumpView` indicating the ComponentFixture comes back with the root components, and `@ViewChild` not finding DetachedLoader by its class. - remove some duplication in testing utilities chore: cleanup and remove some diff noise from a few tests chore: remove more test noise - are the line-endings different on this file? :( chore: convince dumpView and TestComponentRenderer to agree on things - all the TestBed tests except for the DetachedLoader ones and a single Renderer lifecycle are passing. - update NativeScriptRenderer.selectRootElement to find views by ID when given a selector of an id string. TestBed uses this when creating componentRefs to get at the correct views. - change NativeScriptTestComponentRenderer to inject only a ProxyViewContainer which mimics what TestApp did. - update dumpView to strip off the new "source" data attached to a view.toString() result. chore: cleanup lint chore: make nTestBed helpers automatically clean up test components - before the components were destroyed by TestBed, but not removed from the rootView. - maintain a list of active fixtures for a set of tests, and remove them all when the tests complete. - reorder to the testing utils to flow better when reading (start with test init, then before/after then render components) - clean up some lint. chore: fix issue where nTestBedBeforeEach overwrote its own imports - Fixes the DetachedLoader tests, and makes them MUCH simpler. - When you configure the test bed module, you need to specify a full list of imports, because they completely overwrite the imports array that is used. - That's yet another reason to use the provided helper functions, they merge in the common {N} imports for you. - ... and some lint cleanup chore: make renderer lifecyle test more robust - the first assertion is that the view after init has been called. rather than assert it, just wait for it using an observable and avoid asserting about timing and implementation specific details of the system. Specifically this removes the assumption that `app.tick()` will advance time and call `ngAfterViewInit` on the component. chore: cleanup from review - re-enable all tests in karma.conf.js - remove some diff noise --- .gitignore | 2 +- nativescript-angular/renderer.ts | 6 +- nativescript-angular/testing/index.ts | 29 + .../nativescript_test_component_renderer.ts | 24 + nativescript-angular/testing/src/util.ts | 165 ++ nativescript-angular/zone-js/README.md | 10 + .../zone-js/dist/zone-nativescript.jasmine.js | 1442 ++++++++++++++++ .../zone-js/dist/zone-nativescript.js | 1258 +++----------- .../zone-js/dist/zone-nativescript.mocha.js | 1458 +++++++++++++++++ .../zone-js/testing.jasmine.ts | 3 + nativescript-angular/zone-js/testing.mocha.ts | 2 + tests/app/snippets/navigation/page-outlet.ts | 2 +- .../app/snippets/navigation/router-outlet.ts | 2 +- tests/app/tests/detached-loader-tests.ts | 93 +- tests/app/tests/list-view-tests.ts | 56 +- tests/app/tests/modal-dialog.ts | 74 +- tests/app/tests/platform-filter-components.ts | 224 ++- tests/app/tests/renderer-tests.ts | 248 +-- tests/app/tests/snippets.ts | 25 +- tests/app/tests/test-main.ts | 3 + tests/app/tests/test-utils.ts | 94 +- tests/app/tests/third-party.ts | 30 +- tests/karma.conf.js | 3 +- 23 files changed, 3732 insertions(+), 1521 deletions(-) create mode 100644 nativescript-angular/testing/index.ts create mode 100644 nativescript-angular/testing/src/nativescript_test_component_renderer.ts create mode 100644 nativescript-angular/testing/src/util.ts create mode 100644 nativescript-angular/zone-js/README.md create mode 100644 nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js create mode 100644 nativescript-angular/zone-js/dist/zone-nativescript.mocha.js create mode 100644 nativescript-angular/zone-js/testing.jasmine.ts create mode 100644 nativescript-angular/zone-js/testing.mocha.ts create mode 100644 tests/app/tests/test-main.ts diff --git a/.gitignore b/.gitignore index 7b21aa525..83f0e2ce0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ tags !/nativescript-angular/postinstall.js !/nativescript-angular/hooks/**/*.js !/nativescript-angular/gulpfile.js -!/nativescript-angular/zone-js/**/*.js +!/nativescript-angular/zone-js/dist/*.js .tscache .nvm diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 405f1c91d..4330e58a5 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -5,7 +5,7 @@ import { } from "@angular/core"; import { Device } from "tns-core-modules/platform"; -import { View } from "tns-core-modules/ui/core/view"; +import { View, getViewById } from "tns-core-modules/ui/core/view"; import { addCss } from "tns-core-modules/application"; import { topmost } from "tns-core-modules/ui/frame"; import { profile } from "tns-core-modules/profiling"; @@ -110,6 +110,10 @@ export class NativeScriptRenderer extends Renderer2 { @profile selectRootElement(selector: string): NgView { traceLog("NativeScriptRenderer.selectRootElement: " + selector); + if (selector && selector[0] === "#") { + const result = getViewById(this.rootView, selector.slice(1)); + return (result || this.rootView) as NgView; + } return this.rootView; } diff --git a/nativescript-angular/testing/index.ts b/nativescript-angular/testing/index.ts new file mode 100644 index 000000000..d8d00bbe6 --- /dev/null +++ b/nativescript-angular/testing/index.ts @@ -0,0 +1,29 @@ +import { NgModule } from "@angular/core"; +import { TestComponentRenderer } from "@angular/core/testing"; +import { NativeScriptTestComponentRenderer } from "./src/nativescript_test_component_renderer"; +import { COMMON_PROVIDERS } from "../platform-common"; +import { APP_ROOT_VIEW } from "../platform-providers"; +import { testingRootView } from "./src/util"; +export * from "./src/util"; + +/** + * Providers array is exported for cases where a custom module has to be constructed + * to test a particular piece of code. This can happen, for example, if you are trying + * to test dynamic component loading and need to specify an entryComponent for the testing + * module. + */ +export const NATIVESCRIPT_TESTING_PROVIDERS: any[] = [ + COMMON_PROVIDERS, + {provide: APP_ROOT_VIEW, useFactory: testingRootView}, + {provide: TestComponentRenderer, useClass: NativeScriptTestComponentRenderer}, +]; + +/** + * NativeScript testing support module. Enables use of TestBed for angular components, directives, + * pipes, and services. + */ +@NgModule({ + providers: NATIVESCRIPT_TESTING_PROVIDERS +}) +export class NativeScriptTestingModule { +} diff --git a/nativescript-angular/testing/src/nativescript_test_component_renderer.ts b/nativescript-angular/testing/src/nativescript_test_component_renderer.ts new file mode 100644 index 000000000..5cac9a488 --- /dev/null +++ b/nativescript-angular/testing/src/nativescript_test_component_renderer.ts @@ -0,0 +1,24 @@ +import { Injectable } from "@angular/core"; +import { TestComponentRenderer } from "@angular/core/testing"; +import { topmost } from "tns-core-modules/ui/frame"; +import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; +import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; + +/** + * A NativeScript based implementation of the TestComponentRenderer. + */ +@Injectable() +export class NativeScriptTestComponentRenderer extends TestComponentRenderer { + + insertRootElement(rootElId: string) { + const page = topmost().currentPage; + + const layout = new ProxyViewContainer(); + layout.id = rootElId; + + const rootLayout = page.layoutView as LayoutBase; + rootLayout.addChild(layout); + } + +} + diff --git a/nativescript-angular/testing/src/util.ts b/nativescript-angular/testing/src/util.ts new file mode 100644 index 000000000..9c616caf0 --- /dev/null +++ b/nativescript-angular/testing/src/util.ts @@ -0,0 +1,165 @@ + +import { View } from "tns-core-modules/ui/core/view"; +import { topmost } from "tns-core-modules/ui/frame"; +import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { NgModule, Type } from "@angular/core"; +import { NativeScriptModule } from "../../nativescript.module"; +import { platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; +import { NS_COMPILER_PROVIDERS } from "../../platform"; +import { NATIVESCRIPT_TESTING_PROVIDERS, NativeScriptTestingModule } from "../index"; +import { CommonModule } from "@angular/common"; +/** + * Get a reference to the root application view. + */ +export function testingRootView(): View { + return topmost().currentPage.content; +} + +/** + * Declared test contexts. When the suite is done this map should be empty if all lifecycle + * calls have happened as expected. + * @private + */ +const activeTestFixtures: ComponentFixture[][] = []; + +/** + * Return a promise that resolves after (durationMs) milliseconds + */ +export function promiseWait(durationMs: number) { + return () => new Promise((resolve) => setTimeout(() => resolve(), durationMs)); +} + +/** + * Perform basic TestBed environment initialization. Call this once in the main entry point to your tests. + */ +export function nTestBedInit() { + TestBed.initTestEnvironment( + NativeScriptTestingModule, + platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) + ); +} + +/** + * Helper for configuring a TestBed instance for rendering components for test. Ideally this + * would not be needed, and in truth it's just a wrapper to eliminate some boilerplate. It + * exists because when you need to specify `entryComponents` for a test the setup becomes quite + * a bit more complex than if you're just doing a basic component test. + * + * More about entryComponents complexity: https://github.com/angular/angular/issues/12079 + * + * Use: + * ``` + * beforeEach(nTestBedBeforeEach([MyComponent,MyFailComponent])); + * ``` + * + * **NOTE*** Remember to pair with {@see nTestBedAfterEach} + * + * @param components Any components that you will create during the test + * @param providers Any services your tests depend on + * @param imports Any module imports your tests depend on + * @param entryComponents Any entry components that your tests depend on + */ +export function nTestBedBeforeEach( + components: any[], + providers: any[] = [], + imports: any[] = [], + entryComponents: any[] = []) { + return (done) => { + activeTestFixtures.push([]); + // If there are no entry components we can take the simple path. + if (entryComponents.length === 0) { + TestBed.configureTestingModule({ + declarations: [...components], + providers: [...providers], + imports: [NativeScriptModule, ...imports] + }); + } else { + // If there are entry components, we have to reset the testing platform. + // + // There's got to be a better way... (o_O) + TestBed.resetTestEnvironment(); + @NgModule({ + declarations: entryComponents, + exports: entryComponents, + entryComponents: entryComponents + }) + class EntryComponentsTestModule { + } + TestBed.initTestEnvironment( + EntryComponentsTestModule, + platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) + ); + TestBed.configureTestingModule({ + declarations: components, + imports: [ + NativeScriptModule, NativeScriptTestingModule, CommonModule, + ...imports + ], + providers: [...providers, ...NATIVESCRIPT_TESTING_PROVIDERS], + }); + } + TestBed.compileComponents() + .then(() => done()) + .catch((e) => { + console.log(`Failed to instantiate test component with error: ${e}`); + console.log(e.stack); + done(); + }); + }; +} + +/** + * Helper for a basic component TestBed clean up. + * @param resetEnv When true the testing environment will be reset + * @param resetFn When resetting the environment, use this init function + */ +export function nTestBedAfterEach(resetEnv = true, resetFn = nTestBedInit) { + return () => { + if (activeTestFixtures.length === 0) { + throw new Error( + `There are no more declared fixtures.` + + `Did you call "nTestBedBeforeEach" and "nTestBedAfterEach" an equal number of times?` + ); + } + const root = testingRootView() as LayoutBase; + const fixtures = activeTestFixtures.pop(); + fixtures.forEach((fixture) => { + root.removeChild(fixture.nativeElement); + fixture.destroy(); + }); + TestBed.resetTestingModule(); + if (resetEnv) { + TestBed.resetTestEnvironment(); + resetFn(); + } + }; +} + +/** + * Render a component using the TestBed helper, and return a promise that resolves when the + * ComponentFixture is fully initialized. + */ +export function nTestBedRender(componentType: Type): Promise> { + const fixture = TestBed.createComponent(componentType); + fixture.detectChanges(); + return fixture.whenRenderingDone() + // TODO(jd): it seems that the whenStable and whenRenderingDone utilities of ComponentFixture + // do not work as expected. I looked at how to fix it and it's not clear how to provide + // a {N} specific subclass, because ComponentFixture is newed directly rather than injected + // What to do about it? Maybe fakeAsync can help? For now just setTimeout for 100ms (x_X) + .then(promiseWait(100)) + .then(() => { + const list = activeTestFixtures[activeTestFixtures.length - 1]; + if (!list) { + console.warn( + "nTestBedRender called without nTestBedBeforeEach/nTestBedAfter each. " + + "You are responsible for calling 'fixture.destroy()' when your test is done " + + "in order to clean up the components that are created." + ); + } else { + list.push(fixture); + } + return fixture; + }); +} diff --git a/nativescript-angular/zone-js/README.md b/nativescript-angular/zone-js/README.md new file mode 100644 index 000000000..38a0ebc26 --- /dev/null +++ b/nativescript-angular/zone-js/README.md @@ -0,0 +1,10 @@ +Zone.js for NativeScript +--- + +Zone.js is a library that aims to intercept all asynchronous API calls made in an environment, in order +to wrap them into coherent execution contexts over time. + +NativeScript executes inside an environment that Zone.js is not designed to work in, so a custom Zone.js output +must be created. + +Find out more about this in the [Upgrading Zone.js document](../../doc/upgrading-zonejs.md) \ No newline at end of file diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js new file mode 100644 index 000000000..a249f76d2 --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js @@ -0,0 +1,1442 @@ +/** +* @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 +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + +/** + * @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 + */ +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + 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.apply(this, [e]); + } + } + catch (err) { + } + } + function isThenable(value) { + return value && value.then; + } + function forwardResolution(value) { + return value; + } + function forwardRejection(rejection) { + return ZoneAwarePromise.reject(rejection); + } + var symbolState = __symbol__('state'); + var symbolValue = __symbol__('value'); + var source = 'Promise.then'; + var UNRESOLVED = null; + var RESOLVED = true; + var REJECTED = false; + var REJECTED_NO_CATCH = 0; + function makeResolver(promise, state) { + return function (v) { + try { + resolvePromise(promise, state, v); + } + catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + }; + } + var once = function () { + var wasCalled = false; + return function wrapper(wrappedFunction) { + return function () { + if (wasCalled) { + return; + } + wasCalled = true; + wrappedFunction.apply(null, arguments); + }; + }; + }; + var TYPE_ERROR = 'Promise resolved with itself'; + var OBJECT = 'object'; + var FUNCTION = 'function'; + var CURRENT_TASK_SYMBOL = __symbol__('currentTask'); + // Promise Resolution + function resolvePromise(promise, state, value) { + var onceWrapper = once(); + if (promise === value) { + throw new TypeError(TYPE_ERROR); + } + if (promise[symbolState] === UNRESOLVED) { + // should only get value.then once based on promise spec. + var then = null; + try { + if (typeof value === OBJECT || typeof value === FUNCTION) { + then = value && value.then; + } + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + return promise; + } + // if (value instanceof ZoneAwarePromise) { + if (state !== REJECTED && value instanceof ZoneAwarePromise && + value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && + value[symbolState] !== UNRESOLVED) { + clearRejectedNoCatch(value); + resolvePromise(promise, value[symbolState], value[symbolValue]); + } + else if (state !== REJECTED && typeof then === FUNCTION) { + try { + then.apply(value, [ + onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) + ]); + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + } + } + else { + promise[symbolState] = state; + var queue = promise[symbolValue]; + promise[symbolValue] = value; + // 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[CURRENT_TASK_SYMBOL] = Zone.currentTask; + } + for (var i = 0; i < queue.length;) { + scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); + } + if (queue.length == 0 && state == REJECTED) { + promise[symbolState] = REJECTED_NO_CATCH; + try { + throw new Error('Uncaught (in promise): ' + value + + (value && value.stack ? '\n' + value.stack : '')); + } + catch (err) { + var error_1 = err; + error_1.rejection = value; + error_1.promise = promise; + error_1.zone = Zone.current; + error_1.task = Zone.currentTask; + _uncaughtPromiseErrors.push(error_1); + api.scheduleMicroTask(); // to make sure that it is running + } + } + } + } + // 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 + // and queue.length > 0, means there is a error handler + // here to handle the rejected promise, we should trigger + // windows.rejectionhandled eventHandler or nodejs rejectionHandled + // eventHandler + try { + var handler = Zone[REJECTION_HANDLED_HANDLER]; + if (handler && typeof handler === FUNCTION) { + handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + } + } + catch (err) { + } + promise[symbolState] = REJECTED; + for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { + if (promise === _uncaughtPromiseErrors[i].promise) { + _uncaughtPromiseErrors.splice(i, 1); + } + } + } + } + function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { + clearRejectedNoCatch(promise); + var delegate = promise[symbolState] ? + (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : + (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; + zone.scheduleMicroTask(source, function () { + try { + resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + } + catch (error) { + resolvePromise(chainPromise, false, error); + } + }); + } + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = (function () { + function ZoneAwarePromise(executor) { + var promise = this; + if (!(promise instanceof ZoneAwarePromise)) { + throw new Error('Must be an instanceof Promise.'); + } + promise[symbolState] = UNRESOLVED; + promise[symbolValue] = []; // queue; + try { + executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); + } + catch (error) { + resolvePromise(promise, false, error); + } + } + ZoneAwarePromise.toString = function () { + return ZONE_AWARE_PROMISE_TO_STRING; + }; + ZoneAwarePromise.resolve = function (value) { + return resolvePromise(new this(null), RESOLVED, value); + }; + ZoneAwarePromise.reject = function (error) { + return resolvePromise(new this(null), REJECTED, error); + }; + ZoneAwarePromise.race = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + _a = [res, rej], resolve = _a[0], reject = _a[1]; + var _a; + }); + function onResolve(value) { + promise && (promise = null || resolve(value)); + } + 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); + } + value.then(onResolve, onReject); + } + return promise; + }; + ZoneAwarePromise.all = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + resolve = res; + reject = rej; + }); + 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); + } + }; })(count), reject); + count++; + } + if (!count) + resolve(resolvedValues); + return promise; + }; + ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { + var chainPromise = new this.constructor(null); + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); + } + return chainPromise; + }; + ZoneAwarePromise.prototype.catch = function (onRejected) { + return this.then(null, onRejected); + }; + return ZoneAwarePromise; + }()); + // Protect against aggressive optimizers dropping seemingly unused properties. + // E.g. Closure Compiler in advanced mode. + ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; + ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; + ZoneAwarePromise['race'] = ZoneAwarePromise.race; + ZoneAwarePromise['all'] = ZoneAwarePromise.all; + var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + var desc = Object.getOwnPropertyDescriptor(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); + } + }; + Object.defineProperty(global, 'Promise', desc); + } + global['Promise'] = ZoneAwarePromise; + var symbolThenPatched = __symbol__('thenPatched'); + function patchThen(Ctor) { + var proto = Ctor.prototype; + var originalThen = proto.then; + // Keep a reference to the original method. + proto[symbolThen] = originalThen; + // check Ctor.prototype.then propertyDescritor is writable or not + // in meteor env, writable is false, we have to make it to be true. + var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); + if (prop && prop.writable === false && prop.configurable) { + Object.defineProperty(Ctor.prototype, 'then', { writable: true }); + } + Ctor.prototype.then = function (onResolve, onReject) { + var _this = this; + var wrapped = new ZoneAwarePromise(function (resolve, reject) { + originalThen.call(_this, resolve, reject); + }); + return wrapped.then(onResolve, onReject); + }; + Ctor[symbolThenPatched] = true; + } + function zoneify(fn) { + return function () { + var resultPromise = fn.apply(this, arguments); + if (resultPromise instanceof ZoneAwarePromise) { + return resultPromise; + } + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); + } + return resultPromise; + }; + } + if (NativePromise) { + patchThen(NativePromise); + var fetch_1 = global['fetch']; + if (typeof fetch_1 == FUNCTION) { + global['fetch'] = zoneify(fetch_1); + } + } + // 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 + */ +/** + * @fileoverview + * @suppress {globalThis} + */ +var NEWLINE = '\n'; +var IGNORE_FRAMES = {}; +var creationTrace = '__creationTrace__'; +var ERROR_TAG = 'STACKTRACE TRACKING'; +var SEP_TAG = '__SEP_TAG__'; +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = (function () { + function LongStackTrace() { + this.error = getStacktrace(); + this.timestamp = new Date(); + } + return LongStackTrace; +}()); +function getStacktraceWithUncaughtError() { + return new Error(ERROR_TAG); +} +function getStacktraceWithCaughtError() { + try { + throw getStacktraceWithUncaughtError(); + } + catch (err) { + return err; + } +} +// Some implementations of exception handling don't create a stack trace if the exception +// isn't thrown, however it's faster not to actually throw the exception. +var error = getStacktraceWithUncaughtError(); +var caughtError = getStacktraceWithCaughtError(); +var getStacktrace = error.stack ? + getStacktraceWithUncaughtError : + (caughtError.stack ? getStacktraceWithCaughtError : getStacktraceWithUncaughtError); +function getFrames(error) { + return error.stack ? error.stack.split(NEWLINE) : []; +} +function addErrorStack(lines, error) { + var trace = getFrames(error); + for (var i = 0; i < trace.length; i++) { + var frame = trace[i]; + // Filter out the Frames which are part of stack capturing. + if (!IGNORE_FRAMES.hasOwnProperty(frame)) { + lines.push(trace[i]); + } + } +} +function renderLongStackTrace(frames, stack) { + var longTrace = [stack ? stack.trim() : '']; + if (frames) { + var timestamp = new Date().getTime(); + for (var i = 0; i < frames.length; i++) { + var traceFrames = frames[i]; + var lastTime = traceFrames.timestamp; + var separator = "____________________Elapsed " + (timestamp - lastTime.getTime()) + " ms; At: " + lastTime; + separator = separator.replace(/[^\w\d]/g, '_'); + longTrace.push(sepTemplate.replace(SEP_TAG, separator)); + addErrorStack(longTrace, traceFrames.error); + timestamp = lastTime.getTime(); + } + } + return longTrace.join(NEWLINE); +} +Zone['longStackTraceZoneSpec'] = { + name: 'long-stack-trace', + longStackTraceLimit: 10, + // add a getLongStackTrace method in spec to + // handle handled reject promise error. + getLongStackTrace: function (error) { + if (!error) { + return undefined; + } + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; + if (!trace) { + return error.stack; + } + return renderLongStackTrace(trace, error.stack); + }, + onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { + 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) { + 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); + } +}; +function captureStackTraces(stackTraces, count) { + if (count > 0) { + stackTraces.push(getFrames((new LongStackTrace()).error)); + captureStackTraces(stackTraces, count - 1); + } +} +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]; + 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; + } + } +} +computeIgnoreFrames(); + +/** + * @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 + */ +var ProxyZoneSpec = (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!this.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; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + else { + return parentZoneDelegate.fork(targetZone, zoneSpec); + } + }; + ProxyZoneSpec.prototype.onIntercept = function (parentZoneDelegate, currentZone, targetZone, delegate, source) { + if (this._delegateSpec && this._delegateSpec.onIntercept) { + return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source); + } + else { + return parentZoneDelegate.intercept(targetZone, delegate, source); + } + }; + ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + if (this._delegateSpec && this._delegateSpec.onInvoke) { + return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); + } + else { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + }; + ProxyZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + if (this._delegateSpec && this._delegateSpec.onHandleError) { + return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); + } + else { + return parentZoneDelegate.handleError(targetZone, error); + } + }; + ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onScheduleTask) { + return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.scheduleTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + if (this._delegateSpec && this._delegateSpec.onHasTask) { + this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); + } + else { + delegate.hasTask(target, hasTaskState); + } + }; + return ProxyZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['ProxyZoneSpec'] = ProxyZoneSpec; + +/** + * @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 + */ +var SyncTestZoneSpec = (function () { + function SyncTestZoneSpec(namePrefix) { + this.runZone = Zone.current; + this.name = 'syncTestZone for ' + namePrefix; + } + SyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + case 'macroTask': + throw new Error("Cannot call " + task.source + " from within a sync test."); + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + return SyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; + +/** + * @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 + */ +var AsyncTestZoneSpec = (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this.runZone = Zone.current; + this._finishCallback = finishCallback; + this._failCallback = failCallback; + this.name = 'asyncTestZone for ' + namePrefix; + } + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + // 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(); + } + }, 0); + }); + } + }; + // 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. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + 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._alreadyErrored = true; + } + return false; + }; + AsyncTestZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + delegate.hasTask(target, hasTaskState); + if (hasTaskState.change == 'microTask') { + this._pendingMicroTasks = hasTaskState.microTask; + this._finishCallbackIfDone(); + } + else if (hasTaskState.change == 'macroTask') { + this._pendingMacroTasks = hasTaskState.macroTask; + this._finishCallbackIfDone(); + } + }; + return AsyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; + +/** + * @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 + */ +(function (global) { + var Scheduler = (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 0; + // 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; + } + 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, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; + var i = 0; + for (; i < this._schedulerQueue.length; i++) { + var currentEntry = this._schedulerQueue[i]; + if (newEntry.endTime < currentEntry.endTime) { + break; + } + } + this._schedulerQueue.splice(i, 0, newEntry); + return currentId; + }; + Scheduler.prototype.removeScheduledFunctionWithId = function (id) { + for (var i = 0; i < this._schedulerQueue.length; i++) { + if (this._schedulerQueue[i].id == id) { + this._schedulerQueue.splice(i, 1); + break; + } + } + }; + 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) { + // Done processing the queue since it's sorted by endTime. + break; + } + 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. + break; + } + } + } + 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, trackPendingRequestAnimationFrame) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + throw new Error('The code should be running in the fakeAsync zone to call this function'); + } + }; + FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) { + var _this = this; + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + fn.apply(global, args); + if (_this._lastError === null) { + if (completers.onSuccess != null) { + completers.onSuccess.apply(global); + } + // Flush microtasks only on success. + _this.flushMicrotasks(); + } + else { + if (completers.onError != null) { + completers.onError.apply(global); + } + } + // Return true if there were no errors, false otherwise. + return _this._lastError === null; + }; + }; + FakeAsyncTestZoneSpec._removeTimer = function (timers, id) { + var index = timers.indexOf(id); + if (index > -1) { + timers.splice(index, 1); + } + }; + FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) { + var _this = this; + return function () { + // Requeue the timer callback if it's not been canceled. + if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); + } + }; + }; + FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); + }; + }; + 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, 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]; + } + 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, true); + this.pendingPeriodicTimers.push(id); + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () { + var error = this._lastError || this._uncaughtPromiseErrors[0]; + this._uncaughtPromiseErrors.length = 0; + this._lastError = null; + throw error; + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + }; + FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () { + var _this = this; + FakeAsyncTestZoneSpec.assertInZone(); + var flushErrors = function () { + if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) { + // If there is an error stop processing the microtask queue and rethrow the error. + _this._resetLastErrorAndThrow(); + } + }; + while (this._microtasks.length > 0) { + var microtask = this._microtasks.shift(); + 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': + 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 addtionalArgs = void 0; + if (args) { + var callbackIndex = task.data.callbackIndex; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: addtionalArgs, + 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']); + break; + case 'setInterval': + task.data['handleId'] = + this._setInterval(task.invoke, task.data['delay'], task.data['args']); + break; + case 'XMLHttpRequest.send': + 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: + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + 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: + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + return FakeAsyncTestZoneSpec; + }()); + // Export the class so that new instances can be created with proper + // constructor params. + Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec; +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @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 + */ +/** + * A `TaskTrackingZoneSpec` allows one to track all outstanding Tasks. + * + * 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 () { + function TaskTrackingZoneSpec() { + this.name = 'TaskTrackingZone'; + this.microTasks = []; + this.macroTasks = []; + this.eventTasks = []; + this.properties = { 'TaskTrackingZone': this }; + } + TaskTrackingZoneSpec.get = function () { + return Zone.current.get('TaskTrackingZone'); + }; + TaskTrackingZoneSpec.prototype.getTasksFor = function (type) { + switch (type) { + case 'microTask': + return this.microTasks; + case 'macroTask': + return this.macroTasks; + case 'eventTask': + return this.eventTasks; + } + throw new Error('Unknown task format: ' + type); + }; + TaskTrackingZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + task['creationLocation'] = new Error("Task '" + task.type + "' from '" + task.source + "'."); + var tasks = this.getTasksFor(task.type); + tasks.push(task); + return parentZoneDelegate.scheduleTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.cancelTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (task.type === 'eventTask') + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + }; + TaskTrackingZoneSpec.prototype.clearEvents = function () { + while (this.eventTasks.length) { + Zone.current.cancelTask(this.eventTasks[0]); + } + }; + return TaskTrackingZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; + +/** + * @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 {missingRequire} + */ +(function (global) { + // Detect and setup WTF. + var wtfTrace = null; + var wtfEvents = null; + var wtfEnabled = (function () { + var wtf = global['wtf']; + if (wtf) { + wtfTrace = wtf.trace; + if (wtfTrace) { + wtfEvents = wtfTrace.events; + return true; + } + } + return false; + })(); + var WtfZoneSpec = (function () { + function WtfZoneSpec() { + this.name = 'WTF'; + } + WtfZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + var retValue = parentZoneDelegate.fork(targetZone, zoneSpec); + WtfZoneSpec.forkInstance(zonePathName(targetZone), retValue.name); + return retValue; + }; + WtfZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + var scope = WtfZoneSpec.invokeScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeScope[source] = + wtfEvents.createScope("Zone:invoke:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)); + }; + WtfZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + return parentZoneDelegate.handleError(targetZone, error); + }; + WtfZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.type + ':' + task.source; + var instance = WtfZoneSpec.scheduleInstance[key]; + if (!instance) { + instance = WtfZoneSpec.scheduleInstance[key] = + wtfEvents.createInstance("Zone:schedule:" + key + "(ascii zone, any data)"); + } + var retValue = parentZoneDelegate.scheduleTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + WtfZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + var source = task.source; + var scope = WtfZoneSpec.invokeTaskScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeTaskScope[source] = + wtfEvents.createScope("Zone:invokeTask:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs)); + }; + WtfZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.source; + var instance = WtfZoneSpec.cancelInstance[key]; + if (!instance) { + instance = WtfZoneSpec.cancelInstance[key] = + wtfEvents.createInstance("Zone:cancel:" + key + "(ascii zone, any options)"); + } + var retValue = parentZoneDelegate.cancelTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + + 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; + var out = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = obj[key]; + switch (typeof value) { + case 'object': + var name_1 = value && value.constructor && value.constructor.name; + value = name_1 == Object.name ? shallowObj(value, depth - 1) : name_1; + break; + case 'function': + value = value.name || undefined; + break; + } + out[key] = value; + } + } + return out; + } + function zonePathName(zone) { + var name = zone.name; + zone = zone.parent; + while (zone != null) { + name = zone.name + '::' + name; + zone = zone.parent; + } + return name; + } + Zone['wtfZoneSpec'] = !wtfEnabled ? null : new WtfZoneSpec(); +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @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 + */ +'use strict'; +(function () { + var __extends = function (d, b) { + for (var p in b) + if (b.hasOwnProperty(p)) + d[p] = b[p]; + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + // 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) + throw new Error('Missing: zone.js'); + if (typeof jasmine == 'undefined') + throw new Error('Missing: jasmine.js'); + if (jasmine['__zone_patch__']) + throw new Error('\'jasmine\' has already been patched with \'Zone\'.'); + jasmine['__zone_patch__'] = true; + var SyncTestZoneSpec = Zone['SyncTestZoneSpec']; + var ProxyZoneSpec = Zone['ProxyZoneSpec']; + if (!SyncTestZoneSpec) + throw new Error('Missing: SyncTestZoneSpec'); + if (!ProxyZoneSpec) + throw new Error('Missing: ProxyZoneSpec'); + var ambientZone = Zone.current; + // Create a synchronous-only zone in which to run `describe` blocks in order to raise an + // 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 escape the fakeAsync rules. + // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add + // fakeAsync behavior to the childZone. + var testProxyZone = null; + // 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) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (description, specDefinitions) { + return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions)); + }; + }); + ['it', 'xit', 'fit'].forEach(function (methodName) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (description, specDefinitions, timeout) { + arguments[1] = wrapTestInZone(specDefinitions); + return originalJasmineFn.apply(this, arguments); + }; + }); + ['beforeEach', 'afterEach'].forEach(function (methodName) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (specDefinitions, timeout) { + arguments[0] = wrapTestInZone(specDefinitions); + return originalJasmineFn.apply(this, arguments); + }; + }); + /** + * Gets a function wrapping the body of a Jasmine `describe` block to execute in a + * synchronous-only zone. + */ + function wrapDescribeInZone(describeBody) { + return function () { + return syncZone.run(describeBody, this, arguments); + }; + } + /** + * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to + * execute in a ProxyZone zone. + * This will run in `testProxyZone`. The `testProxyZone` will be reset by the `ZoneQueueRunner` + */ + function wrapTestInZone(testBody) { + // 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]); + } : function () { + return testProxyZone.run(testBody, this); + }); + } + var QueueRunner = jasmine.QueueRunner; + jasmine.QueueRunner = (function (_super) { + __extends(ZoneQueueRunner, _super); + function ZoneQueueRunner(attrs) { + attrs.onComplete = (function (fn) { return function () { + // All functions are done, clear the test zone. + testProxyZone = null; + ambientZone.scheduleMicroTask('jasmine.onComplete', fn); + }; })(attrs.onComplete); + _super.call(this, attrs); + } + ZoneQueueRunner.prototype.execute = function () { + var _this = this; + if (Zone.current !== ambientZone) + throw new Error('Unexpected Zone: ' + Zone.current.name); + testProxyZone = ambientZone.fork(new ProxyZoneSpec()); + 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 + // addEventListener callback would think that it is the top most task and would + // drain the microtask queue on element.click() which would be incorrect. + // For this reason we always force a task when running jasmine tests. + Zone.current.scheduleMicroTask('jasmine.execute().forceTask', function () { return QueueRunner.prototype.execute.call(_this); }); + } + else { + _super.prototype.execute.call(this); + } + }; + return ZoneQueueRunner; + }(QueueRunner)); +})(); + +}))); diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.js b/nativescript-angular/zone-js/dist/zone-nativescript.js index 38616df9f..5d79bf336 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.js @@ -19,12 +19,18 @@ * 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 () { function Zone(parent, zoneSpec) { this._properties = null; @@ -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 ' + @@ -70,6 +76,17 @@ var Zone$1 = (function (global) { 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; @@ -107,7 +124,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 +137,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 +149,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 +165,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; @@ -418,6 +445,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,12 +457,11 @@ 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); } }; @@ -453,20 +480,31 @@ 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--; - } - }; + if (type === eventTask && options && options.isUsingGlobalCallback) { + this.invoke = ZoneTask.invokeTask; + } + else { + this.invoke = function () { + return ZoneTask.invokeTask.apply(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,7 +550,6 @@ 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, @@ -523,63 +560,35 @@ var Zone$1 = (function (global) { }; 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,731 +602,46 @@ 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(); - } - } + var showError = !Zone[__symbol__('ignoreConsoleErrorUncaughtError')]; + _api.microtaskDrainDone(); _isDrainingMicrotaskQueue = false; } } - Zone.drainMicroTaskQueue = drainMicroTaskQueue; - function isThenable(value) { - return value && value.then; - } - function forwardResolution(value) { - return value; - } - function forwardRejection(rejection) { - return ZoneAwarePromise.reject(rejection); - } - var symbolState = __symbol__('state'); - var symbolValue = __symbol__('value'); - var source = 'Promise.then'; - var UNRESOLVED = null; - var RESOLVED = true; - var REJECTED = false; - var REJECTED_NO_CATCH = 0; - function makeResolver(promise, state) { - return function (v) { - try { - resolvePromise(promise, state, v); - } - catch (err) { - resolvePromise(promise, false, err); - } - // Do not return value or you will break the Promise spec. - }; - } - var once = function () { - var wasCalled = false; - return function wrapper(wrappedFunction) { - return function () { - if (wasCalled) { - return; - } - wasCalled = true; - wrappedFunction.apply(null, arguments); - }; - }; - }; - // Promise Resolution - function resolvePromise(promise, state, value) { - var onceWrapper = once(); - if (promise === value) { - throw new TypeError('Promise resolved with itself'); - } - if (promise[symbolState] === UNRESOLVED) { - // should only get value.then once based on promise spec. - var then = null; - try { - if (typeof value === 'object' || typeof value === 'function') { - then = value && value.then; - } - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - return promise; - } - // if (value instanceof ZoneAwarePromise) { - if (state !== REJECTED && value instanceof ZoneAwarePromise && - value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && - value[symbolState] !== UNRESOLVED) { - clearRejectedNoCatch(value); - resolvePromise(promise, value[symbolState], value[symbolValue]); - } - else if (state !== REJECTED && typeof then === 'function') { - try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - } - } - else { - promise[symbolState] = state; - var queue = promise[symbolValue]; - promise[symbolValue] = value; - // 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; - } - for (var i = 0; i < queue.length;) { - scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); - } - if (queue.length == 0 && state == REJECTED) { - promise[symbolState] = REJECTED_NO_CATCH; - try { - throw new Error('Uncaught (in promise): ' + value + - (value && value.stack ? '\n' + value.stack : '')); - } - catch (err) { - var error_1 = err; - error_1.rejection = value; - error_1.promise = promise; - error_1.zone = Zone.current; - error_1.task = Zone.currentTask; - _uncaughtPromiseErrors.push(error_1); - scheduleQueueDrain(); - } - } - } - } - // Resolving an already resolved promise is a noop. - return promise; - } - function clearRejectedNoCatch(promise) { - if (promise[symbolState] === REJECTED_NO_CATCH) { - // if the promise is rejected no catch status - // and queue.length > 0, means there is a error handler - // here to handle the rejected promise, we should trigger - // windows.rejectionhandled eventHandler or nodejs rejectionHandled - // eventHandler - try { - var handler = Zone[__symbol__('rejectionHandledHandler')]; - if (handler && typeof handler === 'function') { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); - } - } - catch (err) { - } - promise[symbolState] = REJECTED; - for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { - if (promise === _uncaughtPromiseErrors[i].promise) { - _uncaughtPromiseErrors.splice(i, 1); - } - } - } - } - function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { - clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? - (typeof onFulfilled === 'function') ? onFulfilled : forwardResolution : - (typeof onRejected === 'function') ? onRejected : forwardRejection; - zone.scheduleMicroTask(source, function () { - try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); - } - catch (error) { - resolvePromise(chainPromise, false, error); - } - }); - } - var ZoneAwarePromise = (function () { - function ZoneAwarePromise(executor) { - var promise = this; - if (!(promise instanceof ZoneAwarePromise)) { - throw new Error('Must be an instanceof Promise.'); - } - promise[symbolState] = UNRESOLVED; - promise[symbolValue] = []; // queue; - try { - executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); - } - catch (error) { - resolvePromise(promise, false, error); - } - } - ZoneAwarePromise.toString = function () { - return 'function ZoneAwarePromise() { [native code] }'; - }; - ZoneAwarePromise.resolve = function (value) { - return resolvePromise(new this(null), RESOLVED, value); - }; - ZoneAwarePromise.reject = function (error) { - return resolvePromise(new this(null), REJECTED, error); - }; - ZoneAwarePromise.race = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - _a = [res, rej], resolve = _a[0], reject = _a[1]; - var _a; - }); - function onResolve(value) { - promise && (promise = null || resolve(value)); - } - 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); - } - value.then(onResolve, onReject); - } - return promise; - }; - ZoneAwarePromise.all = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - 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); - } - }; })(count), reject); - count++; - } - if (!count) - resolve(resolvedValues); - return promise; - }; - ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { - var chainPromise = new this.constructor(null); - var zone = Zone.current; - if (this[symbolState] == UNRESOLVED) { - this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); - } - else { - scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); - } - return chainPromise; - }; - ZoneAwarePromise.prototype.catch = function (onRejected) { - return this.then(null, onRejected); - }; - return ZoneAwarePromise; - }()); - // Protect against aggressive optimizers dropping seemingly unused properties. - // E.g. Closure Compiler in advanced mode. - ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; - ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; - ZoneAwarePromise['race'] = ZoneAwarePromise.race; - ZoneAwarePromise['all'] = ZoneAwarePromise.all; - var NativePromise = global[symbolPromise] = global['Promise']; - global['Promise'] = ZoneAwarePromise; - var symbolThenPatched = __symbol__('thenPatched'); - function patchThen(Ctor) { - var proto = Ctor.prototype; - var originalThen = proto.then; - // Keep a reference to the original method. - proto[symbolThen] = originalThen; - Ctor.prototype.then = function (onResolve, onReject) { - var _this = this; - var wrapped = new ZoneAwarePromise(function (resolve, reject) { - originalThen.call(_this, resolve, reject); - }); - return wrapped.then(onResolve, onReject); - }; - Ctor[symbolThenPatched] = true; - } - function zoneify(fn) { - return function () { - var resultPromise = fn.apply(this, arguments); - if (resultPromise instanceof ZoneAwarePromise) { - return resultPromise; - } - var Ctor = resultPromise.constructor; - if (!Ctor[symbolThenPatched]) { - patchThen(Ctor); - } - return resultPromise; - }; - } - if (NativePromise) { - patchThen(NativePromise); - var fetch_1 = global['fetch']; - if (typeof fetch_1 == 'function') { - global['fetch'] = zoneify(fetch_1); - } - } - // This is not part of public API, but it is usefull for tests, so we expose it. - Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; - /* - * 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; - // 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 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() { - // 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 - try { - throw error; - } - catch (err) { - error = err; - } - } - // 1. attach zone information to stack frame - // 2. remove zone internal stack frames - attachZoneAndRemoveInternalZoneFrames(error); - return error; - } - // Copy the prototype so that instanceof operator works as expected - ZoneAwareError.prototype = NativeError.prototype; - ZoneAwareError[Zone.__symbol__('blacklistedStackFrames')] = blackListedStackFrames; - ZoneAwareError[stackRewrite] = false; - // those properties need special handling - var specialPropertyNames = ['stackTraceLimit', 'captureStackTrace', 'prepareStackTrace']; - // those properties of NativeError should be set to ZoneAwareError - var nativeErrorProperties = Object.keys(NativeError); - if (nativeErrorProperties) { - nativeErrorProperties.forEach(function (prop) { - if (specialPropertyNames.filter(function (sp) { return sp === prop; }).length === 0) { - Object.defineProperty(ZoneAwareError, prop, { - get: function () { - return NativeError[prop]; - }, - set: function (value) { - NativeError[prop] = value; - } - }); - } - }); - } - if (NativeError.hasOwnProperty('stackTraceLimit')) { - // Extend default stack limit as we will be removing few frames. - NativeError.stackTraceLimit = Math.max(NativeError.stackTraceLimit, 15); - // make sure that ZoneAwareError has the same property which forwards to NativeError. - Object.defineProperty(ZoneAwareError, 'stackTraceLimit', { - get: function () { - return NativeError.stackTraceLimit; - }, - set: function (value) { - return NativeError.stackTraceLimit = value; - } - }); - } - if (NativeError.hasOwnProperty('captureStackTrace')) { - Object.defineProperty(ZoneAwareError, 'captureStackTrace', { - // add named function here because we need to remove this - // stack frame when prepareStackTrace below - value: function zoneCaptureStackTrace(targetObject, constructorOpt) { - NativeError.captureStackTrace(targetObject, constructorOpt); - } - }); - } - Object.defineProperty(ZoneAwareError, 'prepareStackTrace', { - get: function () { - return NativeError.prepareStackTrace; - }, - set: function (value) { - if (!value || typeof value !== 'function') { - return NativeError.prepareStackTrace = value; - } - return NativeError.prepareStackTrace = function (error, structuredStackTrace) { - // remove additional stack information from ZoneAwareError.captureStackTrace - if (structuredStackTrace) { - for (var i = 0; i < structuredStackTrace.length; i++) { - var st = structuredStackTrace[i]; - // remove the first function which name is zoneCaptureStackTrace - if (st.getFunctionName() === 'zoneCaptureStackTrace') { - structuredStackTrace.splice(i, 1); - break; - } - } - } - return value.apply(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 - // the execution through all of the above methods so that we can look at the stack trace and - // find the frames of interest. - 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); + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// 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; }, + setNativePromise: function (NativePromise) { + nativeMicroTaskQueuePromise = NativePromise.resolve(0); }, - onHandleError: function (parentZD, current, target, error) { - if (error.originalStack && Error === ZoneAwareError) { - var frames_2 = error.originalStack.split(/\n/); - var runFrame = false, runGuardedFrame = false, runTaskFrame = false; - while (frames_2.length) { - 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) - 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 - // Chrome: at Zone.run (zone.js:100) - // 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); - } - if (fnName.indexOf('runGuarded') !== -1) { - runGuardedFrame = true; - } - else if (fnName.indexOf('runTask') !== -1) { - runTaskFrame = true; - } - else if (fnName.indexOf('run') !== -1) { - runFrame = true; - } - else { - frameType = FrameType.blackList; - } - blackListedStackFrames[frame.trim()] = frameType; - // Once we find all of the frames we can stop looking. - if (runFrame && runGuardedFrame && runTaskFrame) { - ZoneAwareError[stackRewrite] = true; - break; - } - } - } - } - return false; - } - }); - // 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 _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + var _currentTask = null; + var _numberOfNestedTaskFrames = 0; + function noop() { } + function __symbol__(name) { + return '__zone_symbol__' + name; } - 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); - }, - onCancelTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onCancelTask')); - handleDetectError(throwError('onCancelTask', false)); - return parentZoneDelegate.cancelTask(targetZone, task); - }, - onHasTask: function (delegate, current, target, hasTaskState) { - handleDetectError(throwError('onHasTask')); - handleDetectError(throwError('onHasTask', false)); - return delegate.hasTask(target, hasTaskState); - }, - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; - } - }); - var detectFn = function () { - throw throwError('zoneAwareFrames'); - }; - var detectWithoutNewFn = function () { - throw throwError('zoneAwareFrames', false); - }; - var detectPromiseFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - }; - 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; + performanceMeasure('Zone', 'Zone'); return global['Zone'] = Zone; })(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); @@ -1331,270 +655,46 @@ var Zone$1 = (function (global) { /** * Suppress closure compiler errors about unknown 'Zone' variable * @fileoverview - * @suppress {undefinedVars,globalThis} + * @suppress {undefinedVars,globalThis,missingRequire} */ -var zoneSymbol = function (n) { return "__zone_symbol__" + n; }; +var zoneSymbol = Zone.__symbol__; var _global = typeof window === 'object' && window || typeof self === 'object' && self || global; +var FUNCTION = 'function'; +var UNDEFINED = 'undefined'; -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); - } +function isPropertyWritable(propertyDesc) { + if (!propertyDesc) { + return true; } - for (var j = 0; j < onProperties.length; j++) { - patchProperty(obj, onProperties[j]); + if (propertyDesc.writable === false) { + return false; } - if (properties) { - for (var i = 0; i < properties.length; i++) { - patchProperty(obj, 'on' + properties[i]); - } + if (typeof propertyDesc.get === FUNCTION && typeof propertyDesc.set === UNDEFINED) { + return false; } + return true; } +var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); +// Make sure to access `process` through `_global` so that WebPack does not accidently browserify +// this code. +var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]'); -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); - } - }; -} +// 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 accidently browserify +// this code. +var isMix = typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]' && !isWebWorker && + !!(typeof window !== 'undefined' && window['HTMLElement']); -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) { + while (proto && !proto.hasOwnProperty(name)) { proto = Object.getPrototypeOf(proto); } if (!proto && target[name]) { @@ -1605,16 +705,25 @@ function patchMethod(target, name, patchFn) { var delegate; if (proto && !(delegate = proto[delegateName])) { delegate = proto[delegateName] = proto[name]; - proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); + // check whether proto[name] is writable + // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob + var desc = proto && Object.getOwnPropertyDescriptor(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 - -Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; -Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; +function attachOriginToPatched(patched, original) { + patched[zoneSymbol('OriginalDelegate')] = original; +} /** * @license @@ -1623,12 +732,23 @@ Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; * 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} + */ +var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; setName += nameSuffix; cancelName += nameSuffix; var tasksByHandleId = {}; + var NUMBER = 'number'; + var STRING = 'string'; + var FUNCTION = 'function'; + var INTERVAL = 'Interval'; + var TIMEOUT = 'Timeout'; + var NOT_SCHEDULED = 'notScheduled'; function scheduleTask(task) { var data = task.data; function timer() { @@ -1636,27 +756,33 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - delete tasksByHandleId[data.handleId]; + 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') { + 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, + isPeriodic: nameSuffix === INTERVAL, + delay: (nameSuffix === TIMEOUT || nameSuffix === INTERVAL) ? args[1] || 0 : null, args: args }; var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); @@ -1665,13 +791,26 @@ function patchTimer(window, setName, cancelName, nameSuffix) { } // 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' && - typeof handle.unref === 'function') { + if (handle && handle.ref && handle.unref && typeof handle.ref === FUNCTION && + typeof handle.unref === FUNCTION) { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } + if (typeof handle === NUMBER || handle) { + return handle; + } return task; } else { @@ -1681,10 +820,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]; - if (task && typeof task.type === 'string') { - if (task.state !== 'notScheduled' && + 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 !== NOT_SCHEDULED && (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 new file mode 100644 index 000000000..4451902b3 --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -0,0 +1,1458 @@ +/** +* @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 +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + +/** + * @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 + */ +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + 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.apply(this, [e]); + } + } + catch (err) { + } + } + function isThenable(value) { + return value && value.then; + } + function forwardResolution(value) { + return value; + } + function forwardRejection(rejection) { + return ZoneAwarePromise.reject(rejection); + } + var symbolState = __symbol__('state'); + var symbolValue = __symbol__('value'); + var source = 'Promise.then'; + var UNRESOLVED = null; + var RESOLVED = true; + var REJECTED = false; + var REJECTED_NO_CATCH = 0; + function makeResolver(promise, state) { + return function (v) { + try { + resolvePromise(promise, state, v); + } + catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + }; + } + var once = function () { + var wasCalled = false; + return function wrapper(wrappedFunction) { + return function () { + if (wasCalled) { + return; + } + wasCalled = true; + wrappedFunction.apply(null, arguments); + }; + }; + }; + var TYPE_ERROR = 'Promise resolved with itself'; + var OBJECT = 'object'; + var FUNCTION = 'function'; + var CURRENT_TASK_SYMBOL = __symbol__('currentTask'); + // Promise Resolution + function resolvePromise(promise, state, value) { + var onceWrapper = once(); + if (promise === value) { + throw new TypeError(TYPE_ERROR); + } + if (promise[symbolState] === UNRESOLVED) { + // should only get value.then once based on promise spec. + var then = null; + try { + if (typeof value === OBJECT || typeof value === FUNCTION) { + then = value && value.then; + } + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + return promise; + } + // if (value instanceof ZoneAwarePromise) { + if (state !== REJECTED && value instanceof ZoneAwarePromise && + value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && + value[symbolState] !== UNRESOLVED) { + clearRejectedNoCatch(value); + resolvePromise(promise, value[symbolState], value[symbolValue]); + } + else if (state !== REJECTED && typeof then === FUNCTION) { + try { + then.apply(value, [ + onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) + ]); + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + } + } + else { + promise[symbolState] = state; + var queue = promise[symbolValue]; + promise[symbolValue] = value; + // 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[CURRENT_TASK_SYMBOL] = Zone.currentTask; + } + for (var i = 0; i < queue.length;) { + scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); + } + if (queue.length == 0 && state == REJECTED) { + promise[symbolState] = REJECTED_NO_CATCH; + try { + throw new Error('Uncaught (in promise): ' + value + + (value && value.stack ? '\n' + value.stack : '')); + } + catch (err) { + var error_1 = err; + error_1.rejection = value; + error_1.promise = promise; + error_1.zone = Zone.current; + error_1.task = Zone.currentTask; + _uncaughtPromiseErrors.push(error_1); + api.scheduleMicroTask(); // to make sure that it is running + } + } + } + } + // 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 + // and queue.length > 0, means there is a error handler + // here to handle the rejected promise, we should trigger + // windows.rejectionhandled eventHandler or nodejs rejectionHandled + // eventHandler + try { + var handler = Zone[REJECTION_HANDLED_HANDLER]; + if (handler && typeof handler === FUNCTION) { + handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + } + } + catch (err) { + } + promise[symbolState] = REJECTED; + for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { + if (promise === _uncaughtPromiseErrors[i].promise) { + _uncaughtPromiseErrors.splice(i, 1); + } + } + } + } + function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { + clearRejectedNoCatch(promise); + var delegate = promise[symbolState] ? + (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : + (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; + zone.scheduleMicroTask(source, function () { + try { + resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + } + catch (error) { + resolvePromise(chainPromise, false, error); + } + }); + } + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = (function () { + function ZoneAwarePromise(executor) { + var promise = this; + if (!(promise instanceof ZoneAwarePromise)) { + throw new Error('Must be an instanceof Promise.'); + } + promise[symbolState] = UNRESOLVED; + promise[symbolValue] = []; // queue; + try { + executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); + } + catch (error) { + resolvePromise(promise, false, error); + } + } + ZoneAwarePromise.toString = function () { + return ZONE_AWARE_PROMISE_TO_STRING; + }; + ZoneAwarePromise.resolve = function (value) { + return resolvePromise(new this(null), RESOLVED, value); + }; + ZoneAwarePromise.reject = function (error) { + return resolvePromise(new this(null), REJECTED, error); + }; + ZoneAwarePromise.race = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + _a = [res, rej], resolve = _a[0], reject = _a[1]; + var _a; + }); + function onResolve(value) { + promise && (promise = null || resolve(value)); + } + 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); + } + value.then(onResolve, onReject); + } + return promise; + }; + ZoneAwarePromise.all = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + resolve = res; + reject = rej; + }); + 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); + } + }; })(count), reject); + count++; + } + if (!count) + resolve(resolvedValues); + return promise; + }; + ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { + var chainPromise = new this.constructor(null); + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); + } + return chainPromise; + }; + ZoneAwarePromise.prototype.catch = function (onRejected) { + return this.then(null, onRejected); + }; + return ZoneAwarePromise; + }()); + // Protect against aggressive optimizers dropping seemingly unused properties. + // E.g. Closure Compiler in advanced mode. + ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; + ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; + ZoneAwarePromise['race'] = ZoneAwarePromise.race; + ZoneAwarePromise['all'] = ZoneAwarePromise.all; + var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + var desc = Object.getOwnPropertyDescriptor(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); + } + }; + Object.defineProperty(global, 'Promise', desc); + } + global['Promise'] = ZoneAwarePromise; + var symbolThenPatched = __symbol__('thenPatched'); + function patchThen(Ctor) { + var proto = Ctor.prototype; + var originalThen = proto.then; + // Keep a reference to the original method. + proto[symbolThen] = originalThen; + // check Ctor.prototype.then propertyDescritor is writable or not + // in meteor env, writable is false, we have to make it to be true. + var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); + if (prop && prop.writable === false && prop.configurable) { + Object.defineProperty(Ctor.prototype, 'then', { writable: true }); + } + Ctor.prototype.then = function (onResolve, onReject) { + var _this = this; + var wrapped = new ZoneAwarePromise(function (resolve, reject) { + originalThen.call(_this, resolve, reject); + }); + return wrapped.then(onResolve, onReject); + }; + Ctor[symbolThenPatched] = true; + } + function zoneify(fn) { + return function () { + var resultPromise = fn.apply(this, arguments); + if (resultPromise instanceof ZoneAwarePromise) { + return resultPromise; + } + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); + } + return resultPromise; + }; + } + if (NativePromise) { + patchThen(NativePromise); + var fetch_1 = global['fetch']; + if (typeof fetch_1 == FUNCTION) { + global['fetch'] = zoneify(fetch_1); + } + } + // 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 + */ +/** + * @fileoverview + * @suppress {globalThis} + */ +var NEWLINE = '\n'; +var IGNORE_FRAMES = {}; +var creationTrace = '__creationTrace__'; +var ERROR_TAG = 'STACKTRACE TRACKING'; +var SEP_TAG = '__SEP_TAG__'; +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = (function () { + function LongStackTrace() { + this.error = getStacktrace(); + this.timestamp = new Date(); + } + return LongStackTrace; +}()); +function getStacktraceWithUncaughtError() { + return new Error(ERROR_TAG); +} +function getStacktraceWithCaughtError() { + try { + throw getStacktraceWithUncaughtError(); + } + catch (err) { + return err; + } +} +// Some implementations of exception handling don't create a stack trace if the exception +// isn't thrown, however it's faster not to actually throw the exception. +var error = getStacktraceWithUncaughtError(); +var caughtError = getStacktraceWithCaughtError(); +var getStacktrace = error.stack ? + getStacktraceWithUncaughtError : + (caughtError.stack ? getStacktraceWithCaughtError : getStacktraceWithUncaughtError); +function getFrames(error) { + return error.stack ? error.stack.split(NEWLINE) : []; +} +function addErrorStack(lines, error) { + var trace = getFrames(error); + for (var i = 0; i < trace.length; i++) { + var frame = trace[i]; + // Filter out the Frames which are part of stack capturing. + if (!IGNORE_FRAMES.hasOwnProperty(frame)) { + lines.push(trace[i]); + } + } +} +function renderLongStackTrace(frames, stack) { + var longTrace = [stack ? stack.trim() : '']; + if (frames) { + var timestamp = new Date().getTime(); + for (var i = 0; i < frames.length; i++) { + var traceFrames = frames[i]; + var lastTime = traceFrames.timestamp; + var separator = "____________________Elapsed " + (timestamp - lastTime.getTime()) + " ms; At: " + lastTime; + separator = separator.replace(/[^\w\d]/g, '_'); + longTrace.push(sepTemplate.replace(SEP_TAG, separator)); + addErrorStack(longTrace, traceFrames.error); + timestamp = lastTime.getTime(); + } + } + return longTrace.join(NEWLINE); +} +Zone['longStackTraceZoneSpec'] = { + name: 'long-stack-trace', + longStackTraceLimit: 10, + // add a getLongStackTrace method in spec to + // handle handled reject promise error. + getLongStackTrace: function (error) { + if (!error) { + return undefined; + } + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; + if (!trace) { + return error.stack; + } + return renderLongStackTrace(trace, error.stack); + }, + onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { + 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) { + 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); + } +}; +function captureStackTraces(stackTraces, count) { + if (count > 0) { + stackTraces.push(getFrames((new LongStackTrace()).error)); + captureStackTraces(stackTraces, count - 1); + } +} +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]; + 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; + } + } +} +computeIgnoreFrames(); + +/** + * @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 + */ +var ProxyZoneSpec = (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!this.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; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + else { + return parentZoneDelegate.fork(targetZone, zoneSpec); + } + }; + ProxyZoneSpec.prototype.onIntercept = function (parentZoneDelegate, currentZone, targetZone, delegate, source) { + if (this._delegateSpec && this._delegateSpec.onIntercept) { + return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source); + } + else { + return parentZoneDelegate.intercept(targetZone, delegate, source); + } + }; + ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + if (this._delegateSpec && this._delegateSpec.onInvoke) { + return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); + } + else { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + }; + ProxyZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + if (this._delegateSpec && this._delegateSpec.onHandleError) { + return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); + } + else { + return parentZoneDelegate.handleError(targetZone, error); + } + }; + ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onScheduleTask) { + return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.scheduleTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + if (this._delegateSpec && this._delegateSpec.onHasTask) { + this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); + } + else { + delegate.hasTask(target, hasTaskState); + } + }; + return ProxyZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['ProxyZoneSpec'] = ProxyZoneSpec; + +/** + * @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 + */ +var SyncTestZoneSpec = (function () { + function SyncTestZoneSpec(namePrefix) { + this.runZone = Zone.current; + this.name = 'syncTestZone for ' + namePrefix; + } + SyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + case 'macroTask': + throw new Error("Cannot call " + task.source + " from within a sync test."); + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + return SyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; + +/** + * @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 + */ +var AsyncTestZoneSpec = (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this.runZone = Zone.current; + this._finishCallback = finishCallback; + this._failCallback = failCallback; + this.name = 'asyncTestZone for ' + namePrefix; + } + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + // 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(); + } + }, 0); + }); + } + }; + // 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. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + 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._alreadyErrored = true; + } + return false; + }; + AsyncTestZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + delegate.hasTask(target, hasTaskState); + if (hasTaskState.change == 'microTask') { + this._pendingMicroTasks = hasTaskState.microTask; + this._finishCallbackIfDone(); + } + else if (hasTaskState.change == 'macroTask') { + this._pendingMacroTasks = hasTaskState.macroTask; + this._finishCallbackIfDone(); + } + }; + return AsyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; + +/** + * @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 + */ +(function (global) { + var Scheduler = (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 0; + // 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; + } + 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, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; + var i = 0; + for (; i < this._schedulerQueue.length; i++) { + var currentEntry = this._schedulerQueue[i]; + if (newEntry.endTime < currentEntry.endTime) { + break; + } + } + this._schedulerQueue.splice(i, 0, newEntry); + return currentId; + }; + Scheduler.prototype.removeScheduledFunctionWithId = function (id) { + for (var i = 0; i < this._schedulerQueue.length; i++) { + if (this._schedulerQueue[i].id == id) { + this._schedulerQueue.splice(i, 1); + break; + } + } + }; + 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) { + // Done processing the queue since it's sorted by endTime. + break; + } + 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. + break; + } + } + } + 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, trackPendingRequestAnimationFrame) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + throw new Error('The code should be running in the fakeAsync zone to call this function'); + } + }; + FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) { + var _this = this; + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + fn.apply(global, args); + if (_this._lastError === null) { + if (completers.onSuccess != null) { + completers.onSuccess.apply(global); + } + // Flush microtasks only on success. + _this.flushMicrotasks(); + } + else { + if (completers.onError != null) { + completers.onError.apply(global); + } + } + // Return true if there were no errors, false otherwise. + return _this._lastError === null; + }; + }; + FakeAsyncTestZoneSpec._removeTimer = function (timers, id) { + var index = timers.indexOf(id); + if (index > -1) { + timers.splice(index, 1); + } + }; + FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) { + var _this = this; + return function () { + // Requeue the timer callback if it's not been canceled. + if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); + } + }; + }; + FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); + }; + }; + 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, 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]; + } + 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, true); + this.pendingPeriodicTimers.push(id); + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () { + var error = this._lastError || this._uncaughtPromiseErrors[0]; + this._uncaughtPromiseErrors.length = 0; + this._lastError = null; + throw error; + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + }; + FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () { + var _this = this; + FakeAsyncTestZoneSpec.assertInZone(); + var flushErrors = function () { + if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) { + // If there is an error stop processing the microtask queue and rethrow the error. + _this._resetLastErrorAndThrow(); + } + }; + while (this._microtasks.length > 0) { + var microtask = this._microtasks.shift(); + 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': + 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 addtionalArgs = void 0; + if (args) { + var callbackIndex = task.data.callbackIndex; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: addtionalArgs, + 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']); + break; + case 'setInterval': + task.data['handleId'] = + this._setInterval(task.invoke, task.data['delay'], task.data['args']); + break; + case 'XMLHttpRequest.send': + 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: + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + 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: + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + return FakeAsyncTestZoneSpec; + }()); + // Export the class so that new instances can be created with proper + // constructor params. + Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec; +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @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 + */ +/** + * A `TaskTrackingZoneSpec` allows one to track all outstanding Tasks. + * + * 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 () { + function TaskTrackingZoneSpec() { + this.name = 'TaskTrackingZone'; + this.microTasks = []; + this.macroTasks = []; + this.eventTasks = []; + this.properties = { 'TaskTrackingZone': this }; + } + TaskTrackingZoneSpec.get = function () { + return Zone.current.get('TaskTrackingZone'); + }; + TaskTrackingZoneSpec.prototype.getTasksFor = function (type) { + switch (type) { + case 'microTask': + return this.microTasks; + case 'macroTask': + return this.macroTasks; + case 'eventTask': + return this.eventTasks; + } + throw new Error('Unknown task format: ' + type); + }; + TaskTrackingZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + task['creationLocation'] = new Error("Task '" + task.type + "' from '" + task.source + "'."); + var tasks = this.getTasksFor(task.type); + tasks.push(task); + return parentZoneDelegate.scheduleTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.cancelTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (task.type === 'eventTask') + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + }; + TaskTrackingZoneSpec.prototype.clearEvents = function () { + while (this.eventTasks.length) { + Zone.current.cancelTask(this.eventTasks[0]); + } + }; + return TaskTrackingZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; + +/** + * @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 {missingRequire} + */ +(function (global) { + // Detect and setup WTF. + var wtfTrace = null; + var wtfEvents = null; + var wtfEnabled = (function () { + var wtf = global['wtf']; + if (wtf) { + wtfTrace = wtf.trace; + if (wtfTrace) { + wtfEvents = wtfTrace.events; + return true; + } + } + return false; + })(); + var WtfZoneSpec = (function () { + function WtfZoneSpec() { + this.name = 'WTF'; + } + WtfZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + var retValue = parentZoneDelegate.fork(targetZone, zoneSpec); + WtfZoneSpec.forkInstance(zonePathName(targetZone), retValue.name); + return retValue; + }; + WtfZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + var scope = WtfZoneSpec.invokeScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeScope[source] = + wtfEvents.createScope("Zone:invoke:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)); + }; + WtfZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + return parentZoneDelegate.handleError(targetZone, error); + }; + WtfZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.type + ':' + task.source; + var instance = WtfZoneSpec.scheduleInstance[key]; + if (!instance) { + instance = WtfZoneSpec.scheduleInstance[key] = + wtfEvents.createInstance("Zone:schedule:" + key + "(ascii zone, any data)"); + } + var retValue = parentZoneDelegate.scheduleTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + WtfZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + var source = task.source; + var scope = WtfZoneSpec.invokeTaskScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeTaskScope[source] = + wtfEvents.createScope("Zone:invokeTask:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs)); + }; + WtfZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.source; + var instance = WtfZoneSpec.cancelInstance[key]; + if (!instance) { + instance = WtfZoneSpec.cancelInstance[key] = + wtfEvents.createInstance("Zone:cancel:" + key + "(ascii zone, any options)"); + } + var retValue = parentZoneDelegate.cancelTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + + 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; + var out = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = obj[key]; + switch (typeof value) { + case 'object': + var name_1 = value && value.constructor && value.constructor.name; + value = name_1 == Object.name ? shallowObj(value, depth - 1) : name_1; + break; + case 'function': + value = value.name || undefined; + break; + } + out[key] = value; + } + } + return out; + } + function zonePathName(zone) { + var name = zone.name; + zone = zone.parent; + while (zone != null) { + name = zone.name + '::' + name; + zone = zone.parent; + } + return name; + } + Zone['wtfZoneSpec'] = !wtfEnabled ? null : new WtfZoneSpec(); +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @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 + */ +'use strict'; +(function (context) { + var Mocha = context.Mocha; + if (typeof Mocha === 'undefined') { + throw new Error('Missing Mocha.js'); + } + if (typeof Zone === 'undefined') { + throw new Error('Missing Zone.js'); + } + var ProxyZoneSpec = Zone['ProxyZoneSpec']; + var SyncTestZoneSpec = Zone['SyncTestZoneSpec']; + if (!ProxyZoneSpec) { + throw new Error('Missing ProxyZoneSpec'); + } + if (Mocha['__zone_patch__']) { + throw new Error('"Mocha" has already been patched with "Zone".'); + } + Mocha['__zone_patch__'] = true; + var rootZone = Zone.current; + var syncZone = rootZone.fork(new SyncTestZoneSpec('Mocha.describe')); + var testZone = null; + var suiteZone = rootZone.fork(new ProxyZoneSpec()); + var mochaOriginal = { + after: Mocha.after, + afterEach: Mocha.afterEach, + before: Mocha.before, + beforeEach: Mocha.beforeEach, + describe: Mocha.describe, + it: Mocha.it + }; + function modifyArguments(args, syncTest, asyncTest) { + var _loop_1 = function (i) { + var arg = args[i]; + if (typeof arg === 'function') { + // 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 mocha will + // think that all functions are sync or async. + args[i] = (arg.length === 0) ? syncTest(arg) : asyncTest(arg); + // Mocha uses toString to view the test body in the result list, make sure we return the + // correct function body + args[i].toString = function () { + return arg.toString(); + }; + } + }; + for (var i = 0; i < args.length; i++) { + _loop_1(i); + } + return args; + } + function wrapDescribeInZone(args) { + var syncTest = function (fn) { + return function () { + return syncZone.run(fn, this, arguments); + }; + }; + return modifyArguments(args, syncTest); + } + function wrapTestInZone(args) { + var asyncTest = function (fn) { + return function (done) { + return testZone.run(fn, this, [done]); + }; + }; + var syncTest = function (fn) { + return function () { + return testZone.run(fn, this); + }; + }; + return modifyArguments(args, syncTest, asyncTest); + } + function wrapSuiteInZone(args) { + var asyncTest = function (fn) { + return function (done) { + return suiteZone.run(fn, this, [done]); + }; + }; + var syncTest = function (fn) { + return function () { + return suiteZone.run(fn, this); + }; + }; + return modifyArguments(args, syncTest, asyncTest); + } + context.describe = context.suite = Mocha.describe = function () { + return mochaOriginal.describe.apply(this, wrapDescribeInZone(arguments)); + }; + context.xdescribe = context.suite.skip = Mocha.describe.skip = function () { + return mochaOriginal.describe.skip.apply(this, wrapDescribeInZone(arguments)); + }; + context.describe.only = context.suite.only = Mocha.describe.only = function () { + return mochaOriginal.describe.only.apply(this, wrapDescribeInZone(arguments)); + }; + context.it = context.specify = context.test = Mocha.it = function () { + return mochaOriginal.it.apply(this, wrapTestInZone(arguments)); + }; + context.xit = context.xspecify = Mocha.it.skip = function () { + return mochaOriginal.it.skip.apply(this, wrapTestInZone(arguments)); + }; + context.it.only = context.test.only = Mocha.it.only = function () { + return mochaOriginal.it.only.apply(this, wrapTestInZone(arguments)); + }; + context.after = context.suiteTeardown = Mocha.after = function () { + return mochaOriginal.after.apply(this, wrapSuiteInZone(arguments)); + }; + context.afterEach = context.teardown = Mocha.afterEach = function () { + return mochaOriginal.afterEach.apply(this, wrapTestInZone(arguments)); + }; + context.before = context.suiteSetup = Mocha.before = function () { + return mochaOriginal.before.apply(this, wrapSuiteInZone(arguments)); + }; + context.beforeEach = context.setup = Mocha.beforeEach = function () { + return mochaOriginal.beforeEach.apply(this, wrapTestInZone(arguments)); + }; + (function (originalRunTest, originalRun) { + Mocha.Runner.prototype.runTest = function (fn) { + var _this = this; + Zone.current.scheduleMicroTask('mocha.forceTask', function () { + originalRunTest.call(_this, fn); + }); + }; + 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()); + }); + return originalRun.call(this, fn); + }; + })(Mocha.Runner.prototype.runTest, Mocha.Runner.prototype.run); +})(typeof window !== 'undefined' ? window : global); + +}))); diff --git a/nativescript-angular/zone-js/testing.jasmine.ts b/nativescript-angular/zone-js/testing.jasmine.ts new file mode 100644 index 000000000..c363546af --- /dev/null +++ b/nativescript-angular/zone-js/testing.jasmine.ts @@ -0,0 +1,3 @@ +// Bootstrap helper module for jasmine spec tests +import "../platform"; +import "./dist/zone-nativescript.jasmine.js"; diff --git a/nativescript-angular/zone-js/testing.mocha.ts b/nativescript-angular/zone-js/testing.mocha.ts new file mode 100644 index 000000000..0f75b1d11 --- /dev/null +++ b/nativescript-angular/zone-js/testing.mocha.ts @@ -0,0 +1,2 @@ +import "../platform"; +import "./dist/zone-nativescript.mocha.js"; diff --git a/tests/app/snippets/navigation/page-outlet.ts b/tests/app/snippets/navigation/page-outlet.ts index 75354b5f6..27ce9ee48 100644 --- a/tests/app/snippets/navigation/page-outlet.ts +++ b/tests/app/snippets/navigation/page-outlet.ts @@ -1,4 +1,4 @@ -import { TestApp, registerTestApp } from "../../tests/test-app"; +import { registerTestApp } from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; // >> page-outlet-example diff --git a/tests/app/snippets/navigation/router-outlet.ts b/tests/app/snippets/navigation/router-outlet.ts index bfd50fafc..bca549d74 100644 --- a/tests/app/snippets/navigation/router-outlet.ts +++ b/tests/app/snippets/navigation/router-outlet.ts @@ -1,4 +1,4 @@ -import {TestApp, registerTestApp} from "../../tests/test-app"; +import {registerTestApp} from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; // >> router-outlet-example import { Component, NgModule } from "@angular/core"; diff --git a/tests/app/tests/detached-loader-tests.ts b/tests/app/tests/detached-loader-tests.ts index 014217e35..5b4f6a964 100644 --- a/tests/app/tests/detached-loader-tests.ts +++ b/tests/app/tests/detached-loader-tests.ts @@ -1,21 +1,7 @@ // make sure you import mocha-config before @angular/core -import {assert} from "./test-config"; -import {TestApp} from "./test-app"; -import { - Component, - ElementRef, - Renderer, - AfterViewInit, - OnInit, - ViewChild, - ChangeDetectionStrategy -} from "@angular/core"; -import {ProxyViewContainer} from "ui/proxy-view-container"; -import {Red} from "color/known-colors"; -import {dumpView} from "./test-utils"; -import {LayoutBase} from "ui/layouts/layout-base"; -import {StackLayout} from "ui/layouts/stack-layout"; -import {DetachedLoader} from "nativescript-angular/common/detached-loader"; +import { ChangeDetectionStrategy, Component, ViewChild } from "@angular/core"; +import { DetachedLoader } from "nativescript-angular"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; @Component({ template: `` @@ -25,18 +11,6 @@ class TestComponent { } class LoaderComponentBase { @ViewChild(DetachedLoader) public loader: DetachedLoader; - - public ready: Promise; - private resolve; - constructor() { - this.ready = new Promise((reslove, reject) => { - this.resolve = reslove; - }); - } - ngAfterViewInit() { - console.log("!!! ngAfterViewInit -> loader: " + this.loader); - this.resolve(this); - } } @Component({ @@ -45,9 +19,9 @@ class LoaderComponentBase { - ` + ` }) -export class LoaderComponent extends LoaderComponentBase { } +export class LoaderComponent extends LoaderComponentBase {} @Component({ selector: "loader-component-on-push", @@ -56,58 +30,27 @@ export class LoaderComponent extends LoaderComponentBase { } - ` + ` }) export class LoaderComponentOnPush extends LoaderComponentBase { } describe("DetachedLoader", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [LoaderComponent, LoaderComponentOnPush, TestComponent]).then((app) => { - console.log("TEST APP: " + app); - testApp = app; - }); - }); - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([LoaderComponent, LoaderComponentOnPush], [], [], [TestComponent])); + afterEach(nTestBedAfterEach()); - it("creates component", (done) => { - testApp.loadComponent(LoaderComponent) - .then((componentRef) => { - // wait for the ngAfterViewInit - return (componentRef.instance).ready; - }) - .then((comp) => { - // load test component with loader - return comp.loader.loadComponent(TestComponent); - }) - .then((compRef) => { - done(); - }) - .catch(done); + it("creates component", () => { + return nTestBedRender(LoaderComponent).then((fixture) => { + const component: LoaderComponent = fixture.componentRef.instance; + return component.loader.loadComponent(TestComponent); + }); }); - it("creates component when ChangeDetectionStrategy is OnPush", (done) => { - testApp.loadComponent(LoaderComponentOnPush) - .then((componentRef) => { - // wait for the ngAfterViewInit - return (componentRef.instance).ready; - }) - .then((comp) => { - // load test component with loader - return comp.loader.loadComponent(TestComponent); - }) - .then((compRef) => { - done(); - }) - .catch(done); + it("creates component when ChangeDetectionStrategy is OnPush", () => { + return nTestBedRender(LoaderComponentOnPush).then((fixture) => { + const component: LoaderComponentOnPush = fixture.componentRef.instance; + return component.loader.loadComponent(TestComponent); + }); }); }); diff --git a/tests/app/tests/list-view-tests.ts b/tests/app/tests/list-view-tests.ts index 3c3d7fb54..4e33ec002 100644 --- a/tests/app/tests/list-view-tests.ts +++ b/tests/app/tests/list-view-tests.ts @@ -1,9 +1,7 @@ import { assert } from "./test-config"; -import { Component, Input, AfterViewInit } from "@angular/core"; -import { TestApp } from "./test-app"; -import { RootLocator, ComponentView, getItemViewRoot } from "nativescript-angular/directives/list-view-comp"; -import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; - +import { Component, Input } from "@angular/core"; +import { ComponentFixture } from "@angular/core/testing"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; // import trace = require("trace"); // trace.setCategories("ns-list-view, " + trace.categories.Navigation); // trace.enable(); @@ -68,7 +66,7 @@ export class ItemTemplateComponent { - ` + ` }) export class TestListViewSelectorComponent { public myItems: Array = ITEMS; @@ -79,45 +77,27 @@ export class TestListViewSelectorComponent { } describe("ListView-tests", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [ - TestListViewComponent, - TestListViewSelectorComponent, - ItemTemplateComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([ + TestListViewComponent, + TestListViewSelectorComponent, + ItemTemplateComponent + ])); + afterEach(nTestBedAfterEach(false)); it("setupItemView is called for every item", (done) => { - testApp.loadComponent(TestListViewComponent).then((componentRef) => { - const component = componentRef.instance; - setTimeout(() => { + nTestBedRender(TestListViewComponent) + .then((fixture: ComponentFixture) => { + const component = fixture.componentRef.instance; assert.equal(component.counter, 3); done(); - }, 1000); - }) - .catch(done); + }); }); it("itemTemplateSelector selects templates", (done) => { - testApp.loadComponent(TestListViewSelectorComponent).then((componentRef) => { - setTimeout(() => { - assert.deepEqual(testTemplates, { first: 2, second: 1 }); - done(); - }, 1000); - }) - .catch(done); + nTestBedRender(TestListViewSelectorComponent).then(() => { + assert.deepEqual(testTemplates, {first: 2, second: 1}); + done(); + }); }); }); diff --git a/tests/app/tests/modal-dialog.ts b/tests/app/tests/modal-dialog.ts index 69a909c73..18036ec19 100644 --- a/tests/app/tests/modal-dialog.ts +++ b/tests/app/tests/modal-dialog.ts @@ -1,12 +1,14 @@ // make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { TestApp } from "./test-app"; -import { Component, ViewContainerRef } from "@angular/core"; -import { Page } from "ui/page"; -import { topmost } from "ui/frame"; -import { ModalDialogParams, ModalDialogService } from "nativescript-angular/directives/dialogs"; +import {assert} from "./test-config"; +import {Component, ViewContainerRef} from "@angular/core"; +import {Page} from "ui/page"; +import {topmost} from "ui/frame"; +import {ModalDialogParams, ModalDialogService} from "nativescript-angular/directives/dialogs"; -import { device, platformNames } from "platform"; +import {device, platformNames} from "platform"; + +import {ComponentFixture} from "@angular/core/testing"; +import {nTestBedRender, nTestBedAfterEach, nTestBedBeforeEach} from "nativescript-angular/testing"; const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0; @Component({ @@ -47,20 +49,13 @@ export class SuccessComponent { } describe("modal-dialog", () => { - let testApp: TestApp = null; + beforeEach(nTestBedBeforeEach([FailComponent, SuccessComponent], [], [], [ModalComponent])); + afterEach(nTestBedAfterEach()); before((done) => { - TestApp.create([], [ModalComponent, FailComponent, SuccessComponent]).then((app) => { - testApp = app; - - // HACK: Wait for the navigations from the test runner app - // Remove the setTimeout when test runner start tests on page.navigatedTo - setTimeout(done, 1000); - }); - }); - - after(() => { - testApp.dispose(); + // HACK: Wait for the navigations from the test runner app + // Remove the setTimeout when test runner start tests on page.navigatedTo + setTimeout(() => done(), 1000); }); afterEach(() => { @@ -69,36 +64,38 @@ describe("modal-dialog", () => { console.log("Warning: closing a leftover modal page!"); page.modal.closeModal(); } - testApp.disposeComponents(); }); it("showModal throws when there is no viewContainer provided", (done) => { - testApp.loadComponent(FailComponent) - .then((ref) => { - const service = ref.instance.service; + nTestBedRender(FailComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; assert.throws(() => service.showModal(ModalComponent, {}), - "No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions." - ); - }).then(() => done(), err => done(err)); + "No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions." + ); + }) + .then(() => done()) + .catch((e) => done(e)); }); it("showModal succeeds when there is viewContainer provided", (done) => { - testApp.loadComponent(SuccessComponent) - .then((ref) => { - const service = ref.instance.service; - const comp = ref.instance; - return service.showModal(ModalComponent, { viewContainerRef: comp.vcRef }); + nTestBedRender(SuccessComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; + const comp = fixture.componentRef.instance; + return service.showModal(ModalComponent, {viewContainerRef: comp.vcRef}); }) - .then((res) => setTimeout(done, CLOSE_WAIT), err => done(err)); // wait for the dialog to close in IOS + .then((res) => setTimeout(done, CLOSE_WAIT)) // wait for the dialog to close in IOS + .catch((e) => done(e)); }); it("showModal passes modal params and gets result when resolved", (done) => { - const context = { property: "my context" }; - testApp.loadComponent(SuccessComponent) - .then((ref) => { - const service = ref.instance.service; - const comp = ref.instance; + const context = {property: "my context"}; + nTestBedRender(SuccessComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; + const comp = fixture.componentRef.instance; return service.showModal(ModalComponent, { viewContainerRef: comp.vcRef, context: context @@ -107,6 +104,7 @@ describe("modal-dialog", () => { .then((res) => { assert.strictEqual(res, context); setTimeout(done, CLOSE_WAIT); // wait for the dialog to close in IOS - }, err => done(err)); + }) + .catch((e) => done(e)); }); }); diff --git a/tests/app/tests/platform-filter-components.ts b/tests/app/tests/platform-filter-components.ts index 5665e1364..72d3d5c62 100644 --- a/tests/app/tests/platform-filter-components.ts +++ b/tests/app/tests/platform-filter-components.ts @@ -1,122 +1,102 @@ -// make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { Component, ElementRef } from "@angular/core"; -import { dumpView, createDevice } from "./test-utils"; -import { TestApp } from "./test-app"; -import { DEVICE } from "nativescript-angular/platform-providers"; -import { platformNames } from "platform"; - -@Component({ - template: ` - - - ` -}) -export class IosSpecificComponent { - constructor(public elementRef: ElementRef) { } -} - -@Component({ - template: ` - - - ` -}) -export class AndroidSpecificComponent { - constructor(public elementRef: ElementRef) { } -} - -@Component({ - template: ` - - - ` -}) -export class PlatformSpecificAttributeComponent { - constructor(public elementRef: ElementRef) { } -} - -describe("Platform filter directives", () => { - describe("on IOS device", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([{ provide: DEVICE, useValue: createDevice(platformNames.ios) }], [ - PlatformSpecificAttributeComponent, - AndroidSpecificComponent, - IosSpecificComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - it("does render ios specific content", () => { - return testApp.loadComponent(IosSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=IOS])") >= 0); - }); - }); - - it("does not render android specific content", () => { - return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); - }); - }); - - - it("applies iOS specific attribute", () => { - return testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=IOS])))", - dumpView(componentRoot, true)); - }); - }); - }); - - describe("on Android device", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([{ provide: DEVICE, useValue: createDevice(platformNames.android) }], [ - AndroidSpecificComponent, - IosSpecificComponent, - PlatformSpecificAttributeComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - it("does render android specific content", () => { - return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=ANDROID])") >= 0); - }); - }); - - it("does not render ios specific content", () => { - return testApp.loadComponent(IosSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); - }); - }); - - it("applies Android specific attribute", () => { - return testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", - dumpView(componentRoot, true)); - }); - }); - }); -}); +// make sure you import mocha-config before @angular/core +import { assert } from "./test-config"; +import { Component, ElementRef } from "@angular/core"; +import { dumpView, createDevice } from "./test-utils"; +import { DEVICE } from "nativescript-angular/platform-providers"; +import { platformNames } from "platform"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; + +@Component({ + template: ` + + + ` +}) +export class IosSpecificComponent { + constructor(public elementRef: ElementRef) { } +} + +@Component({ + template: ` + + + ` +}) +export class AndroidSpecificComponent { + constructor(public elementRef: ElementRef) { } +} + +@Component({ + template: ` + + + ` +}) +export class PlatformSpecificAttributeComponent { + constructor(public elementRef: ElementRef) { } +} + +describe("Platform filter directives", () => { + describe("on IOS device", () => { + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.ios)}] + )); + afterEach(nTestBedAfterEach()); + it("does render ios specific content", () => { + return nTestBedRender(IosSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=IOS])") >= 0); + }); + }); + it("does not render android specific content", () => { + return nTestBedRender(AndroidSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); + }); + }); + it("applies iOS specific attribute", () => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal( + "(ProxyViewContainer (StackLayout (Label[text=IOS])))", + dumpView(componentRoot, true)); + }); + }); + }); + + describe("on Android device", () => { + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.android)}] + )); + afterEach(nTestBedAfterEach()); + + it("does render android specific content", () => { + return nTestBedRender(AndroidSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=ANDROID])") >= 0); + }); + }); + it("does not render ios specific content", () => { + return nTestBedRender(IosSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); + }); + }); + it("applies Android specific attribute", () => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal( + "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", + dumpView(componentRoot, true)); + }); + }); + }); +}); diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index ebb9091ee..cdf715870 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -1,18 +1,27 @@ // make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { Component, ElementRef, Renderer2, NgZone, ViewChild } from "@angular/core"; -import { ProxyViewContainer } from "ui/proxy-view-container"; -import { Red } from "color/known-colors"; -import { dumpView } from "./test-utils"; -import { TestApp } from "./test-app"; -import { LayoutBase } from "ui/layouts/layout-base"; -import { StackLayout } from "ui/layouts/stack-layout"; -import { ContentView } from "ui/content-view"; -import { Button } from "ui/button"; -import { NgView } from "nativescript-angular/element-registry"; -import { registerElement } from "nativescript-angular/element-registry"; +import {assert} from "./test-config"; +import {Component, ComponentRef, ElementRef, NgZone, Renderer2, ViewChild} from "@angular/core"; +import {ProxyViewContainer} from "ui/proxy-view-container"; +import {Red} from "color/known-colors"; +import {dumpView} from "./test-utils"; +import {LayoutBase} from "ui/layouts/layout-base"; +import {StackLayout} from "ui/layouts/stack-layout"; +import {ContentView} from "ui/content-view"; +import {Button} from "ui/button"; +import {registerElement} from "nativescript-angular/element-registry"; import * as button from "tns-core-modules/ui/button"; import * as view from "tns-core-modules/ui/core/view"; +import {nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender} from "nativescript-angular/testing"; +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {Observable} from "rxjs/Observable"; +import {ReplaySubject} from "rxjs/ReplaySubject"; + +@Component({ + template: `` +}) +export class ZonedRenderer { + constructor(public elementRef: ElementRef, public renderer: Renderer2) { } +} @Component({ template: `` @@ -219,10 +228,10 @@ export class NgControlSettersCount { get buttons(): ElementRef[] { return [this.btn1, this.btn2, this.btn3, this.btn4]; } - isAfterViewInit: boolean = false; + ready$: Observable = new ReplaySubject(1); ngAfterViewInit() { - this.isAfterViewInit = true; + (this.ready$ as ReplaySubject).next(true); } } @@ -237,46 +246,36 @@ export class NgForLabel { } describe("Renderer E2E", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [ - LayoutWithLabel, LabelCmp, LabelContainer, - ProjectableCmp, ProjectionContainer, - StyledLabelCmp, StyledLabelCmp2, - NgIfLabel, NgIfThenElseComponent, NgIfMultiple, - NgIfTwoElements, NgIfMultiple, - NgIfElseComponent, NgIfThenElseComponent, - NgForLabel, - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([ + LayoutWithLabel, LabelCmp, LabelContainer, + ProjectableCmp, ProjectionContainer, + StyledLabelCmp, StyledLabelCmp2, + NgIfLabel, NgIfThenElseComponent, NgIfMultiple, + NgIfTwoElements, NgIfMultiple, + NgIfElseComponent, NgIfThenElseComponent, + NgForLabel, ZonedRenderer + ])); + afterEach(nTestBedAfterEach(false)); it("component with a layout", () => { - return testApp.loadComponent(LayoutWithLabel).then((componentRef) => { + return nTestBedRender(LayoutWithLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot)); }); }); it("component without a layout", () => { - return testApp.loadComponent(LabelContainer).then((componentRef) => { + return nTestBedRender(LabelContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot)); }); }); it("projects content into components", () => { - return testApp.loadComponent(ProjectionContainer).then((componentRef) => { + return nTestBedRender(ProjectionContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", @@ -285,7 +284,8 @@ describe("Renderer E2E", () => { }); it("applies component styles from single source", () => { - return testApp.loadComponent(StyledLabelCmp).then((componentRef) => { + return nTestBedRender(StyledLabelCmp).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; const label = (componentRoot).getChildAt(0); assert.equal(Red, label.style.color.hex); @@ -293,7 +293,8 @@ describe("Renderer E2E", () => { }); it("applies component styles from multiple sources", () => { - return testApp.loadComponent(StyledLabelCmp2).then((componentRef) => { + return nTestBedRender(StyledLabelCmp2).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; const layout = (componentRoot).getChildAt(0); @@ -316,15 +317,18 @@ describe("Renderer E2E", () => { done(); }; - testApp.zone.run(() => { - testApp.renderer.listen(view, eventName, callback); + nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + fixture.componentInstance.renderer.listen(view, eventName, callback); + }); + + setTimeout(() => { + fixture.ngZone.runOutsideAngular(() => { + view.notify(eventArg); + }); + }, 10); }); - setTimeout(() => { - testApp.zone.runOutsideAngular(() => { - view.notify(eventArg); - }); - }, 10); }); it("executes events inside NgZone when listen is called outside NgZone", (done) => { @@ -336,40 +340,44 @@ describe("Renderer E2E", () => { assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone"); done(); }; + nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.runOutsideAngular(() => { + fixture.componentInstance.renderer.listen(view, eventName, callback); - testApp.zone.runOutsideAngular(() => { - testApp.renderer.listen(view, eventName, callback); - - view.notify(eventArg); + view.notify(eventArg); + }); }); }); describe("Structural directives", () => { it("ngIf hides component when false", () => { - return testApp.loadComponent(NgIfLabel).then((componentRef) => { + return nTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer)", dumpView(componentRoot)); }); }); it("ngIf show component when true", () => { - return testApp.loadComponent(NgIfLabel).then((componentRef) => { + return nTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot)); }); }); it("ngIf shows elements in correct order when two are rendered", () => { - return testApp.loadComponent(NgIfTwoElements).then((componentRef) => { + return nTestBedRender(NgIfTwoElements).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer (StackLayout (Label), (Button)))", dumpView(componentRoot)); @@ -377,12 +385,13 @@ describe("Renderer E2E", () => { }); it("ngIf shows elements in correct order when multiple are rendered and there's *ngIf", () => { - return testApp.loadComponent(NgIfMultiple).then((componentRef) => { + return nTestBedRender(NgIfMultiple).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -398,11 +407,12 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'if' template when condition is true", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nTestBedRender(NgIfElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + @@ -416,12 +426,13 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nTestBedRender(NgIfElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = false; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -434,11 +445,12 @@ describe("Renderer E2E", () => { }); it("ngIfThenElse show 'then' template when condition is true", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nTestBedRender(NgIfThenElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -452,12 +464,13 @@ describe("Renderer E2E", () => { it("ngIfThenElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nTestBedRender(NgIfThenElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = false; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -470,7 +483,8 @@ describe("Renderer E2E", () => { }); it("ngFor creates element for each item", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (Label[text=one]), (Label[text=two]), (Label[text=three]))", @@ -479,12 +493,13 @@ describe("Renderer E2E", () => { }); it("ngFor updates when item is removed", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.items.splice(1, 1); - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer (Label[text=one]), (Label[text=three]))", @@ -493,12 +508,13 @@ describe("Renderer E2E", () => { }); it("ngFor updates when item is inserted", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.items.splice(1, 0, "new"); - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + @@ -510,20 +526,17 @@ describe("Renderer E2E", () => { }); describe("Renderer createElement", () => { - let testApp: TestApp = null; let renderer: Renderer2 = null; - - before(() => { - return TestApp.create().then((app) => { - testApp = app; - renderer = testApp.renderer; + beforeEach(nTestBedBeforeEach([ZonedRenderer])); + afterEach(nTestBedAfterEach(false)); + beforeEach(() => { + return nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + renderer = fixture.componentInstance.renderer; + }); }); }); - after(() => { - testApp.dispose(); - }); - it("creates element from CamelCase", () => { const result = renderer.createElement("StackLayout"); assert.instanceOf(result, StackLayout, "Renderer should create StackLayout form 'StackLayout'"); @@ -546,20 +559,17 @@ describe("Renderer createElement", () => { }); describe("Renderer attach/detach", () => { - let testApp: TestApp = null; let renderer: Renderer2 = null; - - before(() => { - return TestApp.create().then((app) => { - testApp = app; - renderer = testApp.renderer; + beforeEach(nTestBedBeforeEach([ZonedRenderer])); + afterEach(nTestBedAfterEach(false)); + beforeEach(() => { + return nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + renderer = fixture.componentInstance.renderer; + }); }); }); - after(() => { - testApp.dispose(); - }); - it("createElement element with parent attaches element to content view", () => { const parent = renderer.createElement("ContentView"); const button = + // 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 isPropertyWritable(propertyDesc) { - if (!propertyDesc) { - return true; +function patchOnProperties(obj, properties) { + var onProperties = []; + for (var prop in obj) { + if (prop.substr(0, 2) == 'on') { + onProperties.push(prop); + } } - if (propertyDesc.writable === false) { - return false; + for (var j = 0; j < onProperties.length; j++) { + patchProperty(obj, onProperties[j]); } - if (typeof propertyDesc.get === FUNCTION && typeof propertyDesc.set === UNDEFINED) { - return false; + if (properties) { + for (var i = 0; i < properties.length; i++) { + patchProperty(obj, 'on' + properties[i]); + } } - return true; } -var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); -// Make sure to access `process` through `_global` so that WebPack does not accidently browserify -// this code. -var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && - {}.toString.call(_global.process) === '[object process]'); -// 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 accidently browserify -// this code. -var isMix = typeof _global.process !== 'undefined' && - {}.toString.call(_global.process) === '[object process]' && !isWebWorker && - !!(typeof window !== 'undefined' && window['HTMLElement']); +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 && !proto.hasOwnProperty(name)) { + while (proto && Object.getOwnPropertyNames(proto).indexOf(name) === -1) { proto = Object.getPrototypeOf(proto); } if (!proto && target[name]) { @@ -706,25 +1605,16 @@ function patchMethod(target, name, patchFn) { 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 && Object.getOwnPropertyDescriptor(proto, name); - if (isPropertyWritable(desc)) { - var patchDelegate_1 = patchFn(delegate, delegateName, name); - proto[name] = function () { - return patchDelegate_1(this, arguments); - }; - attachOriginToPatched(proto[name], delegate); - } + proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); } return delegate; } // TODO: @JiaLiPassion, support cancel task later if necessary -function attachOriginToPatched(patched, original) { - patched[zoneSymbol('OriginalDelegate')] = original; -} + +Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; +Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; /** * @license @@ -733,23 +1623,12 @@ function attachOriginToPatched(patched, original) { * 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} - */ -var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; setName += nameSuffix; cancelName += nameSuffix; var tasksByHandleId = {}; - var NUMBER = 'number'; - var STRING = 'string'; - var FUNCTION = 'function'; - var INTERVAL = 'Interval'; - var TIMEOUT = 'Timeout'; - var NOT_SCHEDULED = 'notScheduled'; function scheduleTask(task) { var data = task.data; function timer() { @@ -757,39 +1636,27 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - if (task.data && task.data.isPeriodic) { - // issue-934, task will be cancelled - // even it is a periodic task such as - // setInterval - return; - } - 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; - } + delete tasksByHandleId[data.handleId]; } } + 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) { + 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, + isPeriodic: nameSuffix === 'Interval', + delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, args: args }; var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); @@ -798,26 +1665,13 @@ function patchTimer(window, setName, cancelName, nameSuffix) { } // 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 && - typeof handle.unref === FUNCTION) { + if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && + typeof handle.unref === 'function') { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } - if (typeof handle === NUMBER || handle) { - return handle; - } return task; } else { @@ -827,29 +1681,10 @@ function patchTimer(window, setName, cancelName, nameSuffix) { }; }); clearNative = patchMethod(window, cancelName, function (delegate) { return function (self, args) { - 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 !== NOT_SCHEDULED && + var task = typeof args[0] === 'number' ? tasksByHandleId[args[0]] : args[0]; + 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 522244227..0aa13a09f 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -11,437 +11,6 @@ (factory()); }(this, (function () { 'use strict'; -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 }; - } - }; -}; -/** - * @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 - */ -Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { - 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.apply(this, [e]); - } - } - catch (err) { - } - } - function isThenable(value) { - return value && value.then; - } - function forwardResolution(value) { - return value; - } - function forwardRejection(rejection) { - return ZoneAwarePromise.reject(rejection); - } - var symbolState = __symbol__('state'); - var symbolValue = __symbol__('value'); - var source = 'Promise.then'; - var UNRESOLVED = null; - var RESOLVED = true; - var REJECTED = false; - var REJECTED_NO_CATCH = 0; - function makeResolver(promise, state) { - return function (v) { - try { - resolvePromise(promise, state, v); - } - catch (err) { - resolvePromise(promise, false, err); - } - // Do not return value or you will break the Promise spec. - }; - } - var once = function () { - var wasCalled = false; - return function wrapper(wrappedFunction) { - return function () { - if (wasCalled) { - return; - } - wasCalled = true; - wrappedFunction.apply(null, arguments); - }; - }; - }; - var TYPE_ERROR = 'Promise resolved with itself'; - var OBJECT = 'object'; - var FUNCTION = 'function'; - var CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace'); - // Promise Resolution - function resolvePromise(promise, state, value) { - var onceWrapper = once(); - if (promise === value) { - throw new TypeError(TYPE_ERROR); - } - if (promise[symbolState] === UNRESOLVED) { - // should only get value.then once based on promise spec. - var then = null; - try { - if (typeof value === OBJECT || typeof value === FUNCTION) { - then = value && value.then; - } - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - return promise; - } - // if (value instanceof ZoneAwarePromise) { - if (state !== REJECTED && value instanceof ZoneAwarePromise && - value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && - value[symbolState] !== UNRESOLVED) { - clearRejectedNoCatch(value); - resolvePromise(promise, value[symbolState], value[symbolValue]); - } - else if (state !== REJECTED && typeof then === FUNCTION) { - try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - } - } - else { - promise[symbolState] = state; - var queue = promise[symbolValue]; - promise[symbolValue] = value; - // 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) { - // 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 - Object.defineProperty(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++]); - } - if (queue.length == 0 && state == REJECTED) { - promise[symbolState] = REJECTED_NO_CATCH; - try { - // try to print more readable error log - throw new Error('Uncaught (in promise): ' + readableObjectToString(value) + - (value && value.stack ? '\n' + value.stack : '')); - } - catch (err) { - var error_1 = err; - error_1.rejection = value; - error_1.promise = promise; - error_1.zone = Zone.current; - error_1.task = Zone.currentTask; - _uncaughtPromiseErrors.push(error_1); - api.scheduleMicroTask(); // to make sure that it is running - } - } - } - } - // 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 - // and queue.length > 0, means there is a error handler - // here to handle the rejected promise, we should trigger - // windows.rejectionhandled eventHandler or nodejs rejectionHandled - // eventHandler - try { - var handler = Zone[REJECTION_HANDLED_HANDLER]; - if (handler && typeof handler === FUNCTION) { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); - } - } - catch (err) { - } - promise[symbolState] = REJECTED; - for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { - if (promise === _uncaughtPromiseErrors[i].promise) { - _uncaughtPromiseErrors.splice(i, 1); - } - } - } - } - function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { - clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? - (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : - (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; - zone.scheduleMicroTask(source, function () { - try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); - } - catch (error) { - resolvePromise(chainPromise, false, error); - } - }); - } - var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; - var ZoneAwarePromise = /** @class */ (function () { - function ZoneAwarePromise(executor) { - var promise = this; - if (!(promise instanceof ZoneAwarePromise)) { - throw new Error('Must be an instanceof Promise.'); - } - promise[symbolState] = UNRESOLVED; - promise[symbolValue] = []; // queue; - try { - executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); - } - catch (error) { - resolvePromise(promise, false, error); - } - } - ZoneAwarePromise.toString = function () { - return ZONE_AWARE_PROMISE_TO_STRING; - }; - ZoneAwarePromise.resolve = function (value) { - return resolvePromise(new this(null), RESOLVED, value); - }; - ZoneAwarePromise.reject = function (error) { - return resolvePromise(new this(null), REJECTED, error); - }; - ZoneAwarePromise.race = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - function onResolve(value) { - promise && (promise = null || resolve(value)); - } - function onReject(error) { - promise && (promise = null || reject(error)); - } - 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); - } - finally { if (e_1) throw e_1.error; } - } - return promise; - var e_1, _a; - }; - ZoneAwarePromise.all = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - var count = 0; - var 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); - } - 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); - var zone = Zone.current; - if (this[symbolState] == UNRESOLVED) { - this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); - } - else { - scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); - } - return chainPromise; - }; - ZoneAwarePromise.prototype.catch = function (onRejected) { - return this.then(null, onRejected); - }; - return ZoneAwarePromise; - }()); - // Protect against aggressive optimizers dropping seemingly unused properties. - // E.g. Closure Compiler in advanced mode. - ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; - ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; - ZoneAwarePromise['race'] = ZoneAwarePromise.race; - ZoneAwarePromise['all'] = ZoneAwarePromise.all; - var NativePromise = global[symbolPromise] = global['Promise']; - var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); - var desc = Object.getOwnPropertyDescriptor(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); - } - }; - Object.defineProperty(global, 'Promise', desc); - } - global['Promise'] = ZoneAwarePromise; - var symbolThenPatched = __symbol__('thenPatched'); - function patchThen(Ctor) { - var proto = Ctor.prototype; - var originalThen = proto.then; - // Keep a reference to the original method. - proto[symbolThen] = originalThen; - // check Ctor.prototype.then propertyDescritor is writable or not - // in meteor env, writable is false, we have to make it to be true. - var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); - if (prop && prop.writable === false && prop.configurable) { - Object.defineProperty(Ctor.prototype, 'then', { writable: true }); - } - Ctor.prototype.then = function (onResolve, onReject) { - var _this = this; - var wrapped = new ZoneAwarePromise(function (resolve, reject) { - originalThen.call(_this, resolve, reject); - }); - return wrapped.then(onResolve, onReject); - }; - Ctor[symbolThenPatched] = true; - } - function zoneify(fn) { - return function () { - var resultPromise = fn.apply(this, arguments); - if (resultPromise instanceof ZoneAwarePromise) { - return resultPromise; - } - var ctor = resultPromise.constructor; - if (!ctor[symbolThenPatched]) { - patchThen(ctor); - } - return resultPromise; - }; - } - if (NativePromise) { - patchThen(NativePromise); - var fetch_1 = global['fetch']; - if (typeof fetch_1 == FUNCTION) { - global['fetch'] = zoneify(fetch_1); - } - } - // 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. @@ -458,8 +27,8 @@ var IGNORE_FRAMES = {}; var creationTrace = '__creationTrace__'; var ERROR_TAG = 'STACKTRACE TRACKING'; var SEP_TAG = '__SEP_TAG__'; -var sepTemplate = SEP_TAG + '@[native]'; -var LongStackTrace = /** @class */ (function () { +var sepTemplate = ''; +var LongStackTrace = (function () { function LongStackTrace() { this.error = getStacktrace(); this.timestamp = new Date(); @@ -498,7 +67,7 @@ function addErrorStack(lines, error) { } } function renderLongStackTrace(frames, stack) { - var longTrace = [stack ? stack.trim() : '']; + var longTrace = [stack.trim()]; if (frames) { var timestamp = new Date().getTime(); for (var i = 0; i < frames.length; i++) { @@ -522,44 +91,33 @@ Zone['longStackTraceZoneSpec'] = { if (!error) { return undefined; } - var trace = error[Zone.__symbol__('currentTaskTrace')]; + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; if (!trace) { return error.stack; } return renderLongStackTrace(trace, error.stack); }, onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - 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; - } + 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) { - 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) { - } + 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); @@ -572,32 +130,27 @@ 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]; - 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 (!sepTemplate && frame1.indexOf(ERROR_TAG) == -1) { + sepTemplate = frame1.replace(/^(\s*(at)?\s*)([\w\/\<]+)/, '$1' + SEP_TAG); + } 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(); @@ -609,7 +162,7 @@ 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 = /** @class */ (function () { +var ProxyZoneSpec = (function () { function ProxyZoneSpec(defaultSpecDelegate) { if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } this.defaultSpecDelegate = defaultSpecDelegate; @@ -723,7 +276,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 = /** @class */ (function () { +var SyncTestZoneSpec = (function () { function SyncTestZoneSpec(namePrefix) { this.runZone = Zone.current; this.name = 'syncTestZone for ' + namePrefix; @@ -752,7 +305,7 @@ 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 = /** @class */ (function () { +var AsyncTestZoneSpec = (function () { function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { this._pendingMicroTasks = false; this._pendingMacroTasks = false; @@ -820,7 +373,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * found in the LICENSE file at https://angular.io/license */ (function (global) { - var Scheduler = /** @class */ (function () { + var Scheduler = (function () { function Scheduler() { // Next scheduler id. this.nextId = 0; @@ -829,23 +382,13 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; // Current simulated time in millis. this._currentTime = 0; } - Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + Scheduler.prototype.scheduleFunction = function (cb, delay, args, 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, - isPeriodic: isPeriodic, - isRequestAnimationFrame: isRequestAnimationFrame - }; + var newEntry = { endTime: endTime, id: currentId, func: cb, args: args, delay: delay }; var i = 0; for (; i < this._schedulerQueue.length; i++) { var currentEntry = this._schedulerQueue[i]; @@ -864,14 +407,9 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } } }; - Scheduler.prototype.tick = function (millis, doTick) { + Scheduler.prototype.tick = function (millis) { 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) { @@ -881,11 +419,7 @@ 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. @@ -895,65 +429,10 @@ 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 = /** @class */ (function () { - function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { - if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } - this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; - this.macroTaskOptions = macroTaskOptions; + var FakeAsyncTestZoneSpec = (function () { + function FakeAsyncTestZoneSpec(namePrefix) { this._scheduler = new Scheduler(); this._microtasks = []; this._lastError = null; @@ -962,11 +441,6 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this.pendingTimers = []; this.properties = { 'FakeAsyncTestZoneSpec': this }; this.name = 'fakeAsyncTestZone for ' + namePrefix; - // in case user can't access the construction of FakyAsyncTestSpec - // 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) { @@ -1014,7 +488,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, true, false, id); + _this._scheduler.scheduleFunction(fn, interval, args, id); } }; }; @@ -1024,15 +498,12 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); }; }; - FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { - if (isTimer === void 0) { isTimer = true; } + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args) { 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, false, !isTimer); - if (isTimer) { - this.pendingTimers.push(id); - } + var id = this._scheduler.scheduleFunction(cb, delay, args); + this.pendingTimers.push(id); return id; }; FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { @@ -1050,7 +521,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; // 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, true); + this._scheduler.scheduleFunction(cb, interval, args); this.pendingPeriodicTimers.push(id); return id; }; @@ -1064,11 +535,11 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._lastError = null; throw error; }; - FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + FakeAsyncTestZoneSpec.prototype.tick = function (millis) { if (millis === void 0) { millis = 0; } FakeAsyncTestZoneSpec.assertInZone(); this.flushMicrotasks(); - this._scheduler.tick(millis, doTick); + this._scheduler.tick(millis); if (this._lastError !== null) { this._resetLastErrorAndThrow(); } @@ -1084,38 +555,14 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; }; while (this._microtasks.length > 0) { var microtask = this._microtasks.shift(); - microtask.func.apply(microtask.target, microtask.args); + microtask(); } 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': - 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 addtionalArgs = void 0; - if (args) { - var callbackIndex = task.data.callbackIndex; - if (typeof args.length === 'number' && args.length > callbackIndex + 1) { - addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); - } - } - this._microtasks.push({ - func: task.invoke, - args: addtionalArgs, - target: task.data && task.data.target - }); + this._microtasks.push(task.invoke); break; case 'macroTask': switch (task.source) { @@ -1128,35 +575,9 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._setInterval(task.invoke, task.data['delay'], task.data['args']); break; case 'XMLHttpRequest.send': - 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; + throw new Error('Cannot make XHRs from within a fake async test.'); default: - // 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 setTimout to simulate - task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); - } - break; - } - throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + task = delegate.scheduleTask(target, task); } break; case 'eventTask': @@ -1168,36 +589,13 @@ 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.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. @@ -1222,7 +620,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 = /** @class */ (function () { +var TaskTrackingZoneSpec = (function () { function TaskTrackingZoneSpec() { this.name = 'TaskTrackingZone'; this.microTasks = []; @@ -1290,10 +688,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 */ -/** - * @fileoverview - * @suppress {missingRequire} - */ (function (global) { // Detect and setup WTF. var wtfTrace = null; @@ -1309,7 +703,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } return false; })(); - var WtfZoneSpec = /** @class */ (function () { + var WtfZoneSpec = (function () { function WtfZoneSpec() { this.name = 'WTF'; } @@ -1360,13 +754,14 @@ 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; From 7dd35105fe3531939587b499b50bed0de7a5c2e5 Mon Sep 17 00:00:00 2001 From: Martin Reinhardt Date: Sat, 14 Apr 2018 08:25:45 +0200 Subject: [PATCH 04/12] chore(tests): Fix Renderer Tests --- tests/app/tests/renderer-tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 9618173b3..bd4b7402e 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -10,7 +10,7 @@ import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; import { ContentView } from "tns-core-modules/ui/content-view"; import { NgView, registerElement } from "nativescript-angular/element-registry"; -import * as button from "tns-core-modules/ui/button"; +import { Button } from "tns-core-modules/ui/button"; import * as view from "tns-core-modules/ui/core/view"; import { isIOS } from "tns-core-modules/platform"; import { View, fontInternalProperty, backgroundInternalProperty } from "tns-core-modules/ui/core/view" From b14528897157ba42fe7772c03e7dbc5ac303670c Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 3 May 2018 18:03:59 +0300 Subject: [PATCH 05/12] fix: rxjs6 imports --- tests/app/tests/renderer-tests.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index bd4b7402e..94c0a4e7e 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -5,7 +5,6 @@ import { Component, ComponentRef, ElementRef, NgZone, Renderer2, ViewChild } fro import { ProxyViewContainer } from "ui/proxy-view-container"; import { Red } from "color/known-colors"; import { dumpView } from "./test-utils"; -import { TestApp } from "./test-app"; import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; import { ContentView } from "tns-core-modules/ui/content-view"; @@ -16,8 +15,7 @@ import { isIOS } from "tns-core-modules/platform"; import { View, fontInternalProperty, backgroundInternalProperty } from "tns-core-modules/ui/core/view" import { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } from "nativescript-angular/testing"; import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { Observable } from "rxjs/Observable"; -import { ReplaySubject } from "rxjs/ReplaySubject"; +import { Observable, ReplaySubject } from "rxjs"; @Component({ template: `` From 941307a2b2584a02a9f017750cda54f1e2afdcaa Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 3 May 2018 18:07:31 +0300 Subject: [PATCH 06/12] refactor: Remove snippets tests --- tests/app/snippets/navigation/page-outlet.ts | 3 - .../app/snippets/navigation/router-outlet.ts | 13 +- tests/app/tests/snippets.ts | 95 ----------- tests/app/tests/test-app.ts | 148 ------------------ 4 files changed, 5 insertions(+), 254 deletions(-) delete mode 100644 tests/app/tests/test-app.ts diff --git a/tests/app/snippets/navigation/page-outlet.ts b/tests/app/snippets/navigation/page-outlet.ts index 27ce9ee48..3e1e9fbf8 100644 --- a/tests/app/snippets/navigation/page-outlet.ts +++ b/tests/app/snippets/navigation/page-outlet.ts @@ -1,4 +1,3 @@ -import { registerTestApp } from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; // >> page-outlet-example @@ -22,8 +21,6 @@ export class PageNavigationApp { public done: Promise; constructor(public router: Router, public appRef: ApplicationRef) { - registerTestApp(PageNavigationApp, this, appRef); - this.done = new Promise((resolve, reject) => { this.router.events.subscribe((e) => { if (e instanceof NavigationStart) { diff --git a/tests/app/snippets/navigation/router-outlet.ts b/tests/app/snippets/navigation/router-outlet.ts index bca549d74..df15730bf 100644 --- a/tests/app/snippets/navigation/router-outlet.ts +++ b/tests/app/snippets/navigation/router-outlet.ts @@ -1,4 +1,3 @@ -import {registerTestApp} from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; // >> router-outlet-example import { Component, NgModule } from "@angular/core"; @@ -6,7 +5,7 @@ import { platformNativeScriptDynamic } from "nativescript-angular/platform"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; import { routes } from "./app.routes"; -import {FirstComponent, SecondComponent} from "./navigation-common"; +import { FirstComponent, SecondComponent } from "./navigation-common"; @Component({ selector: "navigation-test", @@ -30,8 +29,6 @@ export class NavigationApp { public done: Promise; constructor(public router: Router, public appRef: ApplicationRef) { - registerTestApp(NavigationApp, this, appRef); - this.done = new Promise((resolve, reject) => { this.router.events.subscribe((e) => { if (e instanceof NavigationStart) { @@ -55,13 +52,13 @@ export class NavigationApp { NativeScriptRouterModule.forRoot(routes) ] }) -export class NavigationAppModule {} +export class NavigationAppModule { } // >> (hide) function start_snippet() { -// << (hide) -platformNativeScriptDynamic().bootstrapModule(NavigationAppModule); -// >> (hide) + // << (hide) + platformNativeScriptDynamic().bootstrapModule(NavigationAppModule); + // >> (hide) } // << (hide) // << router-outlet-example diff --git a/tests/app/tests/snippets.ts b/tests/app/tests/snippets.ts index 72a4da8ab..793b33421 100644 --- a/tests/app/tests/snippets.ts +++ b/tests/app/tests/snippets.ts @@ -3,7 +3,6 @@ import { assert } from "./test-config"; import { NavigationEnd, NavigationStart } from "@angular/router"; import { Subscription } from "rxjs"; -import { bootstrapTestApp, destroyTestApp } from "./test-app"; import { GestureComponent } from "../snippets/gestures.component"; import { LayoutsComponent } from "../snippets/layouts.component"; @@ -56,97 +55,3 @@ describe("Snippets", () => { }); }); }); - -describe("Snippets Navigation", () => { - let runningApp: any; - let subscription: Subscription; - - const cleanup = () => { - if (subscription) { - subscription.unsubscribe(); - subscription = null; - } - if (runningApp) { - destroyTestApp(runningApp); - runningApp = null; - } - }; - - after(cleanup); - - it("router-outlet app", (done) => { - bootstrapTestApp(NavigationApp, [], routes, [ - NavigationApp, - FirstComponent, - SecondComponent - ]).then((app) => { - runningApp = app; - - return runningApp.done.then(() => { - assert(app.startEvent instanceof NavigationStart); - assert.equal("/", app.startEvent.url); - - assert(app.endEvent instanceof NavigationEnd); - assert.equal("/", app.endEvent.url); - assert.equal("/first", app.endEvent.urlAfterRedirects); - - cleanup(); - }).then(() => done(), err => done(err)); - }); - }); - - //TODO: Skip the page-router-outlet test as it causes a crash in android in the current test-runner setup - (isIOS ? it : it.skip)("page-router-outlet app", (done) => { - bootstrapTestApp(PageNavigationApp, [], routes, [ - PageNavigationApp, - FirstComponent, - SecondComponent - ]).then((app) => { - runningApp = app; - - return runningApp.done.then(() => { - assert(app.startEvent instanceof NavigationStart); - assert.equal("/", app.startEvent.url); - - assert(app.endEvent instanceof NavigationEnd); - assert.equal("/", app.endEvent.url); - assert.equal("/first", app.endEvent.urlAfterRedirects); - - cleanup(); - }).then(() => done(), err => done(err)); - }); - }); -}); - -describe("Snippets ListView", () => { - let runningApp: any; - - const cleanup = () => { - if (runningApp) { - destroyTestApp(runningApp); - runningApp = null; - } - }; - - after(cleanup); - - it("template selector", (done) => { - bootstrapTestApp( - ListTemplateSelectorTest, - [DataService], - null, - [ - HeaderComponent, - ItemComponent, - ListTemplateSelectorTest - ]) - .then((app) => { - setTimeout(() => { - cleanup(); - done(); - }, 100); - }) - .catch(err => done(err)); - }); -}); - diff --git a/tests/app/tests/test-app.ts b/tests/app/tests/test-app.ts deleted file mode 100644 index 6d9726f93..000000000 --- a/tests/app/tests/test-app.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { platformNativeScriptDynamic } from "nativescript-angular/platform"; -import { NativeScriptModule } from "nativescript-angular/nativescript.module"; -import { NativeScriptRouterModule } from "nativescript-angular/router"; -import { - Type, Component, ComponentRef, - ComponentFactoryResolver, ApplicationRef, Renderer2, - ViewContainerRef, NgZone, NgModule, -} from "@angular/core"; - -import { getRootView } from "tns-core-modules/application"; -import { Frame } from "tns-core-modules/ui/frame"; -import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout"; -import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; - -import { APP_ROOT_VIEW } from "nativescript-angular/platform-providers"; - -@Component({ - selector: "my-app", - template: `` -}) -export class TestApp { - private _pendingDispose: ComponentRef[] = []; - - constructor( - private resolver: ComponentFactoryResolver, - private containerRef: ViewContainerRef, - public appRef: ApplicationRef, - public renderer: Renderer2, - public zone: NgZone - ) { - - registerTestApp(TestApp, this, appRef); - } - - public loadComponent(componentType: Type): Promise> { - const factory = this.resolver.resolveComponentFactory(componentType); - const componentRef = this.containerRef.createComponent( - factory, this.containerRef.length, this.containerRef.parentInjector); - this._pendingDispose.push(componentRef); - - this.appRef.tick(); - return Promise.resolve(componentRef); - } - - public disposeComponents() { - while (this._pendingDispose.length > 0) { - const componentRef = this._pendingDispose.pop(); - componentRef.destroy(); - } - } - - public static create(providers?: any[], components: any[] = [], directives: any[] = []): Promise { - return bootstrapTestApp(TestApp, providers, [], components, directives); - } - - public dispose() { - this.disposeComponents(); - destroyTestApp(this); - } -} - -const runningApps = new Map(); -const platform = platformNativeScriptDynamic({bootInExistingPage: true}); - -export function registerTestApp(appType, appInstance, appRef) { - appType.moduleType.appInstance = appInstance; - runningApps.set(appInstance, { - container: appType.moduleType.container, - appRoot: appType.moduleType.viewRoot, - appRef: appRef, - }); -} - -export function bootstrapTestApp( - appComponentType: new (...args) => T, - providers: any[] = [], - routes: any[] = [], - components: any[] = [], - directives: any[] = [] -): Promise { - const page = (getRootView()).currentPage; - const rootLayout = page.content; - const viewRoot = new GridLayout(); - rootLayout.addChild(viewRoot); - (viewRoot.style).backgroundColor = "white"; - viewRoot.margin = "20"; - viewRoot.opacity = 0.7; - GridLayout.setRowSpan(rootLayout, 50); - GridLayout.setColumnSpan(rootLayout, 50); - - let imports: any[] = [ - NativeScriptModule, - NativeScriptRouterModule, - ]; - if (routes && routes.length > 0) { - imports.push(NativeScriptRouterModule.forRoot(routes)); - } - - const rootViewProvider = {provide: APP_ROOT_VIEW, useValue: viewRoot}; - - @NgModule({ - bootstrap: [ - appComponentType - ], - declarations: [ - appComponentType, - ...components, - ...directives, - ], - entryComponents: [ - ...components, - ], - imports: imports, - exports: [ - NativeScriptModule, - ...components, - ...directives, - ], - providers: [ - rootViewProvider, - ...providers, - ] - }) - class TestAppModule { - public static viewRoot = viewRoot; - public static container = rootLayout; - } - // app registers with the module type via static fields on start - (appComponentType).moduleType = TestAppModule; - - return platform.bootstrapModule(TestAppModule).then(moduleRef => { - // app component constructor has run and we should have a - // registered component instance. - return (TestAppModule).appInstance; - }); -} - -export function destroyTestApp(app: any) { - if (!runningApps.has(app)) { - throw new Error("Unable to cleanup app: " + app); - } - - const entry = runningApps.get(app); - entry.container.removeChild(entry.appRoot); - // TODO: App disposal not doing anything useful anymore. Get rid of it? - // entry.appRef.dispose(); - runningApps.delete(app); -} From 99afad2cda2e53bb8e7f27d93a20fd01d16eb0c7 Mon Sep 17 00:00:00 2001 From: "Reinhardt, Martin" Date: Wed, 9 May 2018 12:43:52 +0200 Subject: [PATCH 07/12] chore(build): Allow CI testing --- e2e/config/appium.capabilities.json | 12 +++++++++++- tests/karma.conf.js | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/e2e/config/appium.capabilities.json b/e2e/config/appium.capabilities.json index befcaf973..4b002b8bc 100644 --- a/e2e/config/appium.capabilities.json +++ b/e2e/config/appium.capabilities.json @@ -35,6 +35,16 @@ "noReset": false, "fullReset": false }, + "android22": { + "platformName": "Android", + "platformVersion": "5.0", + "deviceName": "Emulator-Api22-Default", + "avd": "Emulator-Api22-Default", + "lt": 60000, + "newCommandTimeout": 720, + "noReset": false, + "fullReset": false + }, "android23": { "platformName": "Android", "platformVersion": "6.0", @@ -114,4 +124,4 @@ "noReset": true, "fullReset": false } -} +} \ No newline at end of file diff --git a/tests/karma.conf.js b/tests/karma.conf.js index 37a8029b9..b3addd839 100644 --- a/tests/karma.conf.js +++ b/tests/karma.conf.js @@ -74,6 +74,6 @@ module.exports = function(config) { // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits - singleRun: false + singleRun: true }) } From c0eb376c59554624faae82d453cebd706a635cd7 Mon Sep 17 00:00:00 2001 From: "Reinhardt, Martin" Date: Wed, 9 May 2018 12:44:24 +0200 Subject: [PATCH 08/12] fix(compat-error): Resolve RxJS issue Adding rxjs-compat --- nativescript-angular/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index 99ae14fcb..ff068f37f 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -52,7 +52,8 @@ "@angular/http": "~6.0.0-rc.0", "@angular/platform-browser": "~6.0.0-rc.0", "@angular/router": "~6.0.0-rc.0", - "rxjs": "~6.0.0-rc.1", + "rxjs": "~6.0.0-rc.1 || >=6.1.0 || >=6.0.0", + "rxjs-compat": "~6.0.0-rc.1 || >=6.1.0 || >=6.0.0", "tns-core-modules": "^4.0.0 || >4.0.0- || >4.1.0-", "zone.js": "^0.8.4", "typescript": "~2.7.2" @@ -70,10 +71,10 @@ "@angular/router": "~6.0.0-rc.0", "codelyzer": "^4.0.0", "rxjs": "~6.0.0-rc.1", + "rxjs-compat": "~6.0.0-rc.1", "tns-core-modules": "next", "tslint": "^5.5.0", "typescript": "~2.7.2", "zone.js": "^0.8.12" } } - From 523e2d70d28cd590bab3a7980b1f341d347fc203 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Wed, 9 May 2018 18:14:21 +0300 Subject: [PATCH 09/12] 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); From 3c8289764ca40e142fc86e9279853e0ebbaa8d56 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 10 May 2018 13:34:31 +0300 Subject: [PATCH 10/12] chore(tests): Unit tests made green again --- nativescript-angular/platform-common.ts | 5 +- nativescript-angular/testing/src/util.ts | 13 +++-- tests/app/tests/platform-filter-components.ts | 8 +-- tests/app/tests/renderer-tests.ts | 58 +++++++++---------- tests/app/tests/snippets.ts | 24 +------- tests/app/tests/test-utils.ts | 6 +- 6 files changed, 52 insertions(+), 62 deletions(-) diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index b37262098..d38f3f2d1 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -67,7 +67,10 @@ export class NativeScriptSanitizer extends Sanitizer { } // Add a fake polyfill for the document object -(global).document = (global).document || {}; +(global).document = (global).document || { + getElementById: () => { return undefined; } +}; + const doc = (global).document; doc.body = Object.assign(doc.body || {}, { isOverride: true, diff --git a/nativescript-angular/testing/src/util.ts b/nativescript-angular/testing/src/util.ts index 52859e89e..3cc5b8f8f 100644 --- a/nativescript-angular/testing/src/util.ts +++ b/nativescript-angular/testing/src/util.ts @@ -125,7 +125,10 @@ export function nsTestBedAfterEach(resetEnv = true, resetFn = nsTestBedInit) { const root = testingRootView() as LayoutBase; const fixtures = activeTestFixtures.pop(); fixtures.forEach((fixture) => { - root.removeChild(fixture.nativeElement); + const fixtureView = fixture.nativeElement; + if (fixtureView.parent === root) { + root.removeChild(fixtureView); + } fixture.destroy(); }); TestBed.resetTestingModule(); @@ -144,10 +147,10 @@ export function nsTestBedRender(componentType: Type): Promise { const list = activeTestFixtures[activeTestFixtures.length - 1]; diff --git a/tests/app/tests/platform-filter-components.ts b/tests/app/tests/platform-filter-components.ts index fff8368ec..d239c495d 100644 --- a/tests/app/tests/platform-filter-components.ts +++ b/tests/app/tests/platform-filter-components.ts @@ -47,7 +47,7 @@ describe("Platform filter directives", () => { return nsTestBedRender(IosSpecificComponent).then((fixture) => { const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=IOS])") >= 0); + assert.isTrue(dumpView(componentRoot, true).indexOf("(label[text=IOS])") >= 0); }); }); it("does not render android specific content", () => { @@ -62,7 +62,7 @@ describe("Platform filter directives", () => { const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=IOS])))", + "(proxyviewcontainer (stacklayout (label[text=IOS])))", dumpView(componentRoot, true)); }); }); @@ -79,7 +79,7 @@ describe("Platform filter directives", () => { return nsTestBedRender(AndroidSpecificComponent).then((fixture) => { const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=ANDROID])") >= 0); + assert.isTrue(dumpView(componentRoot, true).indexOf("(label[text=ANDROID])") >= 0); }); }); it("does not render ios specific content", () => { @@ -94,7 +94,7 @@ describe("Platform filter directives", () => { const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", + "(proxyviewcontainer (stacklayout (label[text=ANDROID])))", dumpView(componentRoot, true)); }); }); diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 94c0a4e7e..ae6c5346b 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -262,7 +262,7 @@ describe("Renderer E2E", () => { return nsTestBedRender(LayoutWithLabel).then((fixture) => { const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer (stacklayout (label)))", dumpView(componentRoot)); }); }); @@ -270,7 +270,7 @@ describe("Renderer E2E", () => { return nsTestBedRender(LabelContainer).then((fixture) => { const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer (gridlayout (proxyviewcontainer (label))))", dumpView(componentRoot)); }); }); @@ -279,7 +279,7 @@ describe("Renderer E2E", () => { const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", + "(proxyviewcontainer (gridlayout (proxyviewcontainer (stacklayout (button)))))", dumpView(componentRoot)); }); }); @@ -355,7 +355,7 @@ describe("Renderer E2E", () => { return nsTestBedRender(NgIfLabel).then((fixture) => { const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer)", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer)", dumpView(componentRoot)); }); }); @@ -367,7 +367,7 @@ describe("Renderer E2E", () => { component.show = true; fixture.detectChanges(); - assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer (label))", dumpView(componentRoot)); }); }); @@ -380,7 +380,7 @@ describe("Renderer E2E", () => { component.show = true; fixture.detectChanges(); assert.equal( - "(ProxyViewContainer (StackLayout (Label), (Button)))", + "(proxyviewcontainer (stacklayout (label), (button)))", dumpView(componentRoot)); }); }); @@ -394,13 +394,13 @@ describe("Renderer E2E", () => { component.show = true; fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(StackLayout " + - "(Label[text=1]), " + - "(Label[text=2]), " + - "(Label[text=3]), " + - "(Label[text=4]), " + // the content to be conditionally displayed - "(Label[text=5])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=1]), " + + "(label[text=2]), " + + "(label[text=3]), " + + "(label[text=4]), " + // the content to be conditionally displayed + "(label[text=5])" + ")" + ")", dumpView(componentRoot, true)); @@ -416,9 +416,9 @@ describe("Renderer E2E", () => { fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(StackLayout " + - "(Label[text=If])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=If])" + ")" + ")", @@ -435,9 +435,9 @@ describe("Renderer E2E", () => { component.show = false; fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(StackLayout " + - "(Label[text=Else])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Else])" + ")" + ")", @@ -453,9 +453,9 @@ describe("Renderer E2E", () => { fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(StackLayout " + - "(Label[text=Then])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Then])" + ")" + ")", @@ -473,9 +473,9 @@ describe("Renderer E2E", () => { component.show = false; fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(StackLayout " + - "(Label[text=Else])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Else])" + ")" + ")", @@ -488,7 +488,7 @@ describe("Renderer E2E", () => { const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (Label[text=one]), (Label[text=two]), (Label[text=three]))", + "(proxyviewcontainer (label[text=one]), (label[text=two]), (label[text=three]))", dumpView(componentRoot, true)); }); }); @@ -503,7 +503,7 @@ describe("Renderer E2E", () => { fixture.detectChanges(); assert.equal( - "(ProxyViewContainer (Label[text=one]), (Label[text=three]))", + "(proxyviewcontainer (label[text=one]), (label[text=three]))", dumpView(componentRoot, true)); }); }); @@ -518,8 +518,8 @@ describe("Renderer E2E", () => { fixture.detectChanges(); assert.equal( - "(ProxyViewContainer " + - "(Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", + "(proxyviewcontainer " + + "(label[text=one]), (label[text=new]), (label[text=two]), (label[text=three]))", dumpView(componentRoot, true)); }); }); diff --git a/tests/app/tests/snippets.ts b/tests/app/tests/snippets.ts index 793b33421..b97b3285a 100644 --- a/tests/app/tests/snippets.ts +++ b/tests/app/tests/snippets.ts @@ -1,27 +1,12 @@ // make sure you import mocha-config before @angular/core import { assert } from "./test-config"; -import { NavigationEnd, NavigationStart } from "@angular/router"; -import { Subscription } from "rxjs"; - import { GestureComponent } from "../snippets/gestures.component"; import { LayoutsComponent } from "../snippets/layouts.component"; import { IconFontComponent } from "../snippets/icon-font.component"; -import { PageNavigationApp } from "../snippets/navigation/page-outlet"; -import { NavigationApp } from "../snippets/navigation/router-outlet"; -import { FirstComponent, SecondComponent } from "../snippets/navigation/navigation-common"; -import { routes } from "../snippets/navigation/app.routes"; -import { - HeaderComponent, - ItemComponent, - DataService, - ListTemplateSelectorTest, -} from "../snippets/list-view/template-selector.component"; - -import { device, platformNames, isIOS } from "platform"; -import {nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender} from "nativescript-angular/testing"; -import {ComponentRef} from "@angular/core"; +import { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } from "nativescript-angular/testing"; +import { ComponentRef } from "@angular/core"; describe("Snippets", () => { @@ -44,14 +29,11 @@ describe("Snippets", () => { }); }); - // TODO: Skip list-view test until karma test launcher double navigate bug is fixed - (isIOS ? it.skip : it)("Icon-font snippets can be loaded", (done) => { + it("Icon-font snippets can be loaded", () => { return nsTestBedRender(IconFontComponent).then((fixture) => { const componentRef: ComponentRef = fixture.componentRef; const componentInstance = componentRef.instance; assert.instanceOf(componentInstance, IconFontComponent); - // Works around a "dehydrated change detector" exception. - setTimeout(done, 10); }); }); }); diff --git a/tests/app/tests/test-utils.ts b/tests/app/tests/test-utils.ts index d168f2caf..5e78de08a 100644 --- a/tests/app/tests/test-utils.ts +++ b/tests/app/tests/test-utils.ts @@ -12,11 +12,13 @@ function getChildren(view: View): Array { } export function dumpView(view: View, verbose: boolean = false): string { - let nodeName = (view).nodeName; + let nodeName: string = (view).nodeName; if (!nodeName) { // Strip off the source - nodeName = view.toString().replace(/(@[^;]*;)/g,''); + nodeName = view.toString().replace(/(@[^;]*;)/g, ''); } + nodeName = nodeName.toLocaleLowerCase(); + let output = ["(", nodeName]; if (verbose) { if (view instanceof TextBase) { From 0edad68b385a0178477f159ffcd9cfedc9a7bc4a Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 10 May 2018 17:09:41 +0300 Subject: [PATCH 11/12] chore(test): Provide NSLocationStrategy for modal tests --- tests/app/tests/modal-dialog.ts | 29 +++++++++++++++---------- tests/app/tests/ns-location-strategy.ts | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/app/tests/modal-dialog.ts b/tests/app/tests/modal-dialog.ts index 60dec8718..1ef92349a 100644 --- a/tests/app/tests/modal-dialog.ts +++ b/tests/app/tests/modal-dialog.ts @@ -1,14 +1,17 @@ // make sure you import mocha-config before @angular/core -import {assert} from "./test-config"; -import {Component, ViewContainerRef} from "@angular/core"; -import {Page} from "ui/page"; -import {topmost} from "ui/frame"; -import {ModalDialogParams, ModalDialogService} from "nativescript-angular/directives/dialogs"; +import { assert } from "./test-config"; +import { Component, ViewContainerRef } from "@angular/core"; +import { Page } from "ui/page"; +import { topmost } from "ui/frame"; +import { ModalDialogParams, ModalDialogService } from "nativescript-angular/directives/dialogs"; -import {device, platformNames} from "platform"; +import { device, platformNames } from "platform"; -import {ComponentFixture} from "@angular/core/testing"; -import {nsTestBedRender, nsTestBedAfterEach, nsTestBedBeforeEach} from "nativescript-angular/testing"; +import { ComponentFixture } from "@angular/core/testing"; +import { nsTestBedRender, nsTestBedAfterEach, nsTestBedBeforeEach } from "nativescript-angular/testing"; +import { NSLocationStrategy } from "nativescript-angular/router/ns-location-strategy"; +import { FrameService } from "nativescript-angular"; +import { FakeFrameService } from "./ns-location-strategy"; const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0; @Component({ @@ -50,7 +53,11 @@ export class SuccessComponent { describe("modal-dialog", () => { - beforeEach(nsTestBedBeforeEach([FailComponent, SuccessComponent], [], [], [ModalComponent])); + beforeEach(nsTestBedBeforeEach( + [FailComponent, SuccessComponent], + [{ provide: FrameService, useValue: new FakeFrameService() }, NSLocationStrategy], + [], + [ModalComponent])); afterEach(nsTestBedAfterEach()); before((done) => { // HACK: Wait for the navigations from the test runner app @@ -84,14 +91,14 @@ describe("modal-dialog", () => { .then((fixture: ComponentFixture) => { const service = fixture.componentRef.instance.service; const comp = fixture.componentRef.instance; - return service.showModal(ModalComponent, {viewContainerRef: comp.vcRef}); + return service.showModal(ModalComponent, { viewContainerRef: comp.vcRef }); }) .then((res) => setTimeout(done, CLOSE_WAIT)) // wait for the dialog to close in IOS .catch((e) => done(e)); }); it("showModal passes modal params and gets result when resolved", (done) => { - const context = {property: "my context"}; + const context = { property: "my context" }; nsTestBedRender(SuccessComponent) .then((fixture: ComponentFixture) => { const service = fixture.componentRef.instance.service; diff --git a/tests/app/tests/ns-location-strategy.ts b/tests/app/tests/ns-location-strategy.ts index 7ed33f074..5519075d1 100644 --- a/tests/app/tests/ns-location-strategy.ts +++ b/tests/app/tests/ns-location-strategy.ts @@ -7,7 +7,7 @@ import { Page } from "ui/page"; import { View } from "ui/core/view"; import { FrameService } from "nativescript-angular/platform-providers"; -class FakeFrameService extends FrameService { +export class FakeFrameService extends FrameService { private frame: Frame; constructor(private backCB?: () => void) { super(); @@ -19,7 +19,7 @@ class FakeFrameService extends FrameService { } } -class FakeFrame extends View implements Frame { +export class FakeFrame extends View implements Frame { backStack: Array; currentPage: Page; currentEntry: NavigationEntry; From c89d617da1acbe7bfd9cc054a161ae4e879d389b Mon Sep 17 00:00:00 2001 From: vakrilov Date: Tue, 15 May 2018 16:48:06 +0300 Subject: [PATCH 12/12] fix(zone): Fix zone js patching Promise prop descriptr and breaking android snapshot --- .../zone-js/dist/zone-nativescript.js | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.js b/nativescript-angular/zone-js/dist/zone-nativescript.js index 7eb6799a8..95756327a 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.js @@ -1019,41 +1019,42 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { 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); - } + // NOTE(NativeScript): Defining the Promise property descriptor this way causes + // problems with V8 snapshot for Android. Just skip it. + // let 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) {