From 2a77728b6d0ef9dcaefb06ef81f9aed90097adf2 Mon Sep 17 00:00:00 2001 From: "Reinhardt, Martin" Date: Sat, 23 Dec 2017 19:18:42 +0100 Subject: [PATCH] feat(TestBed): Activate TestBed for NativeScript Angular Apps --- .gitignore | 2 +- nativescript-angular/renderer.ts | 6 +- nativescript-angular/testing/index.ts | 29 + .../nativescript_test_component_renderer.ts | 23 + 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 | 1266 +++----------- .../zone-js/dist/zone-nativescript.mocha.js | 1458 +++++++++++++++++ .../zone-js/testing.jasmine.ts | 3 + nativescript-angular/zone-js/testing.mocha.ts | 2 + ng-sample/karma.conf.js | 79 + 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 | 66 +- tests/app/tests/renderer-tests.ts | 248 +-- tests/app/tests/snippets.ts | 26 +- tests/app/tests/test-main.ts | 3 + tests/app/tests/test-utils.ts | 7 +- tests/app/tests/third-party.ts | 30 +- tests/karma.conf.js | 1 + tests/package.json | 5 +- 23 files changed, 3692 insertions(+), 1402 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 ng-sample/karma.conf.js 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..098637ed8 --- /dev/null +++ b/nativescript-angular/testing/src/nativescript_test_component_renderer.ts @@ -0,0 +1,23 @@ +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..22909b57a --- /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) 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..11777d5c4 --- /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..96e732e73 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 ' + @@ -61,7 +67,7 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - + Object.defineProperty(Zone, "currentTask", { get: function () { return _currentTask; @@ -69,7 +75,18 @@ 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 +94,7 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - + Object.defineProperty(Zone.prototype, "name", { get: function () { return this._name; @@ -85,7 +102,7 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - + Zone.prototype.get = function (key) { var zone = this.getZoneWith(key); if (zone) @@ -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); - } - } - } - while (_uncaughtPromiseErrors.length) { - var _loop_1 = function () { - var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); - try { - uncaughtPromiseError.zone.runGuarded(function () { - throw uncaughtPromiseError; - }); - } - catch (error) { - handleUnhandledRejection(error); + _api.onUnhandledError(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..4421e6f8b --- /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/ng-sample/karma.conf.js b/ng-sample/karma.conf.js new file mode 100644 index 000000000..c7ebf69fb --- /dev/null +++ b/ng-sample/karma.conf.js @@ -0,0 +1,79 @@ +module.exports = function(config) { + config.set({ + browserNoActivityTimeout: 40000, + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'node_modules/nativescript-angular/testing.js', + 'app/tests/**/*.js', + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress', 'junit'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: [], + + customLaunchers: { + android: { + base: 'NS', + platform: 'android' + }, + ios: { + base: 'NS', + platform: 'ios' + }, + ios_simulator: { + base: 'NS', + platform: 'ios', + arguments: ['--emulator'] + } + }, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }) +} 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..fdf792667 100644 --- a/tests/app/tests/platform-filter-components.ts +++ b/tests/app/tests/platform-filter-components.ts @@ -2,9 +2,9 @@ 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"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; @Component({ template: ` @@ -38,39 +38,28 @@ export class PlatformSpecificAttributeComponent { 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(); - }); - + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.ios)}] + )); + afterEach(nTestBedAfterEach()); it("does render ios specific content", () => { - return testApp.loadComponent(IosSpecificComponent).then((componentRef) => { + 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 testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { + 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 testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (StackLayout (Label[text=IOS])))", @@ -80,38 +69,29 @@ describe("Platform filter directives", () => { }); 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(); - }); + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.android)}] + )); + afterEach(nTestBedAfterEach()); it("does render android specific content", () => { - return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { + 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 testApp.loadComponent(IosSpecificComponent).then((componentRef) => { + 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 testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", 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 =