From f74f38ae328fc94126f7d46251cc178057ea8131 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Mon, 23 Oct 2017 07:31:21 -0700 Subject: [PATCH 1/3] feat(testing): support TestBed and export jasmine/mocha zone scripts - update zone build and add jasmine/mocha test scripts, see: https://github.com/NativeScript/zone.js/pull/1 - refactor testing and zone-js modules to work with each other - add generous TestBed setup helpers in testing/src/util.ts - add single import test helpers for zone-js patches in zone-js/testing.[framework].ts files - update peer deps to reflect updated zone (could peer dep be dropped since prebuilts are exported?) chore: disable most tests and enable ones that use TestApp one-by-one - add test entry point script that inits the test environment for TestBed - list view, modal dialog pass - detached-loader and platform-filter-component could use feedback. see todos chore: replace the remaining TestApp usages in test suite - xdescribe the failing tests. - I think the remaining problems boil down to `dumpView` indicating the ComponentFixture comes back with the root components, and `@ViewChild` not finding DetachedLoader by its class. - remove some duplication in testing utilities chore: cleanup and remove some diff noise from a few tests chore: remove more test noise - are the line-endings different on this file? :( chore: convince dumpView and TestComponentRenderer to agree on things - all the TestBed tests except for the DetachedLoader ones and a single Renderer lifecycle are passing. - update NativeScriptRenderer.selectRootElement to find views by ID when given a selector of an id string. TestBed uses this when creating componentRefs to get at the correct views. - change NativeScriptTestComponentRenderer to inject only a ProxyViewContainer which mimics what TestApp did. - update dumpView to strip off the new "source" data attached to a view.toString() result. chore: cleanup lint chore: make nTestBed helpers automatically clean up test components - before the components were destroyed by TestBed, but not removed from the rootView. - maintain a list of active fixtures for a set of tests, and remove them all when the tests complete. - reorder to the testing utils to flow better when reading (start with test init, then before/after then render components) - clean up some lint. chore: fix issue where nTestBedBeforeEach overwrote its own imports - Fixes the DetachedLoader tests, and makes them MUCH simpler. - When you configure the test bed module, you need to specify a full list of imports, because they completely overwrite the imports array that is used. - That's yet another reason to use the provided helper functions, they merge in the common {N} imports for you. - ... and some lint cleanup chore: make renderer lifecyle test more robust - the first assertion is that the view after init has been called. rather than assert it, just wait for it using an observable and avoid asserting about timing and implementation specific details of the system. Specifically this removes the assumption that `app.tick()` will advance time and call `ngAfterViewInit` on the component. chore: cleanup from review - re-enable all tests in karma.conf.js - remove some diff noise --- .gitignore | 2 +- nativescript-angular/renderer.ts | 6 +- nativescript-angular/testing/index.ts | 29 + .../nativescript_test_component_renderer.ts | 24 + nativescript-angular/testing/src/util.ts | 165 ++ nativescript-angular/zone-js/README.md | 10 + .../zone-js/dist/zone-nativescript.jasmine.js | 1442 ++++++++++++++++ .../zone-js/dist/zone-nativescript.js | 1258 +++----------- .../zone-js/dist/zone-nativescript.mocha.js | 1458 +++++++++++++++++ .../zone-js/testing.jasmine.ts | 3 + nativescript-angular/zone-js/testing.mocha.ts | 2 + tests/app/snippets/navigation/page-outlet.ts | 2 +- .../app/snippets/navigation/router-outlet.ts | 2 +- tests/app/tests/detached-loader-tests.ts | 93 +- tests/app/tests/list-view-tests.ts | 56 +- tests/app/tests/modal-dialog.ts | 74 +- tests/app/tests/platform-filter-components.ts | 224 ++- tests/app/tests/renderer-tests.ts | 248 +-- tests/app/tests/snippets.ts | 25 +- tests/app/tests/test-main.ts | 3 + tests/app/tests/test-utils.ts | 94 +- tests/app/tests/third-party.ts | 30 +- tests/karma.conf.js | 3 +- 23 files changed, 3732 insertions(+), 1521 deletions(-) create mode 100644 nativescript-angular/testing/index.ts create mode 100644 nativescript-angular/testing/src/nativescript_test_component_renderer.ts create mode 100644 nativescript-angular/testing/src/util.ts create mode 100644 nativescript-angular/zone-js/README.md create mode 100644 nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js create mode 100644 nativescript-angular/zone-js/dist/zone-nativescript.mocha.js create mode 100644 nativescript-angular/zone-js/testing.jasmine.ts create mode 100644 nativescript-angular/zone-js/testing.mocha.ts create mode 100644 tests/app/tests/test-main.ts diff --git a/.gitignore b/.gitignore index 7b21aa525..83f0e2ce0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ tags !/nativescript-angular/postinstall.js !/nativescript-angular/hooks/**/*.js !/nativescript-angular/gulpfile.js -!/nativescript-angular/zone-js/**/*.js +!/nativescript-angular/zone-js/dist/*.js .tscache .nvm diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 405f1c91d..4330e58a5 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -5,7 +5,7 @@ import { } from "@angular/core"; import { Device } from "tns-core-modules/platform"; -import { View } from "tns-core-modules/ui/core/view"; +import { View, getViewById } from "tns-core-modules/ui/core/view"; import { addCss } from "tns-core-modules/application"; import { topmost } from "tns-core-modules/ui/frame"; import { profile } from "tns-core-modules/profiling"; @@ -110,6 +110,10 @@ export class NativeScriptRenderer extends Renderer2 { @profile selectRootElement(selector: string): NgView { traceLog("NativeScriptRenderer.selectRootElement: " + selector); + if (selector && selector[0] === "#") { + const result = getViewById(this.rootView, selector.slice(1)); + return (result || this.rootView) as NgView; + } return this.rootView; } diff --git a/nativescript-angular/testing/index.ts b/nativescript-angular/testing/index.ts new file mode 100644 index 000000000..d8d00bbe6 --- /dev/null +++ b/nativescript-angular/testing/index.ts @@ -0,0 +1,29 @@ +import { NgModule } from "@angular/core"; +import { TestComponentRenderer } from "@angular/core/testing"; +import { NativeScriptTestComponentRenderer } from "./src/nativescript_test_component_renderer"; +import { COMMON_PROVIDERS } from "../platform-common"; +import { APP_ROOT_VIEW } from "../platform-providers"; +import { testingRootView } from "./src/util"; +export * from "./src/util"; + +/** + * Providers array is exported for cases where a custom module has to be constructed + * to test a particular piece of code. This can happen, for example, if you are trying + * to test dynamic component loading and need to specify an entryComponent for the testing + * module. + */ +export const NATIVESCRIPT_TESTING_PROVIDERS: any[] = [ + COMMON_PROVIDERS, + {provide: APP_ROOT_VIEW, useFactory: testingRootView}, + {provide: TestComponentRenderer, useClass: NativeScriptTestComponentRenderer}, +]; + +/** + * NativeScript testing support module. Enables use of TestBed for angular components, directives, + * pipes, and services. + */ +@NgModule({ + providers: NATIVESCRIPT_TESTING_PROVIDERS +}) +export class NativeScriptTestingModule { +} diff --git a/nativescript-angular/testing/src/nativescript_test_component_renderer.ts b/nativescript-angular/testing/src/nativescript_test_component_renderer.ts new file mode 100644 index 000000000..5cac9a488 --- /dev/null +++ b/nativescript-angular/testing/src/nativescript_test_component_renderer.ts @@ -0,0 +1,24 @@ +import { Injectable } from "@angular/core"; +import { TestComponentRenderer } from "@angular/core/testing"; +import { topmost } from "tns-core-modules/ui/frame"; +import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; +import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; + +/** + * A NativeScript based implementation of the TestComponentRenderer. + */ +@Injectable() +export class NativeScriptTestComponentRenderer extends TestComponentRenderer { + + insertRootElement(rootElId: string) { + const page = topmost().currentPage; + + const layout = new ProxyViewContainer(); + layout.id = rootElId; + + const rootLayout = page.layoutView as LayoutBase; + rootLayout.addChild(layout); + } + +} + diff --git a/nativescript-angular/testing/src/util.ts b/nativescript-angular/testing/src/util.ts new file mode 100644 index 000000000..9c616caf0 --- /dev/null +++ b/nativescript-angular/testing/src/util.ts @@ -0,0 +1,165 @@ + +import { View } from "tns-core-modules/ui/core/view"; +import { topmost } from "tns-core-modules/ui/frame"; +import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { NgModule, Type } from "@angular/core"; +import { NativeScriptModule } from "../../nativescript.module"; +import { platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; +import { NS_COMPILER_PROVIDERS } from "../../platform"; +import { NATIVESCRIPT_TESTING_PROVIDERS, NativeScriptTestingModule } from "../index"; +import { CommonModule } from "@angular/common"; +/** + * Get a reference to the root application view. + */ +export function testingRootView(): View { + return topmost().currentPage.content; +} + +/** + * Declared test contexts. When the suite is done this map should be empty if all lifecycle + * calls have happened as expected. + * @private + */ +const activeTestFixtures: ComponentFixture[][] = []; + +/** + * Return a promise that resolves after (durationMs) milliseconds + */ +export function promiseWait(durationMs: number) { + return () => new Promise((resolve) => setTimeout(() => resolve(), durationMs)); +} + +/** + * Perform basic TestBed environment initialization. Call this once in the main entry point to your tests. + */ +export function nTestBedInit() { + TestBed.initTestEnvironment( + NativeScriptTestingModule, + platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) + ); +} + +/** + * Helper for configuring a TestBed instance for rendering components for test. Ideally this + * would not be needed, and in truth it's just a wrapper to eliminate some boilerplate. It + * exists because when you need to specify `entryComponents` for a test the setup becomes quite + * a bit more complex than if you're just doing a basic component test. + * + * More about entryComponents complexity: https://github.com/angular/angular/issues/12079 + * + * Use: + * ``` + * beforeEach(nTestBedBeforeEach([MyComponent,MyFailComponent])); + * ``` + * + * **NOTE*** Remember to pair with {@see nTestBedAfterEach} + * + * @param components Any components that you will create during the test + * @param providers Any services your tests depend on + * @param imports Any module imports your tests depend on + * @param entryComponents Any entry components that your tests depend on + */ +export function nTestBedBeforeEach( + components: any[], + providers: any[] = [], + imports: any[] = [], + entryComponents: any[] = []) { + return (done) => { + activeTestFixtures.push([]); + // If there are no entry components we can take the simple path. + if (entryComponents.length === 0) { + TestBed.configureTestingModule({ + declarations: [...components], + providers: [...providers], + imports: [NativeScriptModule, ...imports] + }); + } else { + // If there are entry components, we have to reset the testing platform. + // + // There's got to be a better way... (o_O) + TestBed.resetTestEnvironment(); + @NgModule({ + declarations: entryComponents, + exports: entryComponents, + entryComponents: entryComponents + }) + class EntryComponentsTestModule { + } + TestBed.initTestEnvironment( + EntryComponentsTestModule, + platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) + ); + TestBed.configureTestingModule({ + declarations: components, + imports: [ + NativeScriptModule, NativeScriptTestingModule, CommonModule, + ...imports + ], + providers: [...providers, ...NATIVESCRIPT_TESTING_PROVIDERS], + }); + } + TestBed.compileComponents() + .then(() => done()) + .catch((e) => { + console.log(`Failed to instantiate test component with error: ${e}`); + console.log(e.stack); + done(); + }); + }; +} + +/** + * Helper for a basic component TestBed clean up. + * @param resetEnv When true the testing environment will be reset + * @param resetFn When resetting the environment, use this init function + */ +export function nTestBedAfterEach(resetEnv = true, resetFn = nTestBedInit) { + return () => { + if (activeTestFixtures.length === 0) { + throw new Error( + `There are no more declared fixtures.` + + `Did you call "nTestBedBeforeEach" and "nTestBedAfterEach" an equal number of times?` + ); + } + const root = testingRootView() as LayoutBase; + const fixtures = activeTestFixtures.pop(); + fixtures.forEach((fixture) => { + root.removeChild(fixture.nativeElement); + fixture.destroy(); + }); + TestBed.resetTestingModule(); + if (resetEnv) { + TestBed.resetTestEnvironment(); + resetFn(); + } + }; +} + +/** + * Render a component using the TestBed helper, and return a promise that resolves when the + * ComponentFixture is fully initialized. + */ +export function nTestBedRender(componentType: Type): Promise> { + const fixture = TestBed.createComponent(componentType); + fixture.detectChanges(); + return fixture.whenRenderingDone() + // TODO(jd): it seems that the whenStable and whenRenderingDone utilities of ComponentFixture + // do not work as expected. I looked at how to fix it and it's not clear how to provide + // a {N} specific subclass, because ComponentFixture is newed directly rather than injected + // What to do about it? Maybe fakeAsync can help? For now just setTimeout for 100ms (x_X) + .then(promiseWait(100)) + .then(() => { + const list = activeTestFixtures[activeTestFixtures.length - 1]; + if (!list) { + console.warn( + "nTestBedRender called without nTestBedBeforeEach/nTestBedAfter each. " + + "You are responsible for calling 'fixture.destroy()' when your test is done " + + "in order to clean up the components that are created." + ); + } else { + list.push(fixture); + } + return fixture; + }); +} diff --git a/nativescript-angular/zone-js/README.md b/nativescript-angular/zone-js/README.md new file mode 100644 index 000000000..38a0ebc26 --- /dev/null +++ b/nativescript-angular/zone-js/README.md @@ -0,0 +1,10 @@ +Zone.js for NativeScript +--- + +Zone.js is a library that aims to intercept all asynchronous API calls made in an environment, in order +to wrap them into coherent execution contexts over time. + +NativeScript executes inside an environment that Zone.js is not designed to work in, so a custom Zone.js output +must be created. + +Find out more about this in the [Upgrading Zone.js document](../../doc/upgrading-zonejs.md) \ No newline at end of file diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js new file mode 100644 index 000000000..a249f76d2 --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js @@ -0,0 +1,1442 @@ +/** +* @license +* Copyright Google Inc. All Rights Reserved. +* +* Use of this source code is governed by an MIT-style license that can be +* found in the LICENSE file at https://angular.io/license +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + api.onUnhandledError = function (e) { + if (api.showUncaughtError()) { + var rejection = e && e.rejection; + if (rejection) { + console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); + } + else { + console.error(e); + } + } + }; + api.microtaskDrainDone = function () { + while (_uncaughtPromiseErrors.length) { + var _loop_1 = function () { + var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); + try { + uncaughtPromiseError.zone.runGuarded(function () { + throw uncaughtPromiseError; + }); + } + catch (error) { + handleUnhandledRejection(error); + } + }; + while (_uncaughtPromiseErrors.length) { + _loop_1(); + } + } + }; + var UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); + function handleUnhandledRejection(e) { + api.onUnhandledError(e); + try { + var handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; + if (handler && typeof handler === 'function') { + handler.apply(this, [e]); + } + } + catch (err) { + } + } + function isThenable(value) { + return value && value.then; + } + function forwardResolution(value) { + return value; + } + function forwardRejection(rejection) { + return ZoneAwarePromise.reject(rejection); + } + var symbolState = __symbol__('state'); + var symbolValue = __symbol__('value'); + var source = 'Promise.then'; + var UNRESOLVED = null; + var RESOLVED = true; + var REJECTED = false; + var REJECTED_NO_CATCH = 0; + function makeResolver(promise, state) { + return function (v) { + try { + resolvePromise(promise, state, v); + } + catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + }; + } + var once = function () { + var wasCalled = false; + return function wrapper(wrappedFunction) { + return function () { + if (wasCalled) { + return; + } + wasCalled = true; + wrappedFunction.apply(null, arguments); + }; + }; + }; + var TYPE_ERROR = 'Promise resolved with itself'; + var OBJECT = 'object'; + var FUNCTION = 'function'; + var CURRENT_TASK_SYMBOL = __symbol__('currentTask'); + // Promise Resolution + function resolvePromise(promise, state, value) { + var onceWrapper = once(); + if (promise === value) { + throw new TypeError(TYPE_ERROR); + } + if (promise[symbolState] === UNRESOLVED) { + // should only get value.then once based on promise spec. + var then = null; + try { + if (typeof value === OBJECT || typeof value === FUNCTION) { + then = value && value.then; + } + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + return promise; + } + // if (value instanceof ZoneAwarePromise) { + if (state !== REJECTED && value instanceof ZoneAwarePromise && + value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && + value[symbolState] !== UNRESOLVED) { + clearRejectedNoCatch(value); + resolvePromise(promise, value[symbolState], value[symbolValue]); + } + else if (state !== REJECTED && typeof then === FUNCTION) { + try { + then.apply(value, [ + onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) + ]); + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + } + } + else { + promise[symbolState] = state; + var queue = promise[symbolValue]; + promise[symbolValue] = value; + // record task information in value when error occurs, so we can + // do some additional work such as render longStackTrace + if (state === REJECTED && value instanceof Error) { + value[CURRENT_TASK_SYMBOL] = Zone.currentTask; + } + for (var i = 0; i < queue.length;) { + scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); + } + if (queue.length == 0 && state == REJECTED) { + promise[symbolState] = REJECTED_NO_CATCH; + try { + throw new Error('Uncaught (in promise): ' + value + + (value && value.stack ? '\n' + value.stack : '')); + } + catch (err) { + var error_1 = err; + error_1.rejection = value; + error_1.promise = promise; + error_1.zone = Zone.current; + error_1.task = Zone.currentTask; + _uncaughtPromiseErrors.push(error_1); + api.scheduleMicroTask(); // to make sure that it is running + } + } + } + } + // Resolving an already resolved promise is a noop. + return promise; + } + var REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); + function clearRejectedNoCatch(promise) { + if (promise[symbolState] === REJECTED_NO_CATCH) { + // if the promise is rejected no catch status + // and queue.length > 0, means there is a error handler + // here to handle the rejected promise, we should trigger + // windows.rejectionhandled eventHandler or nodejs rejectionHandled + // eventHandler + try { + var handler = Zone[REJECTION_HANDLED_HANDLER]; + if (handler && typeof handler === FUNCTION) { + handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + } + } + catch (err) { + } + promise[symbolState] = REJECTED; + for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { + if (promise === _uncaughtPromiseErrors[i].promise) { + _uncaughtPromiseErrors.splice(i, 1); + } + } + } + } + function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { + clearRejectedNoCatch(promise); + var delegate = promise[symbolState] ? + (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : + (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; + zone.scheduleMicroTask(source, function () { + try { + resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + } + catch (error) { + resolvePromise(chainPromise, false, error); + } + }); + } + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = (function () { + function ZoneAwarePromise(executor) { + var promise = this; + if (!(promise instanceof ZoneAwarePromise)) { + throw new Error('Must be an instanceof Promise.'); + } + promise[symbolState] = UNRESOLVED; + promise[symbolValue] = []; // queue; + try { + executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); + } + catch (error) { + resolvePromise(promise, false, error); + } + } + ZoneAwarePromise.toString = function () { + return ZONE_AWARE_PROMISE_TO_STRING; + }; + ZoneAwarePromise.resolve = function (value) { + return resolvePromise(new this(null), RESOLVED, value); + }; + ZoneAwarePromise.reject = function (error) { + return resolvePromise(new this(null), REJECTED, error); + }; + ZoneAwarePromise.race = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + _a = [res, rej], resolve = _a[0], reject = _a[1]; + var _a; + }); + function onResolve(value) { + promise && (promise = null || resolve(value)); + } + function onReject(error) { + promise && (promise = null || reject(error)); + } + for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { + var value = values_1[_i]; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + return promise; + }; + ZoneAwarePromise.all = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + resolve = res; + reject = rej; + }); + var count = 0; + var resolvedValues = []; + for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { + var value = values_2[_i]; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then((function (index) { return function (value) { + resolvedValues[index] = value; + count--; + if (!count) { + resolve(resolvedValues); + } + }; })(count), reject); + count++; + } + if (!count) + resolve(resolvedValues); + return promise; + }; + ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { + var chainPromise = new this.constructor(null); + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); + } + return chainPromise; + }; + ZoneAwarePromise.prototype.catch = function (onRejected) { + return this.then(null, onRejected); + }; + return ZoneAwarePromise; + }()); + // Protect against aggressive optimizers dropping seemingly unused properties. + // E.g. Closure Compiler in advanced mode. + ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; + ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; + ZoneAwarePromise['race'] = ZoneAwarePromise.race; + ZoneAwarePromise['all'] = ZoneAwarePromise.all; + var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + var desc = Object.getOwnPropertyDescriptor(global, 'Promise'); + if (!desc || desc.configurable) { + desc && delete desc.writable; + desc && delete desc.value; + if (!desc) { + desc = { configurable: true, enumerable: true }; + } + desc.get = function () { + // if we already set ZoneAwarePromise, use patched one + // otherwise return native one. + return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; + }; + desc.set = function (NewNativePromise) { + if (NewNativePromise === ZoneAwarePromise) { + // if the NewNativePromise is ZoneAwarePromise + // save to global + global[ZONE_AWARE_PROMISE] = NewNativePromise; + } + else { + // if the NewNativePromise is not ZoneAwarePromise + // for example: after load zone.js, some library just + // set es6-promise to global, if we set it to global + // directly, assertZonePatched will fail and angular + // will not loaded, so we just set the NewNativePromise + // to global[symbolPromise], so the result is just like + // we load ES6 Promise before zone.js + global[symbolPromise] = NewNativePromise; + if (!NewNativePromise.prototype[symbolThen]) { + patchThen(NewNativePromise); + } + api.setNativePromise(NewNativePromise); + } + }; + Object.defineProperty(global, 'Promise', desc); + } + global['Promise'] = ZoneAwarePromise; + var symbolThenPatched = __symbol__('thenPatched'); + function patchThen(Ctor) { + var proto = Ctor.prototype; + var originalThen = proto.then; + // Keep a reference to the original method. + proto[symbolThen] = originalThen; + // check Ctor.prototype.then propertyDescritor is writable or not + // in meteor env, writable is false, we have to make it to be true. + var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); + if (prop && prop.writable === false && prop.configurable) { + Object.defineProperty(Ctor.prototype, 'then', { writable: true }); + } + Ctor.prototype.then = function (onResolve, onReject) { + var _this = this; + var wrapped = new ZoneAwarePromise(function (resolve, reject) { + originalThen.call(_this, resolve, reject); + }); + return wrapped.then(onResolve, onReject); + }; + Ctor[symbolThenPatched] = true; + } + function zoneify(fn) { + return function () { + var resultPromise = fn.apply(this, arguments); + if (resultPromise instanceof ZoneAwarePromise) { + return resultPromise; + } + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); + } + return resultPromise; + }; + } + if (NativePromise) { + patchThen(NativePromise); + var fetch_1 = global['fetch']; + if (typeof fetch_1 == FUNCTION) { + global['fetch'] = zoneify(fetch_1); + } + } + // This is not part of public API, but it is useful for tests, so we expose it. + Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; + return ZoneAwarePromise; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {globalThis} + */ +var NEWLINE = '\n'; +var IGNORE_FRAMES = {}; +var creationTrace = '__creationTrace__'; +var ERROR_TAG = 'STACKTRACE TRACKING'; +var SEP_TAG = '__SEP_TAG__'; +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = (function () { + function LongStackTrace() { + this.error = getStacktrace(); + this.timestamp = new Date(); + } + return LongStackTrace; +}()); +function getStacktraceWithUncaughtError() { + return new Error(ERROR_TAG); +} +function getStacktraceWithCaughtError() { + try { + throw getStacktraceWithUncaughtError(); + } + catch (err) { + return err; + } +} +// Some implementations of exception handling don't create a stack trace if the exception +// isn't thrown, however it's faster not to actually throw the exception. +var error = getStacktraceWithUncaughtError(); +var caughtError = getStacktraceWithCaughtError(); +var getStacktrace = error.stack ? + getStacktraceWithUncaughtError : + (caughtError.stack ? getStacktraceWithCaughtError : getStacktraceWithUncaughtError); +function getFrames(error) { + return error.stack ? error.stack.split(NEWLINE) : []; +} +function addErrorStack(lines, error) { + var trace = getFrames(error); + for (var i = 0; i < trace.length; i++) { + var frame = trace[i]; + // Filter out the Frames which are part of stack capturing. + if (!IGNORE_FRAMES.hasOwnProperty(frame)) { + lines.push(trace[i]); + } + } +} +function renderLongStackTrace(frames, stack) { + var longTrace = [stack ? stack.trim() : '']; + if (frames) { + var timestamp = new Date().getTime(); + for (var i = 0; i < frames.length; i++) { + var traceFrames = frames[i]; + var lastTime = traceFrames.timestamp; + var separator = "____________________Elapsed " + (timestamp - lastTime.getTime()) + " ms; At: " + lastTime; + separator = separator.replace(/[^\w\d]/g, '_'); + longTrace.push(sepTemplate.replace(SEP_TAG, separator)); + addErrorStack(longTrace, traceFrames.error); + timestamp = lastTime.getTime(); + } + } + return longTrace.join(NEWLINE); +} +Zone['longStackTraceZoneSpec'] = { + name: 'long-stack-trace', + longStackTraceLimit: 10, + // add a getLongStackTrace method in spec to + // handle handled reject promise error. + getLongStackTrace: function (error) { + if (!error) { + return undefined; + } + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; + if (!trace) { + return error.stack; + } + return renderLongStackTrace(trace, error.stack); + }, + onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var currentTask = Zone.currentTask; + var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) + task.data = {}; + task.data[creationTrace] = trace; + } + return parentZoneDelegate.scheduleTask(targetZone, task); + }, + onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = error.longStack = longStack; + } + catch (err) { + } + } + } + return parentZoneDelegate.handleError(targetZone, error); + } +}; +function captureStackTraces(stackTraces, count) { + if (count > 0) { + stackTraces.push(getFrames((new LongStackTrace()).error)); + captureStackTraces(stackTraces, count - 1); + } +} +function computeIgnoreFrames() { + if (Error.stackTraceLimit <= 0) { + return; + } + var frames = []; + captureStackTraces(frames, 2); + var frames1 = frames[0]; + var frames2 = frames[1]; + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + if (frame1.indexOf(ERROR_TAG) == -1) { + var match = frame1.match(/^\s*at\s+/); + if (match) { + sepTemplate = match[0] + SEP_TAG + ' (http://localhost)'; + break; + } + } + } + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + var frame2 = frames2[i]; + if (frame1 === frame2) { + IGNORE_FRAMES[frame1] = true; + } + else { + break; + } + } +} +computeIgnoreFrames(); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var ProxyZoneSpec = (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!this.isLoaded()) { + throw new Error("Expected to be running in 'ProxyZone', but it was not found."); + } + return ProxyZoneSpec.get(); + }; + ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { + var _this = this; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + else { + return parentZoneDelegate.fork(targetZone, zoneSpec); + } + }; + ProxyZoneSpec.prototype.onIntercept = function (parentZoneDelegate, currentZone, targetZone, delegate, source) { + if (this._delegateSpec && this._delegateSpec.onIntercept) { + return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source); + } + else { + return parentZoneDelegate.intercept(targetZone, delegate, source); + } + }; + ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + if (this._delegateSpec && this._delegateSpec.onInvoke) { + return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); + } + else { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + }; + ProxyZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + if (this._delegateSpec && this._delegateSpec.onHandleError) { + return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); + } + else { + return parentZoneDelegate.handleError(targetZone, error); + } + }; + ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onScheduleTask) { + return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.scheduleTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + if (this._delegateSpec && this._delegateSpec.onHasTask) { + this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); + } + else { + delegate.hasTask(target, hasTaskState); + } + }; + return ProxyZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['ProxyZoneSpec'] = ProxyZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var SyncTestZoneSpec = (function () { + function SyncTestZoneSpec(namePrefix) { + this.runZone = Zone.current; + this.name = 'syncTestZone for ' + namePrefix; + } + SyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + case 'macroTask': + throw new Error("Cannot call " + task.source + " from within a sync test."); + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + return SyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var AsyncTestZoneSpec = (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this.runZone = Zone.current; + this._finishCallback = finishCallback; + this._failCallback = failCallback; + this.name = 'asyncTestZone for ' + namePrefix; + } + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + // We do this because we would like to catch unhandled rejected promises. + this.runZone.run(function () { + setTimeout(function () { + if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { + _this._finishCallback(); + } + }, 0); + }); + } + }; + // Note - we need to use onInvoke at the moment to call finish when a test is + // fully synchronous. TODO(juliemr): remove this when the logic for + // onHasTask changes and it calls whenever the task queues are dirty. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + this._finishCallbackIfDone(); + } + }; + AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + // Let the parent try to handle the error. + var result = parentZoneDelegate.handleError(targetZone, error); + if (result) { + this._failCallback(error); + this._alreadyErrored = true; + } + return false; + }; + AsyncTestZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + delegate.hasTask(target, hasTaskState); + if (hasTaskState.change == 'microTask') { + this._pendingMicroTasks = hasTaskState.microTask; + this._finishCallbackIfDone(); + } + else if (hasTaskState.change == 'macroTask') { + this._pendingMacroTasks = hasTaskState.macroTask; + this._finishCallbackIfDone(); + } + }; + return AsyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function (global) { + var Scheduler = (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 0; + // Scheduler queue with the tuple of end time and callback function - sorted by end time. + this._schedulerQueue = []; + // Current simulated time in millis. + this._currentTime = 0; + } + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } + if (id === void 0) { id = -1; } + var currentId = id < 0 ? this.nextId++ : id; + var endTime = this._currentTime + delay; + // Insert so that scheduler queue remains sorted by end time. + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; + var i = 0; + for (; i < this._schedulerQueue.length; i++) { + var currentEntry = this._schedulerQueue[i]; + if (newEntry.endTime < currentEntry.endTime) { + break; + } + } + this._schedulerQueue.splice(i, 0, newEntry); + return currentId; + }; + Scheduler.prototype.removeScheduledFunctionWithId = function (id) { + for (var i = 0; i < this._schedulerQueue.length; i++) { + if (this._schedulerQueue[i].id == id) { + this._schedulerQueue.splice(i, 1); + break; + } + } + }; + Scheduler.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + var finalTime = this._currentTime + millis; + var lastCurrentTime = 0; + if (this._schedulerQueue.length === 0 && doTick) { + doTick(millis); + return; + } + while (this._schedulerQueue.length > 0) { + var current = this._schedulerQueue[0]; + if (finalTime < current.endTime) { + // Done processing the queue since it's sorted by endTime. + break; + } + else { + // Time to run scheduled function. Remove it from the head of queue. + var current_1 = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current_1.endTime; + if (doTick) { + doTick(this._currentTime - lastCurrentTime); + } + var retval = current_1.func.apply(global, current_1.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + } + this._currentTime = finalTime; + }; + Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) { + if (limit === void 0) { limit = 20; } + if (flushPeriodic === void 0) { flushPeriodic = false; } + if (flushPeriodic) { + return this.flushPeriodic(doTick); + } + else { + return this.flushNonPeriodic(limit, doTick); + } + }; + Scheduler.prototype.flushPeriodic = function (doTick) { + if (this._schedulerQueue.length === 0) { + return 0; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + var startTime = this._currentTime; + var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; + this.tick(lastTask.endTime - startTime, doTick); + return this._currentTime - startTime; + }; + Scheduler.prototype.flushNonPeriodic = function (limit, doTick) { + var startTime = this._currentTime; + var lastCurrentTime = 0; + var count = 0; + while (this._schedulerQueue.length > 0) { + count++; + if (count > limit) { + throw new Error('flush failed after reaching the limit of ' + limit + + ' tasks. Does your code use a polling timeout?'); + } + // flush only non-periodic timers. + // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. + if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; }) + .length === 0) { + break; + } + var current = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current.endTime; + if (doTick) { + // Update any secondary schedulers like Jasmine mock Date. + doTick(this._currentTime - lastCurrentTime); + } + var retval = current.func.apply(global, current.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + return this._currentTime - startTime; + }; + return Scheduler; + }()); + var FakeAsyncTestZoneSpec = (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + throw new Error('The code should be running in the fakeAsync zone to call this function'); + } + }; + FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) { + var _this = this; + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + fn.apply(global, args); + if (_this._lastError === null) { + if (completers.onSuccess != null) { + completers.onSuccess.apply(global); + } + // Flush microtasks only on success. + _this.flushMicrotasks(); + } + else { + if (completers.onError != null) { + completers.onError.apply(global); + } + } + // Return true if there were no errors, false otherwise. + return _this._lastError === null; + }; + }; + FakeAsyncTestZoneSpec._removeTimer = function (timers, id) { + var index = timers.indexOf(id); + if (index > -1) { + timers.splice(index, 1); + } + }; + FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) { + var _this = this; + return function () { + // Requeue the timer callback if it's not been canceled. + if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); + } + }; + }; + FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { + if (isTimer === void 0) { isTimer = true; } + var removeTimerFn = this._dequeueTimer(this._scheduler.nextId); + // Queue the callback and dequeue the timer on success and error. + var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn }); + var id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer); + if (isTimer) { + this.pendingTimers.push(id); + } + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + var id = this._scheduler.nextId; + var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; + var cb = this._fnAndFlush(fn, completers); + // Use the callback created above to requeue on success. + completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); + // Queue the callback and dequeue the periodic timer only on error. + this._scheduler.scheduleFunction(cb, interval, args, true); + this.pendingPeriodicTimers.push(id); + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () { + var error = this._lastError || this._uncaughtPromiseErrors[0]; + this._uncaughtPromiseErrors.length = 0; + this._lastError = null; + throw error; + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + }; + FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () { + var _this = this; + FakeAsyncTestZoneSpec.assertInZone(); + var flushErrors = function () { + if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) { + // If there is an error stop processing the microtask queue and rethrow the error. + _this._resetLastErrorAndThrow(); + } + }; + while (this._microtasks.length > 0) { + var microtask = this._microtasks.shift(); + microtask.func.apply(microtask.target, microtask.args); + } + flushErrors(); + }; + FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) { + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + return elapsed; + }; + FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + var args = task.data && task.data.args; + // should pass additional arguments to callback if have any + // currently we know process.nextTick will have such additional + // arguments + var addtionalArgs = void 0; + if (args) { + var callbackIndex = task.data.callbackIndex; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: addtionalArgs, + target: task.data && task.data.target + }); + break; + case 'macroTask': + switch (task.source) { + case 'setTimeout': + task.data['handleId'] = + this._setTimeout(task.invoke, task.data['delay'], task.data['args']); + break; + case 'setInterval': + task.data['handleId'] = + this._setInterval(task.invoke, task.data['delay'], task.data['args']); + break; + case 'XMLHttpRequest.send': + throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' + + task.data['url']); + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + // Simulate a requestAnimationFrame by using a setTimeout with 16 ms. + // (60 frames per second) + task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame); + break; + default: + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + switch (task.source) { + case 'setTimeout': + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + return this._clearTimeout(task.data['handleId']); + case 'setInterval': + return this._clearInterval(task.data['handleId']); + default: + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + return FakeAsyncTestZoneSpec; + }()); + // Export the class so that new instances can be created with proper + // constructor params. + Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec; +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * A `TaskTrackingZoneSpec` allows one to track all outstanding Tasks. + * + * This is useful in tests. For example to see which tasks are preventing a test from completing + * or an automated way of releasing all of the event listeners at the end of the test. + */ +var TaskTrackingZoneSpec = (function () { + function TaskTrackingZoneSpec() { + this.name = 'TaskTrackingZone'; + this.microTasks = []; + this.macroTasks = []; + this.eventTasks = []; + this.properties = { 'TaskTrackingZone': this }; + } + TaskTrackingZoneSpec.get = function () { + return Zone.current.get('TaskTrackingZone'); + }; + TaskTrackingZoneSpec.prototype.getTasksFor = function (type) { + switch (type) { + case 'microTask': + return this.microTasks; + case 'macroTask': + return this.macroTasks; + case 'eventTask': + return this.eventTasks; + } + throw new Error('Unknown task format: ' + type); + }; + TaskTrackingZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + task['creationLocation'] = new Error("Task '" + task.type + "' from '" + task.source + "'."); + var tasks = this.getTasksFor(task.type); + tasks.push(task); + return parentZoneDelegate.scheduleTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.cancelTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (task.type === 'eventTask') + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + }; + TaskTrackingZoneSpec.prototype.clearEvents = function () { + while (this.eventTasks.length) { + Zone.current.cancelTask(this.eventTasks[0]); + } + }; + return TaskTrackingZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {missingRequire} + */ +(function (global) { + // Detect and setup WTF. + var wtfTrace = null; + var wtfEvents = null; + var wtfEnabled = (function () { + var wtf = global['wtf']; + if (wtf) { + wtfTrace = wtf.trace; + if (wtfTrace) { + wtfEvents = wtfTrace.events; + return true; + } + } + return false; + })(); + var WtfZoneSpec = (function () { + function WtfZoneSpec() { + this.name = 'WTF'; + } + WtfZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + var retValue = parentZoneDelegate.fork(targetZone, zoneSpec); + WtfZoneSpec.forkInstance(zonePathName(targetZone), retValue.name); + return retValue; + }; + WtfZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + var scope = WtfZoneSpec.invokeScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeScope[source] = + wtfEvents.createScope("Zone:invoke:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)); + }; + WtfZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + return parentZoneDelegate.handleError(targetZone, error); + }; + WtfZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.type + ':' + task.source; + var instance = WtfZoneSpec.scheduleInstance[key]; + if (!instance) { + instance = WtfZoneSpec.scheduleInstance[key] = + wtfEvents.createInstance("Zone:schedule:" + key + "(ascii zone, any data)"); + } + var retValue = parentZoneDelegate.scheduleTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + WtfZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + var source = task.source; + var scope = WtfZoneSpec.invokeTaskScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeTaskScope[source] = + wtfEvents.createScope("Zone:invokeTask:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs)); + }; + WtfZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.source; + var instance = WtfZoneSpec.cancelInstance[key]; + if (!instance) { + instance = WtfZoneSpec.cancelInstance[key] = + wtfEvents.createInstance("Zone:cancel:" + key + "(ascii zone, any options)"); + } + var retValue = parentZoneDelegate.cancelTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + + return WtfZoneSpec; + }()); + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; + function shallowObj(obj, depth) { + if (!depth) + return null; + var out = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = obj[key]; + switch (typeof value) { + case 'object': + var name_1 = value && value.constructor && value.constructor.name; + value = name_1 == Object.name ? shallowObj(value, depth - 1) : name_1; + break; + case 'function': + value = value.name || undefined; + break; + } + out[key] = value; + } + } + return out; + } + function zonePathName(zone) { + var name = zone.name; + zone = zone.parent; + while (zone != null) { + name = zone.name + '::' + name; + zone = zone.parent; + } + return name; + } + Zone['wtfZoneSpec'] = !wtfEnabled ? null : new WtfZoneSpec(); +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +'use strict'; +(function () { + var __extends = function (d, b) { + for (var p in b) + if (b.hasOwnProperty(p)) + d[p] = b[p]; + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs + // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503) + if (!Zone) + throw new Error('Missing: zone.js'); + if (typeof jasmine == 'undefined') + throw new Error('Missing: jasmine.js'); + if (jasmine['__zone_patch__']) + throw new Error('\'jasmine\' has already been patched with \'Zone\'.'); + jasmine['__zone_patch__'] = true; + var SyncTestZoneSpec = Zone['SyncTestZoneSpec']; + var ProxyZoneSpec = Zone['ProxyZoneSpec']; + if (!SyncTestZoneSpec) + throw new Error('Missing: SyncTestZoneSpec'); + if (!ProxyZoneSpec) + throw new Error('Missing: ProxyZoneSpec'); + var ambientZone = Zone.current; + // Create a synchronous-only zone in which to run `describe` blocks in order to raise an + // error if any asynchronous operations are attempted inside of a `describe` but outside of + // a `beforeEach` or `it`. + var syncZone = ambientZone.fork(new SyncTestZoneSpec('jasmine.describe')); + // This is the zone which will be used for running individual tests. + // It will be a proxy zone, so that the tests function can retroactively install + // different zones. + // Example: + // - In beforeEach() do childZone = Zone.current.fork(...); + // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the + // zone outside of fakeAsync it will be able to escape the fakeAsync rules. + // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add + // fakeAsync behavior to the childZone. + var testProxyZone = null; + // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone. + var jasmineEnv = jasmine.getEnv(); + ['describe', 'xdescribe', 'fdescribe'].forEach(function (methodName) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (description, specDefinitions) { + return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions)); + }; + }); + ['it', 'xit', 'fit'].forEach(function (methodName) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (description, specDefinitions, timeout) { + arguments[1] = wrapTestInZone(specDefinitions); + return originalJasmineFn.apply(this, arguments); + }; + }); + ['beforeEach', 'afterEach'].forEach(function (methodName) { + var originalJasmineFn = jasmineEnv[methodName]; + jasmineEnv[methodName] = function (specDefinitions, timeout) { + arguments[0] = wrapTestInZone(specDefinitions); + return originalJasmineFn.apply(this, arguments); + }; + }); + /** + * Gets a function wrapping the body of a Jasmine `describe` block to execute in a + * synchronous-only zone. + */ + function wrapDescribeInZone(describeBody) { + return function () { + return syncZone.run(describeBody, this, arguments); + }; + } + /** + * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to + * execute in a ProxyZone zone. + * This will run in `testProxyZone`. The `testProxyZone` will be reset by the `ZoneQueueRunner` + */ + function wrapTestInZone(testBody) { + // The `done` callback is only passed through if the function expects at least one argument. + // Note we have to make a function with correct number of arguments, otherwise jasmine will + // think that all functions are sync or async. + return testBody && (testBody.length ? function (done) { + return testProxyZone.run(testBody, this, [done]); + } : function () { + return testProxyZone.run(testBody, this); + }); + } + var QueueRunner = jasmine.QueueRunner; + jasmine.QueueRunner = (function (_super) { + __extends(ZoneQueueRunner, _super); + function ZoneQueueRunner(attrs) { + attrs.onComplete = (function (fn) { return function () { + // All functions are done, clear the test zone. + testProxyZone = null; + ambientZone.scheduleMicroTask('jasmine.onComplete', fn); + }; })(attrs.onComplete); + _super.call(this, attrs); + } + ZoneQueueRunner.prototype.execute = function () { + var _this = this; + if (Zone.current !== ambientZone) + throw new Error('Unexpected Zone: ' + Zone.current.name); + testProxyZone = ambientZone.fork(new ProxyZoneSpec()); + if (!Zone.currentTask) { + // if we are not running in a task then if someone would register a + // element.addEventListener and then calling element.click() the + // addEventListener callback would think that it is the top most task and would + // drain the microtask queue on element.click() which would be incorrect. + // For this reason we always force a task when running jasmine tests. + Zone.current.scheduleMicroTask('jasmine.execute().forceTask', function () { return QueueRunner.prototype.execute.call(_this); }); + } + else { + _super.prototype.execute.call(this); + } + }; + return ZoneQueueRunner; + }(QueueRunner)); +})(); + +}))); diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.js b/nativescript-angular/zone-js/dist/zone-nativescript.js index 38616df9f..5d79bf336 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.js @@ -19,12 +19,18 @@ * found in the LICENSE file at https://angular.io/license */ var Zone$1 = (function (global) { + var FUNCTION = 'function'; + var performance = global['performance']; + function mark(name) { + performance && performance['mark'] && performance['mark'](name); + } + function performanceMeasure(name, label) { + performance && performance['measure'] && performance['measure'](name, label); + } + mark('Zone'); if (global['Zone']) { throw new Error('Zone already loaded.'); } - var NO_ZONE = { name: 'NO ZONE' }; - var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; - var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; var Zone = (function () { function Zone(parent, zoneSpec) { this._properties = null; @@ -35,7 +41,7 @@ var Zone$1 = (function (global) { new ZoneDelegate(this, this._parent && this._parent._zoneDelegate, zoneSpec); } Zone.assertZonePatched = function () { - if (global.Promise !== ZoneAwarePromise) { + if (global['Promise'] !== patches['ZoneAwarePromise']) { throw new Error('Zone.js has detected that ZoneAwarePromise `(window|global).Promise` ' + 'has been overwritten.\n' + 'Most likely cause is that a Promise polyfill has been loaded ' + @@ -70,6 +76,17 @@ var Zone$1 = (function (global) { configurable: true }); + Zone.__load_patch = function (name, fn) { + if (patches.hasOwnProperty(name)) { + throw Error('Already loaded patch: ' + name); + } + else if (!global['__Zone_disable_' + name]) { + var perfName = 'Zone:' + name; + mark(perfName); + patches[name] = fn(global, Zone, _api); + performanceMeasure(perfName, perfName); + } + }; Object.defineProperty(Zone.prototype, "parent", { get: function () { return this._parent; @@ -107,7 +124,7 @@ var Zone$1 = (function (global) { return this._zoneDelegate.fork(this, zoneSpec); }; Zone.prototype.wrap = function (callback, source) { - if (typeof callback !== 'function') { + if (typeof callback !== FUNCTION) { throw new Error('Expecting function got: ' + callback); } var _callback = this._zoneDelegate.intercept(this, callback, source); @@ -120,7 +137,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = undefined; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); } @@ -132,7 +149,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = null; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); @@ -148,15 +165,25 @@ var Zone$1 = (function (global) { } }; Zone.prototype.runTask = function (task, applyThis, applyArgs) { - if (task.zone != this) + if (task.zone != this) { throw new Error('A task can only be run in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); + } + // https://github.com/angular/zone.js/issues/778, sometimes eventTask + // will run in notScheduled(canceled) state, we should not try to + // run such kind of task but just return + // we have to define an variable here, if not + // typescript compiler will complain below + var isNotScheduled = task.state === notScheduled; + if (isNotScheduled && task.type === eventTask) { + return; + } var reEntryGuard = task.state != running; reEntryGuard && task._transitionTo(running, scheduled); task.runCount++; var previousTask = _currentTask; _currentTask = task; - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { if (task.type == macroTask && task.data && !task.data.isPeriodic) { task.cancelFn = null; @@ -418,6 +445,7 @@ var Zone$1 = (function (global) { this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty); } catch (err) { + this.handleError(targetZone, err); } }; ZoneDelegate.prototype._updateTaskCount = function (type, count) { @@ -429,12 +457,11 @@ var Zone$1 = (function (global) { } if (prev == 0 || next == 0) { var isEmpty = { - microTask: counts.microTask > 0, - macroTask: counts.macroTask > 0, - eventTask: counts.eventTask > 0, + microTask: counts['microTask'] > 0, + macroTask: counts['macroTask'] > 0, + eventTask: counts['eventTask'] > 0, change: type }; - // TODO(misko): what should happen if it throws? this.hasTask(this.zone, isEmpty); } }; @@ -453,20 +480,31 @@ var Zone$1 = (function (global) { this.cancelFn = cancelFn; this.callback = callback; var self = this; - this.invoke = function () { - _numberOfNestedTaskFrames++; - try { - self.runCount++; - return self.zone.runTask(self, this, arguments); - } - finally { - if (_numberOfNestedTaskFrames == 1) { - drainMicroTaskQueue(); - } - _numberOfNestedTaskFrames--; - } - }; + if (type === eventTask && options && options.isUsingGlobalCallback) { + this.invoke = ZoneTask.invokeTask; + } + else { + this.invoke = function () { + return ZoneTask.invokeTask.apply(global, [self, this, arguments]); + }; + } } + ZoneTask.invokeTask = function (task, target, args) { + if (!task) { + task = this; + } + _numberOfNestedTaskFrames++; + try { + task.runCount++; + return task.zone.runTask(task, target, args); + } + finally { + if (_numberOfNestedTaskFrames == 1) { + drainMicroTaskQueue(); + } + _numberOfNestedTaskFrames--; + } + }; Object.defineProperty(ZoneTask.prototype, "zone", { get: function () { return this._zone; @@ -512,7 +550,6 @@ var Zone$1 = (function (global) { type: this.type, state: this.state, source: this.source, - data: this.data, zone: this.zone.name, invoke: this.invoke, scheduleFn: this.scheduleFn, @@ -523,63 +560,35 @@ var Zone$1 = (function (global) { }; return ZoneTask; }()); - var ZoneFrame = (function () { - function ZoneFrame(parent, zone) { - this.parent = parent; - this.zone = zone; - } - return ZoneFrame; - }()); - function __symbol__(name) { - return '__zone_symbol__' + name; - } - + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// MICROTASK QUEUE + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// var symbolSetTimeout = __symbol__('setTimeout'); var symbolPromise = __symbol__('Promise'); var symbolThen = __symbol__('then'); - var _currentZoneFrame = new ZoneFrame(null, new Zone(null, null)); - var _currentTask = null; var _microTaskQueue = []; var _isDrainingMicrotaskQueue = false; - var _uncaughtPromiseErrors = []; - var _numberOfNestedTaskFrames = 0; - function scheduleQueueDrain() { + var nativeMicroTaskQueuePromise; + function scheduleMicroTask(task) { // if we are not running in any task, and there has not been anything scheduled // we must bootstrap the initial task creation by manually scheduling the drain if (_numberOfNestedTaskFrames === 0 && _microTaskQueue.length === 0) { // We are not running in Task, so we need to kickstart the microtask queue. - if (global[symbolPromise]) { - global[symbolPromise].resolve(0)[symbolThen](drainMicroTaskQueue); + if (!nativeMicroTaskQueuePromise) { + if (global[symbolPromise]) { + nativeMicroTaskQueuePromise = global[symbolPromise].resolve(0); + } + } + if (nativeMicroTaskQueuePromise) { + nativeMicroTaskQueuePromise[symbolThen](drainMicroTaskQueue); } else { global[symbolSetTimeout](drainMicroTaskQueue, 0); } } - } - function scheduleMicroTask(task) { - scheduleQueueDrain(); - _microTaskQueue.push(task); - } - function consoleError(e) { - if (Zone[__symbol__('ignoreConsoleErrorUncaughtError')]) { - return; - } - var rejection = e && e.rejection; - if (rejection) { - console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); - } - console.error(e); - } - function handleUnhandledRejection(e) { - consoleError(e); - try { - var handler = Zone[__symbol__('unhandledPromiseRejectionHandler')]; - if (handler && typeof handler === 'function') { - handler.apply(this, [e]); - } - } - catch (err) { - } + task && _microTaskQueue.push(task); } function drainMicroTaskQueue() { if (!_isDrainingMicrotaskQueue) { @@ -593,731 +602,46 @@ var Zone$1 = (function (global) { task.zone.runTask(task, null, null); } catch (error) { - consoleError(error); + _api.onUnhandledError(error); } } } - while (_uncaughtPromiseErrors.length) { - var _loop_1 = function () { - var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); - try { - uncaughtPromiseError.zone.runGuarded(function () { - throw uncaughtPromiseError; - }); - } - catch (error) { - handleUnhandledRejection(error); - } - }; - while (_uncaughtPromiseErrors.length) { - _loop_1(); - } - } + var showError = !Zone[__symbol__('ignoreConsoleErrorUncaughtError')]; + _api.microtaskDrainDone(); _isDrainingMicrotaskQueue = false; } } - Zone.drainMicroTaskQueue = drainMicroTaskQueue; - function isThenable(value) { - return value && value.then; - } - function forwardResolution(value) { - return value; - } - function forwardRejection(rejection) { - return ZoneAwarePromise.reject(rejection); - } - var symbolState = __symbol__('state'); - var symbolValue = __symbol__('value'); - var source = 'Promise.then'; - var UNRESOLVED = null; - var RESOLVED = true; - var REJECTED = false; - var REJECTED_NO_CATCH = 0; - function makeResolver(promise, state) { - return function (v) { - try { - resolvePromise(promise, state, v); - } - catch (err) { - resolvePromise(promise, false, err); - } - // Do not return value or you will break the Promise spec. - }; - } - var once = function () { - var wasCalled = false; - return function wrapper(wrappedFunction) { - return function () { - if (wasCalled) { - return; - } - wasCalled = true; - wrappedFunction.apply(null, arguments); - }; - }; - }; - // Promise Resolution - function resolvePromise(promise, state, value) { - var onceWrapper = once(); - if (promise === value) { - throw new TypeError('Promise resolved with itself'); - } - if (promise[symbolState] === UNRESOLVED) { - // should only get value.then once based on promise spec. - var then = null; - try { - if (typeof value === 'object' || typeof value === 'function') { - then = value && value.then; - } - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - return promise; - } - // if (value instanceof ZoneAwarePromise) { - if (state !== REJECTED && value instanceof ZoneAwarePromise && - value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && - value[symbolState] !== UNRESOLVED) { - clearRejectedNoCatch(value); - resolvePromise(promise, value[symbolState], value[symbolValue]); - } - else if (state !== REJECTED && typeof then === 'function') { - try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - } - } - else { - promise[symbolState] = state; - var queue = promise[symbolValue]; - promise[symbolValue] = value; - // record task information in value when error occurs, so we can - // do some additional work such as render longStackTrace - if (state === REJECTED && value instanceof Error) { - value[__symbol__('currentTask')] = Zone.currentTask; - } - for (var i = 0; i < queue.length;) { - scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); - } - if (queue.length == 0 && state == REJECTED) { - promise[symbolState] = REJECTED_NO_CATCH; - try { - throw new Error('Uncaught (in promise): ' + value + - (value && value.stack ? '\n' + value.stack : '')); - } - catch (err) { - var error_1 = err; - error_1.rejection = value; - error_1.promise = promise; - error_1.zone = Zone.current; - error_1.task = Zone.currentTask; - _uncaughtPromiseErrors.push(error_1); - scheduleQueueDrain(); - } - } - } - } - // Resolving an already resolved promise is a noop. - return promise; - } - function clearRejectedNoCatch(promise) { - if (promise[symbolState] === REJECTED_NO_CATCH) { - // if the promise is rejected no catch status - // and queue.length > 0, means there is a error handler - // here to handle the rejected promise, we should trigger - // windows.rejectionhandled eventHandler or nodejs rejectionHandled - // eventHandler - try { - var handler = Zone[__symbol__('rejectionHandledHandler')]; - if (handler && typeof handler === 'function') { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); - } - } - catch (err) { - } - promise[symbolState] = REJECTED; - for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { - if (promise === _uncaughtPromiseErrors[i].promise) { - _uncaughtPromiseErrors.splice(i, 1); - } - } - } - } - function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { - clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? - (typeof onFulfilled === 'function') ? onFulfilled : forwardResolution : - (typeof onRejected === 'function') ? onRejected : forwardRejection; - zone.scheduleMicroTask(source, function () { - try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); - } - catch (error) { - resolvePromise(chainPromise, false, error); - } - }); - } - var ZoneAwarePromise = (function () { - function ZoneAwarePromise(executor) { - var promise = this; - if (!(promise instanceof ZoneAwarePromise)) { - throw new Error('Must be an instanceof Promise.'); - } - promise[symbolState] = UNRESOLVED; - promise[symbolValue] = []; // queue; - try { - executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); - } - catch (error) { - resolvePromise(promise, false, error); - } - } - ZoneAwarePromise.toString = function () { - return 'function ZoneAwarePromise() { [native code] }'; - }; - ZoneAwarePromise.resolve = function (value) { - return resolvePromise(new this(null), RESOLVED, value); - }; - ZoneAwarePromise.reject = function (error) { - return resolvePromise(new this(null), REJECTED, error); - }; - ZoneAwarePromise.race = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - _a = [res, rej], resolve = _a[0], reject = _a[1]; - var _a; - }); - function onResolve(value) { - promise && (promise = null || resolve(value)); - } - function onReject(error) { - promise && (promise = null || reject(error)); - } - for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { - var value = values_1[_i]; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then(onResolve, onReject); - } - return promise; - }; - ZoneAwarePromise.all = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - var count = 0; - var resolvedValues = []; - for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { - var value = values_2[_i]; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then((function (index) { return function (value) { - resolvedValues[index] = value; - count--; - if (!count) { - resolve(resolvedValues); - } - }; })(count), reject); - count++; - } - if (!count) - resolve(resolvedValues); - return promise; - }; - ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { - var chainPromise = new this.constructor(null); - var zone = Zone.current; - if (this[symbolState] == UNRESOLVED) { - this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); - } - else { - scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); - } - return chainPromise; - }; - ZoneAwarePromise.prototype.catch = function (onRejected) { - return this.then(null, onRejected); - }; - return ZoneAwarePromise; - }()); - // Protect against aggressive optimizers dropping seemingly unused properties. - // E.g. Closure Compiler in advanced mode. - ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; - ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; - ZoneAwarePromise['race'] = ZoneAwarePromise.race; - ZoneAwarePromise['all'] = ZoneAwarePromise.all; - var NativePromise = global[symbolPromise] = global['Promise']; - global['Promise'] = ZoneAwarePromise; - var symbolThenPatched = __symbol__('thenPatched'); - function patchThen(Ctor) { - var proto = Ctor.prototype; - var originalThen = proto.then; - // Keep a reference to the original method. - proto[symbolThen] = originalThen; - Ctor.prototype.then = function (onResolve, onReject) { - var _this = this; - var wrapped = new ZoneAwarePromise(function (resolve, reject) { - originalThen.call(_this, resolve, reject); - }); - return wrapped.then(onResolve, onReject); - }; - Ctor[symbolThenPatched] = true; - } - function zoneify(fn) { - return function () { - var resultPromise = fn.apply(this, arguments); - if (resultPromise instanceof ZoneAwarePromise) { - return resultPromise; - } - var Ctor = resultPromise.constructor; - if (!Ctor[symbolThenPatched]) { - patchThen(Ctor); - } - return resultPromise; - }; - } - if (NativePromise) { - patchThen(NativePromise); - var fetch_1 = global['fetch']; - if (typeof fetch_1 == 'function') { - global['fetch'] = zoneify(fetch_1); - } - } - // This is not part of public API, but it is usefull for tests, so we expose it. - Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; - /* - * This code patches Error so that: - * - It ignores un-needed stack frames. - * - It Shows the associated Zone for reach frame. - */ - var FrameType; - (function (FrameType) { - /// Skip this frame when printing out stack - FrameType[FrameType["blackList"] = 0] = "blackList"; - /// This frame marks zone transition - FrameType[FrameType["transition"] = 1] = "transition"; - })(FrameType || (FrameType = {})); - var NativeError = global[__symbol__('Error')] = global.Error; - // Store the frames which should be removed from the stack frames - var blackListedStackFrames = {}; - // We must find the frame where Error was created, otherwise we assume we don't understand stack - // the frame will be an array, because Error with new or without new will - // have different stack frames. - var zoneAwareErrorStartFrames = []; - global.Error = ZoneAwareError; - var stackRewrite = 'stackRewrite'; - // some functions are not easily to be detected here, - // for example Timeout.ZoneTask.invoke, if we want to detect those functions - // by detect zone, we have to run all patched APIs, it is too risky - // so for those functions, just check whether the stack contains the string or not. - var otherZoneAwareFunctionNames = [ - 'ZoneTask.invoke', 'ZoneAware', 'getStacktraceWithUncaughtError', 'new LongStackTrace', - 'long-stack-trace' - ]; - function attachZoneAndRemoveInternalZoneFrames(error) { - // Save original stack trace - error.originalStack = error.stack; - // Process the stack trace and rewrite the frames. - if (ZoneAwareError[stackRewrite] && error.originalStack) { - var frames_1 = error.originalStack.split('\n'); - var zoneFrame = _currentZoneFrame; - var i_1 = 0; - // Find the first frame - while (i_1 < frames_1.length && - zoneAwareErrorStartFrames.filter(function (zf) { return zf.trim() === frames_1[i_1].trim(); }).length === 0) { - i_1++; - } - var _loop_2 = function () { - // trim here because blackListedStackFrames store the trimmed frames - var frame = frames_1[i_1].trim(); - if (frame) { - var frameType = blackListedStackFrames.hasOwnProperty(frame) && blackListedStackFrames[frame]; - if (frameType === FrameType.blackList) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (otherZoneAwareFunctionNames - .filter(function (f) { return frame.toLowerCase().indexOf(f.toLowerCase()) !== -1; }) - .length > 0) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (frameType === FrameType.transition) { - if (zoneFrame.parent) { - // This is the special frame where zone changed. Print and process it accordingly - zoneFrame = zoneFrame.parent; - } - else { - zoneFrame = null; - } - frames_1.splice(i_1, 1); - i_1--; - } - else { - frames_1[i_1] += " [" + zoneFrame.zone.name + "]"; - } - } - }; - for (; i_1 < frames_1.length && zoneFrame; i_1++) { - _loop_2(); - } - var finalStack = frames_1.join('\n'); - try { - error.stack = error.zoneAwareStack = finalStack; - } - catch (nonWritableErr) { - // in some browser, the error.stack is readonly such as PhantomJS - // so we need to store the stack frames to zoneAwareError directly - } - } - } - /** - * This is ZoneAwareError which processes the stack frame and cleans up extra frames as well as - * adds zone information to it. - */ - function ZoneAwareError() { - // We always have to return native error otherwise the browser console will not work. - var error = NativeError.apply(this, arguments); - if (!error.stack) { - // in IE, the error.stack will be undefined - // when error was constructed, it will only - // be available when throw - try { - throw error; - } - catch (err) { - error = err; - } - } - // 1. attach zone information to stack frame - // 2. remove zone internal stack frames - attachZoneAndRemoveInternalZoneFrames(error); - return error; - } - // Copy the prototype so that instanceof operator works as expected - ZoneAwareError.prototype = NativeError.prototype; - ZoneAwareError[Zone.__symbol__('blacklistedStackFrames')] = blackListedStackFrames; - ZoneAwareError[stackRewrite] = false; - // those properties need special handling - var specialPropertyNames = ['stackTraceLimit', 'captureStackTrace', 'prepareStackTrace']; - // those properties of NativeError should be set to ZoneAwareError - var nativeErrorProperties = Object.keys(NativeError); - if (nativeErrorProperties) { - nativeErrorProperties.forEach(function (prop) { - if (specialPropertyNames.filter(function (sp) { return sp === prop; }).length === 0) { - Object.defineProperty(ZoneAwareError, prop, { - get: function () { - return NativeError[prop]; - }, - set: function (value) { - NativeError[prop] = value; - } - }); - } - }); - } - if (NativeError.hasOwnProperty('stackTraceLimit')) { - // Extend default stack limit as we will be removing few frames. - NativeError.stackTraceLimit = Math.max(NativeError.stackTraceLimit, 15); - // make sure that ZoneAwareError has the same property which forwards to NativeError. - Object.defineProperty(ZoneAwareError, 'stackTraceLimit', { - get: function () { - return NativeError.stackTraceLimit; - }, - set: function (value) { - return NativeError.stackTraceLimit = value; - } - }); - } - if (NativeError.hasOwnProperty('captureStackTrace')) { - Object.defineProperty(ZoneAwareError, 'captureStackTrace', { - // add named function here because we need to remove this - // stack frame when prepareStackTrace below - value: function zoneCaptureStackTrace(targetObject, constructorOpt) { - NativeError.captureStackTrace(targetObject, constructorOpt); - } - }); - } - Object.defineProperty(ZoneAwareError, 'prepareStackTrace', { - get: function () { - return NativeError.prepareStackTrace; - }, - set: function (value) { - if (!value || typeof value !== 'function') { - return NativeError.prepareStackTrace = value; - } - return NativeError.prepareStackTrace = function (error, structuredStackTrace) { - // remove additional stack information from ZoneAwareError.captureStackTrace - if (structuredStackTrace) { - for (var i = 0; i < structuredStackTrace.length; i++) { - var st = structuredStackTrace[i]; - // remove the first function which name is zoneCaptureStackTrace - if (st.getFunctionName() === 'zoneCaptureStackTrace') { - structuredStackTrace.splice(i, 1); - break; - } - } - } - return value.apply(this, [error, structuredStackTrace]); - }; - } - }); - // Now we need to populate the `blacklistedStackFrames` as well as find the - // run/runGuraded/runTask frames. This is done by creating a detect zone and then threading - // the execution through all of the above methods so that we can look at the stack trace and - // find the frames of interest. - var detectZone = Zone.current.fork({ - name: 'detect', - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - // Here only so that it will show up in the stack frame so that it can be black listed. - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// BOOTSTRAP + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + var NO_ZONE = { name: 'NO ZONE' }; + var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; + var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; + var patches = {}; + var _api = { + symbol: __symbol__, + currentZoneFrame: function () { return _currentZoneFrame; }, + onUnhandledError: noop, + microtaskDrainDone: noop, + scheduleMicroTask: scheduleMicroTask, + showUncaughtError: function () { return !Zone[__symbol__('ignoreConsoleErrorUncaughtError')]; }, + patchEventTarget: function () { return []; }, + patchOnProperties: noop, + patchMethod: function () { return noop; }, + setNativePromise: function (NativePromise) { + nativeMicroTaskQueuePromise = NativePromise.resolve(0); }, - onHandleError: function (parentZD, current, target, error) { - if (error.originalStack && Error === ZoneAwareError) { - var frames_2 = error.originalStack.split(/\n/); - var runFrame = false, runGuardedFrame = false, runTaskFrame = false; - while (frames_2.length) { - var frame = frames_2.shift(); - // On safari it is possible to have stack frame with no line number. - // This check makes sure that we don't filter frames on name only (must have - // linenumber) - if (/:\d+:\d+/.test(frame)) { - // Get rid of the path so that we don't accidentally find function name in path. - // In chrome the separator is `(` and `@` in FF and safari - // Chrome: at Zone.run (zone.js:100) - // Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24) - // FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24 - // Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24 - var fnName = frame.split('(')[0].split('@')[0]; - var frameType = FrameType.transition; - if (fnName.indexOf('ZoneAwareError') !== -1) { - // we found the ZoneAwareError start frame - // the frame will be different when call Error(...) - // and new Error(...), so we store them both - zoneAwareErrorStartFrames.push(frame); - } - if (fnName.indexOf('runGuarded') !== -1) { - runGuardedFrame = true; - } - else if (fnName.indexOf('runTask') !== -1) { - runTaskFrame = true; - } - else if (fnName.indexOf('run') !== -1) { - runFrame = true; - } - else { - frameType = FrameType.blackList; - } - blackListedStackFrames[frame.trim()] = frameType; - // Once we find all of the frames we can stop looking. - if (runFrame && runGuardedFrame && runTaskFrame) { - ZoneAwareError[stackRewrite] = true; - break; - } - } - } - } - return false; - } - }); - // carefully constructor a stack frame which contains all of the frames of interest which - // need to be detected and blacklisted. - // use this method to handle - // 1. IE issue, the error.stack can only be not undefined after throw - // 2. handle Error(...) without new options - var throwError = function (message, withNew) { - try { - if (withNew) { - throw new Error(message); - } - else { - throw Error(message); - } - } - catch (err) { - return err; - } - }; - var nativeStackTraceLimit = NativeError.stackTraceLimit; - // in some system/browser, some additional stack frames - // will be generated (such as inline function) - // so the the stack frame to check ZoneAwareError Start - // maybe ignored because the frame's number will exceed - // stackTraceLimit, so we just set stackTraceLimit to 100 - // and reset after all detect work is done. - NativeError.stackTraceLimit = 100; - var detectRunFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames', true); - }); - }); - }; - var detectRunWithoutNewFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames'); - }); - }); }; - // Cause the error to extract the stack frames. - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunFn, null, function () { return null; }, null)); - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunWithoutNewFn, null, function () { return null; }, null)); - function handleDetectError(error) { - var frames = error.stack ? error.stack.split(/\n/) : []; - while (frames.length) { - var frame = frames.shift(); - // On safari it is possible to have stack frame with no line number. - // This check makes sure that we don't filter frames on name only (must have - // linenumber) - var trimmedFrame = frame.trim().split('[')[0].trim(); - if (/:\d+:\d+/.test(trimmedFrame) && !blackListedStackFrames.hasOwnProperty(trimmedFrame)) { - blackListedStackFrames[trimmedFrame] = FrameType.blackList; - } - // when we found runGuarded or runTask, we should stop - // otherwise we will store some stack frames like - // module.load, require and something like that - var fnName = frame.split('(')[0].split('@')[0]; - if (fnName.indexOf('runGuarded') !== -1) { - break; - } - else if (fnName.indexOf('runTask') !== -1) { - break; - } - } + var _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + var _currentTask = null; + var _numberOfNestedTaskFrames = 0; + function noop() { } + function __symbol__(name) { + return '__zone_symbol__' + name; } - var detectEmptyZone = Zone.root.fork({ - name: 'detectEmptyZone', - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; - } - }); - var detectZoneWithCallbacks = Zone.root.fork({ - name: 'detectCallbackZone', - onFork: function (parentDelegate, currentZone, targetZone, zoneSpec) { - // we need to generate Error with or without new - handleDetectError(throwError('onFork')); - handleDetectError(throwError('onFork', false)); - return parentDelegate.fork(targetZone, zoneSpec); - }, - onIntercept: function (parentDelegate, currentZone, targetZone, delegate, source) { - handleDetectError(throwError('onIntercept')); - handleDetectError(throwError('onIntercept', false)); - return parentDelegate.intercept(targetZone, delegate, source); - }, - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - handleDetectError(throwError('onInvoke')); - handleDetectError(throwError('onInvoke', false)); - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); - }, - onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onScheduleTask')); - handleDetectError(throwError('onScheduleTask', false)); - return parentZoneDelegate.scheduleTask(targetZone, task); - }, - onInvokeTask: function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { - handleDetectError(throwError('onInvokeTask')); - handleDetectError(throwError('onInvokeTask', false)); - return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); - }, - onCancelTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onCancelTask')); - handleDetectError(throwError('onCancelTask', false)); - return parentZoneDelegate.cancelTask(targetZone, task); - }, - onHasTask: function (delegate, current, target, hasTaskState) { - handleDetectError(throwError('onHasTask')); - handleDetectError(throwError('onHasTask', false)); - return delegate.hasTask(target, hasTaskState); - }, - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; - } - }); - var detectFn = function () { - throw throwError('zoneAwareFrames'); - }; - var detectWithoutNewFn = function () { - throw throwError('zoneAwareFrames', false); - }; - var detectPromiseFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - }; - var detectPromiseWithoutNewFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); - }); - }; - var detectPromiseCaughtFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - p.catch(function (err) { - throw err; - }); - }; - var detectPromiseCaughtWithoutNewFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); - }); - p.catch(function (err) { - throw err; - }); - }; - // Cause the error to extract the stack frames. - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectFn); - }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectFn); - }); - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectEmptyZone.runGuarded(detectPromiseFn); - detectZoneWithCallbacks.runGuarded(detectPromiseFn); - detectEmptyZone.runGuarded(detectPromiseWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseWithoutNewFn); - detectEmptyZone.runGuarded(detectPromiseCaughtFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtFn); - detectEmptyZone.runGuarded(detectPromiseCaughtWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtWithoutNewFn); - NativeError.stackTraceLimit = nativeStackTraceLimit; + performanceMeasure('Zone', 'Zone'); return global['Zone'] = Zone; })(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); @@ -1331,270 +655,46 @@ var Zone$1 = (function (global) { /** * Suppress closure compiler errors about unknown 'Zone' variable * @fileoverview - * @suppress {undefinedVars,globalThis} + * @suppress {undefinedVars,globalThis,missingRequire} */ -var zoneSymbol = function (n) { return "__zone_symbol__" + n; }; +var zoneSymbol = Zone.__symbol__; var _global = typeof window === 'object' && window || typeof self === 'object' && self || global; +var FUNCTION = 'function'; +var UNDEFINED = 'undefined'; -var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); -var isNode = (!('nw' in _global) && typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]'); - -// we are in electron of nw, so we are both browser and nodejs -var isMix = typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]' && !isWebWorker && - !!(typeof window !== 'undefined' && window['HTMLElement']); -function patchProperty(obj, prop) { - var desc = Object.getOwnPropertyDescriptor(obj, prop) || { enumerable: true, configurable: true }; - var originalDesc = Object.getOwnPropertyDescriptor(obj, 'original' + prop); - if (!originalDesc && desc.get) { - Object.defineProperty(obj, 'original' + prop, { enumerable: false, configurable: true, get: desc.get }); - } - // A property descriptor cannot have getter/setter and be writable - // deleting the writable and value properties avoids this error: - // - // TypeError: property descriptors must not specify a value or be writable when a - // getter or setter has been specified - delete desc.writable; - delete desc.value; - // substr(2) cuz 'onclick' -> 'click', etc - var eventName = prop.substr(2); - var _prop = zoneSymbol('_' + prop); - desc.set = function (fn) { - if (this[_prop]) { - this.removeEventListener(eventName, this[_prop]); - } - if (typeof fn === 'function') { - var wrapFn = function (event) { - var result; - result = fn.apply(this, arguments); - if (result != undefined && !result) - event.preventDefault(); - }; - this[_prop] = wrapFn; - this.addEventListener(eventName, wrapFn, false); - } - else { - this[_prop] = null; - } - }; - // The getter would return undefined for unassigned properties but the default value of an - // unassigned property is null - desc.get = function () { - var r = this[_prop] || null; - // result will be null when use inline event attribute, - // such as - // because the onclick function is internal raw uncompiled handler - // the onclick will be evaluated when first time event was triggered or - // the property is accessed, https://github.com/angular/zone.js/issues/525 - // so we should use original native get to retrieve the handler - if (r === null) { - if (originalDesc && originalDesc.get) { - r = originalDesc.get.apply(this, arguments); - if (r) { - desc.set.apply(this, [r]); - if (typeof this['removeAttribute'] === 'function') { - this.removeAttribute(prop); - } - } - } - } - return this[_prop] || null; - }; - Object.defineProperty(obj, prop, desc); -} - -function patchOnProperties(obj, properties) { - var onProperties = []; - for (var prop in obj) { - if (prop.substr(0, 2) == 'on') { - onProperties.push(prop); - } +function isPropertyWritable(propertyDesc) { + if (!propertyDesc) { + return true; } - for (var j = 0; j < onProperties.length; j++) { - patchProperty(obj, onProperties[j]); + if (propertyDesc.writable === false) { + return false; } - if (properties) { - for (var i = 0; i < properties.length; i++) { - patchProperty(obj, 'on' + properties[i]); - } + if (typeof propertyDesc.get === FUNCTION && typeof propertyDesc.set === UNDEFINED) { + return false; } + return true; } +var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); +// Make sure to access `process` through `_global` so that WebPack does not accidently browserify +// this code. +var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]'); -var EVENT_TASKS = zoneSymbol('eventTasks'); -// For EventTarget -var ADD_EVENT_LISTENER = 'addEventListener'; -var REMOVE_EVENT_LISTENER = 'removeEventListener'; -function findExistingRegisteredTask(target, handler, name, capture, remove) { - var eventTasks = target[EVENT_TASKS]; - if (eventTasks) { - for (var i = 0; i < eventTasks.length; i++) { - var eventTask = eventTasks[i]; - var data = eventTask.data; - var listener = data.handler; - if ((data.handler === handler || listener.listener === handler) && - data.useCapturing === capture && data.eventName === name) { - if (remove) { - eventTasks.splice(i, 1); - } - return eventTask; - } - } - } - return null; -} -function attachRegisteredEvent(target, eventTask, isPrepend) { - var eventTasks = target[EVENT_TASKS]; - if (!eventTasks) { - eventTasks = target[EVENT_TASKS] = []; - } - if (isPrepend) { - eventTasks.unshift(eventTask); - } - else { - eventTasks.push(eventTask); - } -} -var defaultListenerMetaCreator = function (self, args) { - return { - useCapturing: args[2], - eventName: args[0], - handler: args[1], - target: self || _global, - name: args[0], - invokeAddFunc: function (addFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[addFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[addFnSymbol](this.eventName, delegate, this.useCapturing); - } - }, - invokeRemoveFunc: function (removeFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[removeFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[removeFnSymbol](this.eventName, delegate, this.useCapturing); - } - } - }; -}; -function makeZoneAwareAddListener(addFnName, removeFnName, useCapturingParam, allowDuplicates, isPrepend, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (allowDuplicates === void 0) { allowDuplicates = false; } - if (isPrepend === void 0) { isPrepend = false; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var addFnSymbol = zoneSymbol(addFnName); - var removeFnSymbol = zoneSymbol(removeFnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - function scheduleEventListener(eventTask) { - var meta = eventTask.data; - attachRegisteredEvent(meta.target, eventTask, isPrepend); - return meta.invokeAddFunc(addFnSymbol, eventTask); - } - function cancelEventListener(eventTask) { - var meta = eventTask.data; - findExistingRegisteredTask(meta.target, eventTask.invoke, meta.eventName, meta.useCapturing, true); - return meta.invokeRemoveFunc(removeFnSymbol, eventTask); - } - return function zoneAwareAddListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var delegate = null; - if (typeof data.handler == 'function') { - delegate = data.handler; - } - else if (data.handler && data.handler.handleEvent) { - delegate = function (event) { return data.handler.handleEvent(event); }; - } - var validZoneHandler = false; - try { - // In cross site contexts (such as WebDriver frameworks like Selenium), - // accessing the handler object here will cause an exception to be thrown which - // will fail tests prematurely. - validZoneHandler = data.handler && data.handler.toString() === '[object FunctionWrapper]'; - } - catch (error) { - // Returning nothing here is fine, because objects in a cross-site context are unusable - return; - } - // Ignore special listeners of IE11 & Edge dev tools, see - // https://github.com/angular/zone.js/issues/150 - if (!delegate || validZoneHandler) { - return data.invokeAddFunc(addFnSymbol, data.handler); - } - if (!allowDuplicates) { - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, false); - if (eventTask) { - // we already registered, so this will have noop. - return data.invokeAddFunc(addFnSymbol, eventTask); - } - } - var zone = Zone.current; - var source = data.target.constructor['name'] + '.' + addFnName + ':' + data.eventName; - zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener); - }; -} -function makeZoneAwareRemoveListener(fnName, useCapturingParam, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var symbol = zoneSymbol(fnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - return function zoneAwareRemoveListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, true); - if (eventTask) { - eventTask.zone.cancelTask(eventTask); - } - else { - data.invokeRemoveFunc(symbol, data.handler); - } - }; -} +// we are in electron of nw, so we are both browser and nodejs +// Make sure to access `process` through `_global` so that WebPack does not accidently browserify +// this code. +var isMix = typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]' && !isWebWorker && + !!(typeof window !== 'undefined' && window['HTMLElement']); -var zoneAwareAddEventListener = makeZoneAwareAddListener(ADD_EVENT_LISTENER, REMOVE_EVENT_LISTENER); -var zoneAwareRemoveEventListener = makeZoneAwareRemoveListener(REMOVE_EVENT_LISTENER); -function patchEventTargetMethods(obj, addFnName, removeFnName, metaCreator) { - if (addFnName === void 0) { addFnName = ADD_EVENT_LISTENER; } - if (removeFnName === void 0) { removeFnName = REMOVE_EVENT_LISTENER; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - if (obj && obj[addFnName]) { - patchMethod(obj, addFnName, function () { return makeZoneAwareAddListener(addFnName, removeFnName, true, false, false, metaCreator); }); - patchMethod(obj, removeFnName, function () { return makeZoneAwareRemoveListener(removeFnName, true, metaCreator); }); - return true; - } - else { - return false; - } -} var originalInstanceKey = zoneSymbol('originalInstance'); // wrap some native API on `window` - -function createNamedFn(name, delegate) { - try { - return (Function('f', "return function " + name + "(){return f(this, arguments)}"))(delegate); - } - catch (error) { - // if we fail, we must be CSP, just return delegate. - return function () { - return delegate(this, arguments); - }; - } -} function patchMethod(target, name, patchFn) { var proto = target; - while (proto && Object.getOwnPropertyNames(proto).indexOf(name) === -1) { + while (proto && !proto.hasOwnProperty(name)) { proto = Object.getPrototypeOf(proto); } if (!proto && target[name]) { @@ -1605,16 +705,25 @@ function patchMethod(target, name, patchFn) { var delegate; if (proto && !(delegate = proto[delegateName])) { delegate = proto[delegateName] = proto[name]; - proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); + // check whether proto[name] is writable + // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob + var desc = proto && Object.getOwnPropertyDescriptor(proto, name); + if (isPropertyWritable(desc)) { + var patchDelegate_1 = patchFn(delegate, delegateName, name); + proto[name] = function () { + return patchDelegate_1(this, arguments); + }; + attachOriginToPatched(proto[name], delegate); + } } return delegate; } // TODO: @JiaLiPassion, support cancel task later if necessary - -Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; -Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; +function attachOriginToPatched(patched, original) { + patched[zoneSymbol('OriginalDelegate')] = original; +} /** * @license @@ -1623,12 +732,23 @@ Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +/** + * @fileoverview + * @suppress {missingRequire} + */ +var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; setName += nameSuffix; cancelName += nameSuffix; var tasksByHandleId = {}; + var NUMBER = 'number'; + var STRING = 'string'; + var FUNCTION = 'function'; + var INTERVAL = 'Interval'; + var TIMEOUT = 'Timeout'; + var NOT_SCHEDULED = 'notScheduled'; function scheduleTask(task) { var data = task.data; function timer() { @@ -1636,27 +756,33 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - delete tasksByHandleId[data.handleId]; + if (typeof data.handleId === NUMBER) { + // in non-nodejs env, we remove timerId + // from local cache + delete tasksByHandleId[data.handleId]; + } + else if (data.handleId) { + // Node returns complex objects as handleIds + // we remove task reference from timer object + data.handleId[taskSymbol] = null; + } } } - data.args[0] = timer; data.handleId = setNative.apply(window, data.args); - tasksByHandleId[data.handleId] = task; return task; } function clearTask(task) { - delete tasksByHandleId[task.data.handleId]; return clearNative(task.data.handleId); } setNative = patchMethod(window, setName, function (delegate) { return function (self, args) { - if (typeof args[0] === 'function') { + if (typeof args[0] === FUNCTION) { var zone = Zone.current; var options = { handleId: null, - isPeriodic: nameSuffix === 'Interval', - delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, + isPeriodic: nameSuffix === INTERVAL, + delay: (nameSuffix === TIMEOUT || nameSuffix === INTERVAL) ? args[1] || 0 : null, args: args }; var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); @@ -1665,13 +791,26 @@ function patchTimer(window, setName, cancelName, nameSuffix) { } // Node.js must additionally support the ref and unref functions. var handle = task.data.handleId; + if (typeof handle === NUMBER) { + // for non nodejs env, we save handleId: task + // mapping in local cache for clearTimeout + tasksByHandleId[handle] = task; + } + else if (handle) { + // for nodejs env, we save task + // reference in timerId Object for clearTimeout + handle[taskSymbol] = task; + } // check whether handle is null, because some polyfill or browser // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame - if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && - typeof handle.unref === 'function') { + if (handle && handle.ref && handle.unref && typeof handle.ref === FUNCTION && + typeof handle.unref === FUNCTION) { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } + if (typeof handle === NUMBER || handle) { + return handle; + } return task; } else { @@ -1681,10 +820,29 @@ function patchTimer(window, setName, cancelName, nameSuffix) { }; }); clearNative = patchMethod(window, cancelName, function (delegate) { return function (self, args) { - var task = typeof args[0] === 'number' ? tasksByHandleId[args[0]] : args[0]; - if (task && typeof task.type === 'string') { - if (task.state !== 'notScheduled' && + var id = args[0]; + var task; + if (typeof id === NUMBER) { + // non nodejs env. + task = tasksByHandleId[id]; + } + else { + // nodejs env. + task = id && id[taskSymbol]; + // other environments. + if (!task) { + task = id; + } + } + if (task && typeof task.type === STRING) { + if (task.state !== NOT_SCHEDULED && (task.cancelFn && task.data.isPeriodic || task.runCount === 0)) { + if (typeof id === NUMBER) { + delete tasksByHandleId[id]; + } + else if (id) { + id[taskSymbol] = null; + } // Do not cancel already canceled functions task.zone.cancelTask(task); } diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js new file mode 100644 index 000000000..4451902b3 --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -0,0 +1,1458 @@ +/** +* @license +* Copyright Google Inc. All Rights Reserved. +* +* Use of this source code is governed by an MIT-style license that can be +* found in the LICENSE file at https://angular.io/license +*/ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + api.onUnhandledError = function (e) { + if (api.showUncaughtError()) { + var rejection = e && e.rejection; + if (rejection) { + console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); + } + else { + console.error(e); + } + } + }; + api.microtaskDrainDone = function () { + while (_uncaughtPromiseErrors.length) { + var _loop_1 = function () { + var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); + try { + uncaughtPromiseError.zone.runGuarded(function () { + throw uncaughtPromiseError; + }); + } + catch (error) { + handleUnhandledRejection(error); + } + }; + while (_uncaughtPromiseErrors.length) { + _loop_1(); + } + } + }; + var UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); + function handleUnhandledRejection(e) { + api.onUnhandledError(e); + try { + var handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; + if (handler && typeof handler === 'function') { + handler.apply(this, [e]); + } + } + catch (err) { + } + } + function isThenable(value) { + return value && value.then; + } + function forwardResolution(value) { + return value; + } + function forwardRejection(rejection) { + return ZoneAwarePromise.reject(rejection); + } + var symbolState = __symbol__('state'); + var symbolValue = __symbol__('value'); + var source = 'Promise.then'; + var UNRESOLVED = null; + var RESOLVED = true; + var REJECTED = false; + var REJECTED_NO_CATCH = 0; + function makeResolver(promise, state) { + return function (v) { + try { + resolvePromise(promise, state, v); + } + catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + }; + } + var once = function () { + var wasCalled = false; + return function wrapper(wrappedFunction) { + return function () { + if (wasCalled) { + return; + } + wasCalled = true; + wrappedFunction.apply(null, arguments); + }; + }; + }; + var TYPE_ERROR = 'Promise resolved with itself'; + var OBJECT = 'object'; + var FUNCTION = 'function'; + var CURRENT_TASK_SYMBOL = __symbol__('currentTask'); + // Promise Resolution + function resolvePromise(promise, state, value) { + var onceWrapper = once(); + if (promise === value) { + throw new TypeError(TYPE_ERROR); + } + if (promise[symbolState] === UNRESOLVED) { + // should only get value.then once based on promise spec. + var then = null; + try { + if (typeof value === OBJECT || typeof value === FUNCTION) { + then = value && value.then; + } + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + return promise; + } + // if (value instanceof ZoneAwarePromise) { + if (state !== REJECTED && value instanceof ZoneAwarePromise && + value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && + value[symbolState] !== UNRESOLVED) { + clearRejectedNoCatch(value); + resolvePromise(promise, value[symbolState], value[symbolValue]); + } + else if (state !== REJECTED && typeof then === FUNCTION) { + try { + then.apply(value, [ + onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) + ]); + } + catch (err) { + onceWrapper(function () { + resolvePromise(promise, false, err); + })(); + } + } + else { + promise[symbolState] = state; + var queue = promise[symbolValue]; + promise[symbolValue] = value; + // record task information in value when error occurs, so we can + // do some additional work such as render longStackTrace + if (state === REJECTED && value instanceof Error) { + value[CURRENT_TASK_SYMBOL] = Zone.currentTask; + } + for (var i = 0; i < queue.length;) { + scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); + } + if (queue.length == 0 && state == REJECTED) { + promise[symbolState] = REJECTED_NO_CATCH; + try { + throw new Error('Uncaught (in promise): ' + value + + (value && value.stack ? '\n' + value.stack : '')); + } + catch (err) { + var error_1 = err; + error_1.rejection = value; + error_1.promise = promise; + error_1.zone = Zone.current; + error_1.task = Zone.currentTask; + _uncaughtPromiseErrors.push(error_1); + api.scheduleMicroTask(); // to make sure that it is running + } + } + } + } + // Resolving an already resolved promise is a noop. + return promise; + } + var REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); + function clearRejectedNoCatch(promise) { + if (promise[symbolState] === REJECTED_NO_CATCH) { + // if the promise is rejected no catch status + // and queue.length > 0, means there is a error handler + // here to handle the rejected promise, we should trigger + // windows.rejectionhandled eventHandler or nodejs rejectionHandled + // eventHandler + try { + var handler = Zone[REJECTION_HANDLED_HANDLER]; + if (handler && typeof handler === FUNCTION) { + handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + } + } + catch (err) { + } + promise[symbolState] = REJECTED; + for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { + if (promise === _uncaughtPromiseErrors[i].promise) { + _uncaughtPromiseErrors.splice(i, 1); + } + } + } + } + function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { + clearRejectedNoCatch(promise); + var delegate = promise[symbolState] ? + (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : + (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; + zone.scheduleMicroTask(source, function () { + try { + resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + } + catch (error) { + resolvePromise(chainPromise, false, error); + } + }); + } + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = (function () { + function ZoneAwarePromise(executor) { + var promise = this; + if (!(promise instanceof ZoneAwarePromise)) { + throw new Error('Must be an instanceof Promise.'); + } + promise[symbolState] = UNRESOLVED; + promise[symbolValue] = []; // queue; + try { + executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); + } + catch (error) { + resolvePromise(promise, false, error); + } + } + ZoneAwarePromise.toString = function () { + return ZONE_AWARE_PROMISE_TO_STRING; + }; + ZoneAwarePromise.resolve = function (value) { + return resolvePromise(new this(null), RESOLVED, value); + }; + ZoneAwarePromise.reject = function (error) { + return resolvePromise(new this(null), REJECTED, error); + }; + ZoneAwarePromise.race = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + _a = [res, rej], resolve = _a[0], reject = _a[1]; + var _a; + }); + function onResolve(value) { + promise && (promise = null || resolve(value)); + } + function onReject(error) { + promise && (promise = null || reject(error)); + } + for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { + var value = values_1[_i]; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + return promise; + }; + ZoneAwarePromise.all = function (values) { + var resolve; + var reject; + var promise = new this(function (res, rej) { + resolve = res; + reject = rej; + }); + var count = 0; + var resolvedValues = []; + for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { + var value = values_2[_i]; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then((function (index) { return function (value) { + resolvedValues[index] = value; + count--; + if (!count) { + resolve(resolvedValues); + } + }; })(count), reject); + count++; + } + if (!count) + resolve(resolvedValues); + return promise; + }; + ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { + var chainPromise = new this.constructor(null); + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); + } + return chainPromise; + }; + ZoneAwarePromise.prototype.catch = function (onRejected) { + return this.then(null, onRejected); + }; + return ZoneAwarePromise; + }()); + // Protect against aggressive optimizers dropping seemingly unused properties. + // E.g. Closure Compiler in advanced mode. + ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; + ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; + ZoneAwarePromise['race'] = ZoneAwarePromise.race; + ZoneAwarePromise['all'] = ZoneAwarePromise.all; + var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + var desc = Object.getOwnPropertyDescriptor(global, 'Promise'); + if (!desc || desc.configurable) { + desc && delete desc.writable; + desc && delete desc.value; + if (!desc) { + desc = { configurable: true, enumerable: true }; + } + desc.get = function () { + // if we already set ZoneAwarePromise, use patched one + // otherwise return native one. + return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; + }; + desc.set = function (NewNativePromise) { + if (NewNativePromise === ZoneAwarePromise) { + // if the NewNativePromise is ZoneAwarePromise + // save to global + global[ZONE_AWARE_PROMISE] = NewNativePromise; + } + else { + // if the NewNativePromise is not ZoneAwarePromise + // for example: after load zone.js, some library just + // set es6-promise to global, if we set it to global + // directly, assertZonePatched will fail and angular + // will not loaded, so we just set the NewNativePromise + // to global[symbolPromise], so the result is just like + // we load ES6 Promise before zone.js + global[symbolPromise] = NewNativePromise; + if (!NewNativePromise.prototype[symbolThen]) { + patchThen(NewNativePromise); + } + api.setNativePromise(NewNativePromise); + } + }; + Object.defineProperty(global, 'Promise', desc); + } + global['Promise'] = ZoneAwarePromise; + var symbolThenPatched = __symbol__('thenPatched'); + function patchThen(Ctor) { + var proto = Ctor.prototype; + var originalThen = proto.then; + // Keep a reference to the original method. + proto[symbolThen] = originalThen; + // check Ctor.prototype.then propertyDescritor is writable or not + // in meteor env, writable is false, we have to make it to be true. + var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); + if (prop && prop.writable === false && prop.configurable) { + Object.defineProperty(Ctor.prototype, 'then', { writable: true }); + } + Ctor.prototype.then = function (onResolve, onReject) { + var _this = this; + var wrapped = new ZoneAwarePromise(function (resolve, reject) { + originalThen.call(_this, resolve, reject); + }); + return wrapped.then(onResolve, onReject); + }; + Ctor[symbolThenPatched] = true; + } + function zoneify(fn) { + return function () { + var resultPromise = fn.apply(this, arguments); + if (resultPromise instanceof ZoneAwarePromise) { + return resultPromise; + } + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); + } + return resultPromise; + }; + } + if (NativePromise) { + patchThen(NativePromise); + var fetch_1 = global['fetch']; + if (typeof fetch_1 == FUNCTION) { + global['fetch'] = zoneify(fetch_1); + } + } + // This is not part of public API, but it is useful for tests, so we expose it. + Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; + return ZoneAwarePromise; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {globalThis} + */ +var NEWLINE = '\n'; +var IGNORE_FRAMES = {}; +var creationTrace = '__creationTrace__'; +var ERROR_TAG = 'STACKTRACE TRACKING'; +var SEP_TAG = '__SEP_TAG__'; +var sepTemplate = SEP_TAG + '@[native]'; +var LongStackTrace = (function () { + function LongStackTrace() { + this.error = getStacktrace(); + this.timestamp = new Date(); + } + return LongStackTrace; +}()); +function getStacktraceWithUncaughtError() { + return new Error(ERROR_TAG); +} +function getStacktraceWithCaughtError() { + try { + throw getStacktraceWithUncaughtError(); + } + catch (err) { + return err; + } +} +// Some implementations of exception handling don't create a stack trace if the exception +// isn't thrown, however it's faster not to actually throw the exception. +var error = getStacktraceWithUncaughtError(); +var caughtError = getStacktraceWithCaughtError(); +var getStacktrace = error.stack ? + getStacktraceWithUncaughtError : + (caughtError.stack ? getStacktraceWithCaughtError : getStacktraceWithUncaughtError); +function getFrames(error) { + return error.stack ? error.stack.split(NEWLINE) : []; +} +function addErrorStack(lines, error) { + var trace = getFrames(error); + for (var i = 0; i < trace.length; i++) { + var frame = trace[i]; + // Filter out the Frames which are part of stack capturing. + if (!IGNORE_FRAMES.hasOwnProperty(frame)) { + lines.push(trace[i]); + } + } +} +function renderLongStackTrace(frames, stack) { + var longTrace = [stack ? stack.trim() : '']; + if (frames) { + var timestamp = new Date().getTime(); + for (var i = 0; i < frames.length; i++) { + var traceFrames = frames[i]; + var lastTime = traceFrames.timestamp; + var separator = "____________________Elapsed " + (timestamp - lastTime.getTime()) + " ms; At: " + lastTime; + separator = separator.replace(/[^\w\d]/g, '_'); + longTrace.push(sepTemplate.replace(SEP_TAG, separator)); + addErrorStack(longTrace, traceFrames.error); + timestamp = lastTime.getTime(); + } + } + return longTrace.join(NEWLINE); +} +Zone['longStackTraceZoneSpec'] = { + name: 'long-stack-trace', + longStackTraceLimit: 10, + // add a getLongStackTrace method in spec to + // handle handled reject promise error. + getLongStackTrace: function (error) { + if (!error) { + return undefined; + } + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; + if (!trace) { + return error.stack; + } + return renderLongStackTrace(trace, error.stack); + }, + onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var currentTask = Zone.currentTask; + var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) + task.data = {}; + task.data[creationTrace] = trace; + } + return parentZoneDelegate.scheduleTask(targetZone, task); + }, + onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + var parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = error.longStack = longStack; + } + catch (err) { + } + } + } + return parentZoneDelegate.handleError(targetZone, error); + } +}; +function captureStackTraces(stackTraces, count) { + if (count > 0) { + stackTraces.push(getFrames((new LongStackTrace()).error)); + captureStackTraces(stackTraces, count - 1); + } +} +function computeIgnoreFrames() { + if (Error.stackTraceLimit <= 0) { + return; + } + var frames = []; + captureStackTraces(frames, 2); + var frames1 = frames[0]; + var frames2 = frames[1]; + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + if (frame1.indexOf(ERROR_TAG) == -1) { + var match = frame1.match(/^\s*at\s+/); + if (match) { + sepTemplate = match[0] + SEP_TAG + ' (http://localhost)'; + break; + } + } + } + for (var i = 0; i < frames1.length; i++) { + var frame1 = frames1[i]; + var frame2 = frames2[i]; + if (frame1 === frame2) { + IGNORE_FRAMES[frame1] = true; + } + else { + break; + } + } +} +computeIgnoreFrames(); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var ProxyZoneSpec = (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!this.isLoaded()) { + throw new Error("Expected to be running in 'ProxyZone', but it was not found."); + } + return ProxyZoneSpec.get(); + }; + ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { + var _this = this; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + else { + return parentZoneDelegate.fork(targetZone, zoneSpec); + } + }; + ProxyZoneSpec.prototype.onIntercept = function (parentZoneDelegate, currentZone, targetZone, delegate, source) { + if (this._delegateSpec && this._delegateSpec.onIntercept) { + return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source); + } + else { + return parentZoneDelegate.intercept(targetZone, delegate, source); + } + }; + ProxyZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + if (this._delegateSpec && this._delegateSpec.onInvoke) { + return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); + } + else { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + }; + ProxyZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + if (this._delegateSpec && this._delegateSpec.onHandleError) { + return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); + } + else { + return parentZoneDelegate.handleError(targetZone, error); + } + }; + ProxyZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onScheduleTask) { + return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.scheduleTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + if (this._delegateSpec && this._delegateSpec.onHasTask) { + this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); + } + else { + delegate.hasTask(target, hasTaskState); + } + }; + return ProxyZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['ProxyZoneSpec'] = ProxyZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var SyncTestZoneSpec = (function () { + function SyncTestZoneSpec(namePrefix) { + this.runZone = Zone.current; + this.name = 'syncTestZone for ' + namePrefix; + } + SyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + case 'macroTask': + throw new Error("Cannot call " + task.source + " from within a sync test."); + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + return SyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +var AsyncTestZoneSpec = (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this.runZone = Zone.current; + this._finishCallback = finishCallback; + this._failCallback = failCallback; + this.name = 'asyncTestZone for ' + namePrefix; + } + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks)) { + // We do this because we would like to catch unhandled rejected promises. + this.runZone.run(function () { + setTimeout(function () { + if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { + _this._finishCallback(); + } + }, 0); + }); + } + }; + // Note - we need to use onInvoke at the moment to call finish when a test is + // fully synchronous. TODO(juliemr): remove this when the logic for + // onHasTask changes and it calls whenever the task queues are dirty. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + this._finishCallbackIfDone(); + } + }; + AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + // Let the parent try to handle the error. + var result = parentZoneDelegate.handleError(targetZone, error); + if (result) { + this._failCallback(error); + this._alreadyErrored = true; + } + return false; + }; + AsyncTestZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + delegate.hasTask(target, hasTaskState); + if (hasTaskState.change == 'microTask') { + this._pendingMicroTasks = hasTaskState.microTask; + this._finishCallbackIfDone(); + } + else if (hasTaskState.change == 'macroTask') { + this._pendingMacroTasks = hasTaskState.macroTask; + this._finishCallbackIfDone(); + } + }; + return AsyncTestZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function (global) { + var Scheduler = (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 0; + // Scheduler queue with the tuple of end time and callback function - sorted by end time. + this._schedulerQueue = []; + // Current simulated time in millis. + this._currentTime = 0; + } + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } + if (id === void 0) { id = -1; } + var currentId = id < 0 ? this.nextId++ : id; + var endTime = this._currentTime + delay; + // Insert so that scheduler queue remains sorted by end time. + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + isPeriodic: isPeriodic, + isRequestAnimationFrame: isRequestAnimationFrame + }; + var i = 0; + for (; i < this._schedulerQueue.length; i++) { + var currentEntry = this._schedulerQueue[i]; + if (newEntry.endTime < currentEntry.endTime) { + break; + } + } + this._schedulerQueue.splice(i, 0, newEntry); + return currentId; + }; + Scheduler.prototype.removeScheduledFunctionWithId = function (id) { + for (var i = 0; i < this._schedulerQueue.length; i++) { + if (this._schedulerQueue[i].id == id) { + this._schedulerQueue.splice(i, 1); + break; + } + } + }; + Scheduler.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + var finalTime = this._currentTime + millis; + var lastCurrentTime = 0; + if (this._schedulerQueue.length === 0 && doTick) { + doTick(millis); + return; + } + while (this._schedulerQueue.length > 0) { + var current = this._schedulerQueue[0]; + if (finalTime < current.endTime) { + // Done processing the queue since it's sorted by endTime. + break; + } + else { + // Time to run scheduled function. Remove it from the head of queue. + var current_1 = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current_1.endTime; + if (doTick) { + doTick(this._currentTime - lastCurrentTime); + } + var retval = current_1.func.apply(global, current_1.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + } + this._currentTime = finalTime; + }; + Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) { + if (limit === void 0) { limit = 20; } + if (flushPeriodic === void 0) { flushPeriodic = false; } + if (flushPeriodic) { + return this.flushPeriodic(doTick); + } + else { + return this.flushNonPeriodic(limit, doTick); + } + }; + Scheduler.prototype.flushPeriodic = function (doTick) { + if (this._schedulerQueue.length === 0) { + return 0; + } + // Find the last task currently queued in the scheduler queue and tick + // till that time. + var startTime = this._currentTime; + var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; + this.tick(lastTask.endTime - startTime, doTick); + return this._currentTime - startTime; + }; + Scheduler.prototype.flushNonPeriodic = function (limit, doTick) { + var startTime = this._currentTime; + var lastCurrentTime = 0; + var count = 0; + while (this._schedulerQueue.length > 0) { + count++; + if (count > limit) { + throw new Error('flush failed after reaching the limit of ' + limit + + ' tasks. Does your code use a polling timeout?'); + } + // flush only non-periodic timers. + // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. + if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; }) + .length === 0) { + break; + } + var current = this._schedulerQueue.shift(); + lastCurrentTime = this._currentTime; + this._currentTime = current.endTime; + if (doTick) { + // Update any secondary schedulers like Jasmine mock Date. + doTick(this._currentTime - lastCurrentTime); + } + var retval = current.func.apply(global, current.args); + if (!retval) { + // Uncaught exception in the current scheduled function. Stop processing the queue. + break; + } + } + return this._currentTime - startTime; + }; + return Scheduler; + }()); + var FakeAsyncTestZoneSpec = (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + throw new Error('The code should be running in the fakeAsync zone to call this function'); + } + }; + FakeAsyncTestZoneSpec.prototype._fnAndFlush = function (fn, completers) { + var _this = this; + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + fn.apply(global, args); + if (_this._lastError === null) { + if (completers.onSuccess != null) { + completers.onSuccess.apply(global); + } + // Flush microtasks only on success. + _this.flushMicrotasks(); + } + else { + if (completers.onError != null) { + completers.onError.apply(global); + } + } + // Return true if there were no errors, false otherwise. + return _this._lastError === null; + }; + }; + FakeAsyncTestZoneSpec._removeTimer = function (timers, id) { + var index = timers.indexOf(id); + if (index > -1) { + timers.splice(index, 1); + } + }; + FakeAsyncTestZoneSpec.prototype._dequeueTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._requeuePeriodicTimer = function (fn, interval, args, id) { + var _this = this; + return function () { + // Requeue the timer callback if it's not been canceled. + if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { + _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); + } + }; + }; + FakeAsyncTestZoneSpec.prototype._dequeuePeriodicTimer = function (id) { + var _this = this; + return function () { + FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); + }; + }; + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { + if (isTimer === void 0) { isTimer = true; } + var removeTimerFn = this._dequeueTimer(this._scheduler.nextId); + // Queue the callback and dequeue the timer on success and error. + var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn }); + var id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer); + if (isTimer) { + this.pendingTimers.push(id); + } + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._setInterval = function (fn, interval) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + var id = this._scheduler.nextId; + var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; + var cb = this._fnAndFlush(fn, completers); + // Use the callback created above to requeue on success. + completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); + // Queue the callback and dequeue the periodic timer only on error. + this._scheduler.scheduleFunction(cb, interval, args, true); + this.pendingPeriodicTimers.push(id); + return id; + }; + FakeAsyncTestZoneSpec.prototype._clearInterval = function (id) { + FakeAsyncTestZoneSpec._removeTimer(this.pendingPeriodicTimers, id); + this._scheduler.removeScheduledFunctionWithId(id); + }; + FakeAsyncTestZoneSpec.prototype._resetLastErrorAndThrow = function () { + var error = this._lastError || this._uncaughtPromiseErrors[0]; + this._uncaughtPromiseErrors.length = 0; + this._lastError = null; + throw error; + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + }; + FakeAsyncTestZoneSpec.prototype.flushMicrotasks = function () { + var _this = this; + FakeAsyncTestZoneSpec.assertInZone(); + var flushErrors = function () { + if (_this._lastError !== null || _this._uncaughtPromiseErrors.length) { + // If there is an error stop processing the microtask queue and rethrow the error. + _this._resetLastErrorAndThrow(); + } + }; + while (this._microtasks.length > 0) { + var microtask = this._microtasks.shift(); + microtask.func.apply(microtask.target, microtask.args); + } + flushErrors(); + }; + FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) { + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick); + if (this._lastError !== null) { + this._resetLastErrorAndThrow(); + } + return elapsed; + }; + FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + switch (task.type) { + case 'microTask': + var args = task.data && task.data.args; + // should pass additional arguments to callback if have any + // currently we know process.nextTick will have such additional + // arguments + var addtionalArgs = void 0; + if (args) { + var callbackIndex = task.data.callbackIndex; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: addtionalArgs, + target: task.data && task.data.target + }); + break; + case 'macroTask': + switch (task.source) { + case 'setTimeout': + task.data['handleId'] = + this._setTimeout(task.invoke, task.data['delay'], task.data['args']); + break; + case 'setInterval': + task.data['handleId'] = + this._setInterval(task.invoke, task.data['delay'], task.data['args']); + break; + case 'XMLHttpRequest.send': + throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' + + task.data['url']); + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + // Simulate a requestAnimationFrame by using a setTimeout with 16 ms. + // (60 frames per second) + task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame); + break; + default: + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + task = delegate.scheduleTask(target, task); + break; + } + return task; + }; + FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + switch (task.source) { + case 'setTimeout': + case 'requestAnimationFrame': + case 'webkitRequestAnimationFrame': + case 'mozRequestAnimationFrame': + return this._clearTimeout(task.data['handleId']); + case 'setInterval': + return this._clearInterval(task.data['handleId']); + default: + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + return FakeAsyncTestZoneSpec; + }()); + // Export the class so that new instances can be created with proper + // constructor params. + Zone['FakeAsyncTestZoneSpec'] = FakeAsyncTestZoneSpec; +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * A `TaskTrackingZoneSpec` allows one to track all outstanding Tasks. + * + * This is useful in tests. For example to see which tasks are preventing a test from completing + * or an automated way of releasing all of the event listeners at the end of the test. + */ +var TaskTrackingZoneSpec = (function () { + function TaskTrackingZoneSpec() { + this.name = 'TaskTrackingZone'; + this.microTasks = []; + this.macroTasks = []; + this.eventTasks = []; + this.properties = { 'TaskTrackingZone': this }; + } + TaskTrackingZoneSpec.get = function () { + return Zone.current.get('TaskTrackingZone'); + }; + TaskTrackingZoneSpec.prototype.getTasksFor = function (type) { + switch (type) { + case 'microTask': + return this.microTasks; + case 'macroTask': + return this.macroTasks; + case 'eventTask': + return this.eventTasks; + } + throw new Error('Unknown task format: ' + type); + }; + TaskTrackingZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + task['creationLocation'] = new Error("Task '" + task.type + "' from '" + task.source + "'."); + var tasks = this.getTasksFor(task.type); + tasks.push(task); + return parentZoneDelegate.scheduleTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.cancelTask(targetZone, task); + }; + TaskTrackingZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + if (task.type === 'eventTask') + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var tasks = this.getTasksFor(task.type); + for (var i = 0; i < tasks.length; i++) { + if (tasks[i] == task) { + tasks.splice(i, 1); + break; + } + } + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + }; + TaskTrackingZoneSpec.prototype.clearEvents = function () { + while (this.eventTasks.length) { + Zone.current.cancelTask(this.eventTasks[0]); + } + }; + return TaskTrackingZoneSpec; +}()); +// Export the class so that new instances can be created with proper +// constructor params. +Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {missingRequire} + */ +(function (global) { + // Detect and setup WTF. + var wtfTrace = null; + var wtfEvents = null; + var wtfEnabled = (function () { + var wtf = global['wtf']; + if (wtf) { + wtfTrace = wtf.trace; + if (wtfTrace) { + wtfEvents = wtfTrace.events; + return true; + } + } + return false; + })(); + var WtfZoneSpec = (function () { + function WtfZoneSpec() { + this.name = 'WTF'; + } + WtfZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + var retValue = parentZoneDelegate.fork(targetZone, zoneSpec); + WtfZoneSpec.forkInstance(zonePathName(targetZone), retValue.name); + return retValue; + }; + WtfZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + var scope = WtfZoneSpec.invokeScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeScope[source] = + wtfEvents.createScope("Zone:invoke:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)); + }; + WtfZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + return parentZoneDelegate.handleError(targetZone, error); + }; + WtfZoneSpec.prototype.onScheduleTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.type + ':' + task.source; + var instance = WtfZoneSpec.scheduleInstance[key]; + if (!instance) { + instance = WtfZoneSpec.scheduleInstance[key] = + wtfEvents.createInstance("Zone:schedule:" + key + "(ascii zone, any data)"); + } + var retValue = parentZoneDelegate.scheduleTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + WtfZoneSpec.prototype.onInvokeTask = function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { + var source = task.source; + var scope = WtfZoneSpec.invokeTaskScope[source]; + if (!scope) { + scope = WtfZoneSpec.invokeTaskScope[source] = + wtfEvents.createScope("Zone:invokeTask:" + source + "(ascii zone)"); + } + return wtfTrace.leaveScope(scope(zonePathName(targetZone)), parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs)); + }; + WtfZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + var key = task.source; + var instance = WtfZoneSpec.cancelInstance[key]; + if (!instance) { + instance = WtfZoneSpec.cancelInstance[key] = + wtfEvents.createInstance("Zone:cancel:" + key + "(ascii zone, any options)"); + } + var retValue = parentZoneDelegate.cancelTask(targetZone, task); + instance(zonePathName(targetZone), shallowObj(task.data, 2)); + return retValue; + }; + + return WtfZoneSpec; + }()); + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; + function shallowObj(obj, depth) { + if (!depth) + return null; + var out = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = obj[key]; + switch (typeof value) { + case 'object': + var name_1 = value && value.constructor && value.constructor.name; + value = name_1 == Object.name ? shallowObj(value, depth - 1) : name_1; + break; + case 'function': + value = value.name || undefined; + break; + } + out[key] = value; + } + } + return out; + } + function zonePathName(zone) { + var name = zone.name; + zone = zone.parent; + while (zone != null) { + name = zone.name + '::' + name; + zone = zone.parent; + } + return name; + } + Zone['wtfZoneSpec'] = !wtfEnabled ? null : new WtfZoneSpec(); +})(typeof window === 'object' && window || typeof self === 'object' && self || global); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +'use strict'; +(function (context) { + var Mocha = context.Mocha; + if (typeof Mocha === 'undefined') { + throw new Error('Missing Mocha.js'); + } + if (typeof Zone === 'undefined') { + throw new Error('Missing Zone.js'); + } + var ProxyZoneSpec = Zone['ProxyZoneSpec']; + var SyncTestZoneSpec = Zone['SyncTestZoneSpec']; + if (!ProxyZoneSpec) { + throw new Error('Missing ProxyZoneSpec'); + } + if (Mocha['__zone_patch__']) { + throw new Error('"Mocha" has already been patched with "Zone".'); + } + Mocha['__zone_patch__'] = true; + var rootZone = Zone.current; + var syncZone = rootZone.fork(new SyncTestZoneSpec('Mocha.describe')); + var testZone = null; + var suiteZone = rootZone.fork(new ProxyZoneSpec()); + var mochaOriginal = { + after: Mocha.after, + afterEach: Mocha.afterEach, + before: Mocha.before, + beforeEach: Mocha.beforeEach, + describe: Mocha.describe, + it: Mocha.it + }; + function modifyArguments(args, syncTest, asyncTest) { + var _loop_1 = function (i) { + var arg = args[i]; + if (typeof arg === 'function') { + // The `done` callback is only passed through if the function expects at + // least one argument. + // Note we have to make a function with correct number of arguments, + // otherwise mocha will + // think that all functions are sync or async. + args[i] = (arg.length === 0) ? syncTest(arg) : asyncTest(arg); + // Mocha uses toString to view the test body in the result list, make sure we return the + // correct function body + args[i].toString = function () { + return arg.toString(); + }; + } + }; + for (var i = 0; i < args.length; i++) { + _loop_1(i); + } + return args; + } + function wrapDescribeInZone(args) { + var syncTest = function (fn) { + return function () { + return syncZone.run(fn, this, arguments); + }; + }; + return modifyArguments(args, syncTest); + } + function wrapTestInZone(args) { + var asyncTest = function (fn) { + return function (done) { + return testZone.run(fn, this, [done]); + }; + }; + var syncTest = function (fn) { + return function () { + return testZone.run(fn, this); + }; + }; + return modifyArguments(args, syncTest, asyncTest); + } + function wrapSuiteInZone(args) { + var asyncTest = function (fn) { + return function (done) { + return suiteZone.run(fn, this, [done]); + }; + }; + var syncTest = function (fn) { + return function () { + return suiteZone.run(fn, this); + }; + }; + return modifyArguments(args, syncTest, asyncTest); + } + context.describe = context.suite = Mocha.describe = function () { + return mochaOriginal.describe.apply(this, wrapDescribeInZone(arguments)); + }; + context.xdescribe = context.suite.skip = Mocha.describe.skip = function () { + return mochaOriginal.describe.skip.apply(this, wrapDescribeInZone(arguments)); + }; + context.describe.only = context.suite.only = Mocha.describe.only = function () { + return mochaOriginal.describe.only.apply(this, wrapDescribeInZone(arguments)); + }; + context.it = context.specify = context.test = Mocha.it = function () { + return mochaOriginal.it.apply(this, wrapTestInZone(arguments)); + }; + context.xit = context.xspecify = Mocha.it.skip = function () { + return mochaOriginal.it.skip.apply(this, wrapTestInZone(arguments)); + }; + context.it.only = context.test.only = Mocha.it.only = function () { + return mochaOriginal.it.only.apply(this, wrapTestInZone(arguments)); + }; + context.after = context.suiteTeardown = Mocha.after = function () { + return mochaOriginal.after.apply(this, wrapSuiteInZone(arguments)); + }; + context.afterEach = context.teardown = Mocha.afterEach = function () { + return mochaOriginal.afterEach.apply(this, wrapTestInZone(arguments)); + }; + context.before = context.suiteSetup = Mocha.before = function () { + return mochaOriginal.before.apply(this, wrapSuiteInZone(arguments)); + }; + context.beforeEach = context.setup = Mocha.beforeEach = function () { + return mochaOriginal.beforeEach.apply(this, wrapTestInZone(arguments)); + }; + (function (originalRunTest, originalRun) { + Mocha.Runner.prototype.runTest = function (fn) { + var _this = this; + Zone.current.scheduleMicroTask('mocha.forceTask', function () { + originalRunTest.call(_this, fn); + }); + }; + Mocha.Runner.prototype.run = function (fn) { + this.on('test', function (e) { + // if (Zone.current !== rootZone) { + // throw new Error('Unexpected zone: ' + Zone.current.name); + // } + testZone = rootZone.fork(new ProxyZoneSpec()); + }); + return originalRun.call(this, fn); + }; + })(Mocha.Runner.prototype.runTest, Mocha.Runner.prototype.run); +})(typeof window !== 'undefined' ? window : global); + +}))); diff --git a/nativescript-angular/zone-js/testing.jasmine.ts b/nativescript-angular/zone-js/testing.jasmine.ts new file mode 100644 index 000000000..c363546af --- /dev/null +++ b/nativescript-angular/zone-js/testing.jasmine.ts @@ -0,0 +1,3 @@ +// Bootstrap helper module for jasmine spec tests +import "../platform"; +import "./dist/zone-nativescript.jasmine.js"; diff --git a/nativescript-angular/zone-js/testing.mocha.ts b/nativescript-angular/zone-js/testing.mocha.ts new file mode 100644 index 000000000..0f75b1d11 --- /dev/null +++ b/nativescript-angular/zone-js/testing.mocha.ts @@ -0,0 +1,2 @@ +import "../platform"; +import "./dist/zone-nativescript.mocha.js"; diff --git a/tests/app/snippets/navigation/page-outlet.ts b/tests/app/snippets/navigation/page-outlet.ts index 75354b5f6..27ce9ee48 100644 --- a/tests/app/snippets/navigation/page-outlet.ts +++ b/tests/app/snippets/navigation/page-outlet.ts @@ -1,4 +1,4 @@ -import { TestApp, registerTestApp } from "../../tests/test-app"; +import { registerTestApp } from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; // >> page-outlet-example diff --git a/tests/app/snippets/navigation/router-outlet.ts b/tests/app/snippets/navigation/router-outlet.ts index bfd50fafc..bca549d74 100644 --- a/tests/app/snippets/navigation/router-outlet.ts +++ b/tests/app/snippets/navigation/router-outlet.ts @@ -1,4 +1,4 @@ -import {TestApp, registerTestApp} from "../../tests/test-app"; +import {registerTestApp} from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; // >> router-outlet-example import { Component, NgModule } from "@angular/core"; diff --git a/tests/app/tests/detached-loader-tests.ts b/tests/app/tests/detached-loader-tests.ts index 014217e35..5b4f6a964 100644 --- a/tests/app/tests/detached-loader-tests.ts +++ b/tests/app/tests/detached-loader-tests.ts @@ -1,21 +1,7 @@ // make sure you import mocha-config before @angular/core -import {assert} from "./test-config"; -import {TestApp} from "./test-app"; -import { - Component, - ElementRef, - Renderer, - AfterViewInit, - OnInit, - ViewChild, - ChangeDetectionStrategy -} from "@angular/core"; -import {ProxyViewContainer} from "ui/proxy-view-container"; -import {Red} from "color/known-colors"; -import {dumpView} from "./test-utils"; -import {LayoutBase} from "ui/layouts/layout-base"; -import {StackLayout} from "ui/layouts/stack-layout"; -import {DetachedLoader} from "nativescript-angular/common/detached-loader"; +import { ChangeDetectionStrategy, Component, ViewChild } from "@angular/core"; +import { DetachedLoader } from "nativescript-angular"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; @Component({ template: `` @@ -25,18 +11,6 @@ class TestComponent { } class LoaderComponentBase { @ViewChild(DetachedLoader) public loader: DetachedLoader; - - public ready: Promise; - private resolve; - constructor() { - this.ready = new Promise((reslove, reject) => { - this.resolve = reslove; - }); - } - ngAfterViewInit() { - console.log("!!! ngAfterViewInit -> loader: " + this.loader); - this.resolve(this); - } } @Component({ @@ -45,9 +19,9 @@ class LoaderComponentBase { - ` + ` }) -export class LoaderComponent extends LoaderComponentBase { } +export class LoaderComponent extends LoaderComponentBase {} @Component({ selector: "loader-component-on-push", @@ -56,58 +30,27 @@ export class LoaderComponent extends LoaderComponentBase { } - ` + ` }) export class LoaderComponentOnPush extends LoaderComponentBase { } describe("DetachedLoader", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [LoaderComponent, LoaderComponentOnPush, TestComponent]).then((app) => { - console.log("TEST APP: " + app); - testApp = app; - }); - }); - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([LoaderComponent, LoaderComponentOnPush], [], [], [TestComponent])); + afterEach(nTestBedAfterEach()); - it("creates component", (done) => { - testApp.loadComponent(LoaderComponent) - .then((componentRef) => { - // wait for the ngAfterViewInit - return (componentRef.instance).ready; - }) - .then((comp) => { - // load test component with loader - return comp.loader.loadComponent(TestComponent); - }) - .then((compRef) => { - done(); - }) - .catch(done); + it("creates component", () => { + return nTestBedRender(LoaderComponent).then((fixture) => { + const component: LoaderComponent = fixture.componentRef.instance; + return component.loader.loadComponent(TestComponent); + }); }); - it("creates component when ChangeDetectionStrategy is OnPush", (done) => { - testApp.loadComponent(LoaderComponentOnPush) - .then((componentRef) => { - // wait for the ngAfterViewInit - return (componentRef.instance).ready; - }) - .then((comp) => { - // load test component with loader - return comp.loader.loadComponent(TestComponent); - }) - .then((compRef) => { - done(); - }) - .catch(done); + it("creates component when ChangeDetectionStrategy is OnPush", () => { + return nTestBedRender(LoaderComponentOnPush).then((fixture) => { + const component: LoaderComponentOnPush = fixture.componentRef.instance; + return component.loader.loadComponent(TestComponent); + }); }); }); diff --git a/tests/app/tests/list-view-tests.ts b/tests/app/tests/list-view-tests.ts index 3c3d7fb54..4e33ec002 100644 --- a/tests/app/tests/list-view-tests.ts +++ b/tests/app/tests/list-view-tests.ts @@ -1,9 +1,7 @@ import { assert } from "./test-config"; -import { Component, Input, AfterViewInit } from "@angular/core"; -import { TestApp } from "./test-app"; -import { RootLocator, ComponentView, getItemViewRoot } from "nativescript-angular/directives/list-view-comp"; -import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; - +import { Component, Input } from "@angular/core"; +import { ComponentFixture } from "@angular/core/testing"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; // import trace = require("trace"); // trace.setCategories("ns-list-view, " + trace.categories.Navigation); // trace.enable(); @@ -68,7 +66,7 @@ export class ItemTemplateComponent { - ` + ` }) export class TestListViewSelectorComponent { public myItems: Array = ITEMS; @@ -79,45 +77,27 @@ export class TestListViewSelectorComponent { } describe("ListView-tests", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [ - TestListViewComponent, - TestListViewSelectorComponent, - ItemTemplateComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([ + TestListViewComponent, + TestListViewSelectorComponent, + ItemTemplateComponent + ])); + afterEach(nTestBedAfterEach(false)); it("setupItemView is called for every item", (done) => { - testApp.loadComponent(TestListViewComponent).then((componentRef) => { - const component = componentRef.instance; - setTimeout(() => { + nTestBedRender(TestListViewComponent) + .then((fixture: ComponentFixture) => { + const component = fixture.componentRef.instance; assert.equal(component.counter, 3); done(); - }, 1000); - }) - .catch(done); + }); }); it("itemTemplateSelector selects templates", (done) => { - testApp.loadComponent(TestListViewSelectorComponent).then((componentRef) => { - setTimeout(() => { - assert.deepEqual(testTemplates, { first: 2, second: 1 }); - done(); - }, 1000); - }) - .catch(done); + nTestBedRender(TestListViewSelectorComponent).then(() => { + assert.deepEqual(testTemplates, {first: 2, second: 1}); + done(); + }); }); }); diff --git a/tests/app/tests/modal-dialog.ts b/tests/app/tests/modal-dialog.ts index 69a909c73..18036ec19 100644 --- a/tests/app/tests/modal-dialog.ts +++ b/tests/app/tests/modal-dialog.ts @@ -1,12 +1,14 @@ // make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { TestApp } from "./test-app"; -import { Component, ViewContainerRef } from "@angular/core"; -import { Page } from "ui/page"; -import { topmost } from "ui/frame"; -import { ModalDialogParams, ModalDialogService } from "nativescript-angular/directives/dialogs"; +import {assert} from "./test-config"; +import {Component, ViewContainerRef} from "@angular/core"; +import {Page} from "ui/page"; +import {topmost} from "ui/frame"; +import {ModalDialogParams, ModalDialogService} from "nativescript-angular/directives/dialogs"; -import { device, platformNames } from "platform"; +import {device, platformNames} from "platform"; + +import {ComponentFixture} from "@angular/core/testing"; +import {nTestBedRender, nTestBedAfterEach, nTestBedBeforeEach} from "nativescript-angular/testing"; const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0; @Component({ @@ -47,20 +49,13 @@ export class SuccessComponent { } describe("modal-dialog", () => { - let testApp: TestApp = null; + beforeEach(nTestBedBeforeEach([FailComponent, SuccessComponent], [], [], [ModalComponent])); + afterEach(nTestBedAfterEach()); before((done) => { - TestApp.create([], [ModalComponent, FailComponent, SuccessComponent]).then((app) => { - testApp = app; - - // HACK: Wait for the navigations from the test runner app - // Remove the setTimeout when test runner start tests on page.navigatedTo - setTimeout(done, 1000); - }); - }); - - after(() => { - testApp.dispose(); + // HACK: Wait for the navigations from the test runner app + // Remove the setTimeout when test runner start tests on page.navigatedTo + setTimeout(() => done(), 1000); }); afterEach(() => { @@ -69,36 +64,38 @@ describe("modal-dialog", () => { console.log("Warning: closing a leftover modal page!"); page.modal.closeModal(); } - testApp.disposeComponents(); }); it("showModal throws when there is no viewContainer provided", (done) => { - testApp.loadComponent(FailComponent) - .then((ref) => { - const service = ref.instance.service; + nTestBedRender(FailComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; assert.throws(() => service.showModal(ModalComponent, {}), - "No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions." - ); - }).then(() => done(), err => done(err)); + "No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions." + ); + }) + .then(() => done()) + .catch((e) => done(e)); }); it("showModal succeeds when there is viewContainer provided", (done) => { - testApp.loadComponent(SuccessComponent) - .then((ref) => { - const service = ref.instance.service; - const comp = ref.instance; - return service.showModal(ModalComponent, { viewContainerRef: comp.vcRef }); + nTestBedRender(SuccessComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; + const comp = fixture.componentRef.instance; + return service.showModal(ModalComponent, {viewContainerRef: comp.vcRef}); }) - .then((res) => setTimeout(done, CLOSE_WAIT), err => done(err)); // wait for the dialog to close in IOS + .then((res) => setTimeout(done, CLOSE_WAIT)) // wait for the dialog to close in IOS + .catch((e) => done(e)); }); it("showModal passes modal params and gets result when resolved", (done) => { - const context = { property: "my context" }; - testApp.loadComponent(SuccessComponent) - .then((ref) => { - const service = ref.instance.service; - const comp = ref.instance; + const context = {property: "my context"}; + nTestBedRender(SuccessComponent) + .then((fixture: ComponentFixture) => { + const service = fixture.componentRef.instance.service; + const comp = fixture.componentRef.instance; return service.showModal(ModalComponent, { viewContainerRef: comp.vcRef, context: context @@ -107,6 +104,7 @@ describe("modal-dialog", () => { .then((res) => { assert.strictEqual(res, context); setTimeout(done, CLOSE_WAIT); // wait for the dialog to close in IOS - }, err => done(err)); + }) + .catch((e) => done(e)); }); }); diff --git a/tests/app/tests/platform-filter-components.ts b/tests/app/tests/platform-filter-components.ts index 5665e1364..72d3d5c62 100644 --- a/tests/app/tests/platform-filter-components.ts +++ b/tests/app/tests/platform-filter-components.ts @@ -1,122 +1,102 @@ -// make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { Component, ElementRef } from "@angular/core"; -import { dumpView, createDevice } from "./test-utils"; -import { TestApp } from "./test-app"; -import { DEVICE } from "nativescript-angular/platform-providers"; -import { platformNames } from "platform"; - -@Component({ - template: ` - - - ` -}) -export class IosSpecificComponent { - constructor(public elementRef: ElementRef) { } -} - -@Component({ - template: ` - - - ` -}) -export class AndroidSpecificComponent { - constructor(public elementRef: ElementRef) { } -} - -@Component({ - template: ` - - - ` -}) -export class PlatformSpecificAttributeComponent { - constructor(public elementRef: ElementRef) { } -} - -describe("Platform filter directives", () => { - describe("on IOS device", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([{ provide: DEVICE, useValue: createDevice(platformNames.ios) }], [ - PlatformSpecificAttributeComponent, - AndroidSpecificComponent, - IosSpecificComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - it("does render ios specific content", () => { - return testApp.loadComponent(IosSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=IOS])") >= 0); - }); - }); - - it("does not render android specific content", () => { - return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); - }); - }); - - - it("applies iOS specific attribute", () => { - return testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=IOS])))", - dumpView(componentRoot, true)); - }); - }); - }); - - describe("on Android device", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([{ provide: DEVICE, useValue: createDevice(platformNames.android) }], [ - AndroidSpecificComponent, - IosSpecificComponent, - PlatformSpecificAttributeComponent - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - it("does render android specific content", () => { - return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=ANDROID])") >= 0); - }); - }); - - it("does not render ios specific content", () => { - return testApp.loadComponent(IosSpecificComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); - }); - }); - - it("applies Android specific attribute", () => { - return testApp.loadComponent(PlatformSpecificAttributeComponent).then((componentRef) => { - const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal( - "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", - dumpView(componentRoot, true)); - }); - }); - }); -}); +// make sure you import mocha-config before @angular/core +import { assert } from "./test-config"; +import { Component, ElementRef } from "@angular/core"; +import { dumpView, createDevice } from "./test-utils"; +import { DEVICE } from "nativescript-angular/platform-providers"; +import { platformNames } from "platform"; +import { nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender } from "nativescript-angular/testing"; + +@Component({ + template: ` + + + ` +}) +export class IosSpecificComponent { + constructor(public elementRef: ElementRef) { } +} + +@Component({ + template: ` + + + ` +}) +export class AndroidSpecificComponent { + constructor(public elementRef: ElementRef) { } +} + +@Component({ + template: ` + + + ` +}) +export class PlatformSpecificAttributeComponent { + constructor(public elementRef: ElementRef) { } +} + +describe("Platform filter directives", () => { + describe("on IOS device", () => { + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.ios)}] + )); + afterEach(nTestBedAfterEach()); + it("does render ios specific content", () => { + return nTestBedRender(IosSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=IOS])") >= 0); + }); + }); + it("does not render android specific content", () => { + return nTestBedRender(AndroidSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); + }); + }); + it("applies iOS specific attribute", () => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal( + "(ProxyViewContainer (StackLayout (Label[text=IOS])))", + dumpView(componentRoot, true)); + }); + }); + }); + + describe("on Android device", () => { + beforeEach(nTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.android)}] + )); + afterEach(nTestBedAfterEach()); + + it("does render android specific content", () => { + return nTestBedRender(AndroidSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(Label[text=ANDROID])") >= 0); + }); + }); + it("does not render ios specific content", () => { + return nTestBedRender(IosSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("Label") < 0); + }); + }); + it("applies Android specific attribute", () => { + return nTestBedRender(PlatformSpecificAttributeComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal( + "(ProxyViewContainer (StackLayout (Label[text=ANDROID])))", + dumpView(componentRoot, true)); + }); + }); + }); +}); diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index ebb9091ee..cdf715870 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -1,18 +1,27 @@ // make sure you import mocha-config before @angular/core -import { assert } from "./test-config"; -import { Component, ElementRef, Renderer2, NgZone, ViewChild } from "@angular/core"; -import { ProxyViewContainer } from "ui/proxy-view-container"; -import { Red } from "color/known-colors"; -import { dumpView } from "./test-utils"; -import { TestApp } from "./test-app"; -import { LayoutBase } from "ui/layouts/layout-base"; -import { StackLayout } from "ui/layouts/stack-layout"; -import { ContentView } from "ui/content-view"; -import { Button } from "ui/button"; -import { NgView } from "nativescript-angular/element-registry"; -import { registerElement } from "nativescript-angular/element-registry"; +import {assert} from "./test-config"; +import {Component, ComponentRef, ElementRef, NgZone, Renderer2, ViewChild} from "@angular/core"; +import {ProxyViewContainer} from "ui/proxy-view-container"; +import {Red} from "color/known-colors"; +import {dumpView} from "./test-utils"; +import {LayoutBase} from "ui/layouts/layout-base"; +import {StackLayout} from "ui/layouts/stack-layout"; +import {ContentView} from "ui/content-view"; +import {Button} from "ui/button"; +import {registerElement} from "nativescript-angular/element-registry"; import * as button from "tns-core-modules/ui/button"; import * as view from "tns-core-modules/ui/core/view"; +import {nTestBedAfterEach, nTestBedBeforeEach, nTestBedRender} from "nativescript-angular/testing"; +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {Observable} from "rxjs/Observable"; +import {ReplaySubject} from "rxjs/ReplaySubject"; + +@Component({ + template: `` +}) +export class ZonedRenderer { + constructor(public elementRef: ElementRef, public renderer: Renderer2) { } +} @Component({ template: `` @@ -219,10 +228,10 @@ export class NgControlSettersCount { get buttons(): ElementRef[] { return [this.btn1, this.btn2, this.btn3, this.btn4]; } - isAfterViewInit: boolean = false; + ready$: Observable = new ReplaySubject(1); ngAfterViewInit() { - this.isAfterViewInit = true; + (this.ready$ as ReplaySubject).next(true); } } @@ -237,46 +246,36 @@ export class NgForLabel { } describe("Renderer E2E", () => { - let testApp: TestApp = null; - - before(() => { - return TestApp.create([], [ - LayoutWithLabel, LabelCmp, LabelContainer, - ProjectableCmp, ProjectionContainer, - StyledLabelCmp, StyledLabelCmp2, - NgIfLabel, NgIfThenElseComponent, NgIfMultiple, - NgIfTwoElements, NgIfMultiple, - NgIfElseComponent, NgIfThenElseComponent, - NgForLabel, - ]).then((app) => { - testApp = app; - }); - }); - - after(() => { - testApp.dispose(); - }); - - afterEach(() => { - testApp.disposeComponents(); - }); + beforeEach(nTestBedBeforeEach([ + LayoutWithLabel, LabelCmp, LabelContainer, + ProjectableCmp, ProjectionContainer, + StyledLabelCmp, StyledLabelCmp2, + NgIfLabel, NgIfThenElseComponent, NgIfMultiple, + NgIfTwoElements, NgIfMultiple, + NgIfElseComponent, NgIfThenElseComponent, + NgForLabel, ZonedRenderer + ])); + afterEach(nTestBedAfterEach(false)); it("component with a layout", () => { - return testApp.loadComponent(LayoutWithLabel).then((componentRef) => { + return nTestBedRender(LayoutWithLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot)); }); }); it("component without a layout", () => { - return testApp.loadComponent(LabelContainer).then((componentRef) => { + return nTestBedRender(LabelContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot)); }); }); it("projects content into components", () => { - return testApp.loadComponent(ProjectionContainer).then((componentRef) => { + return nTestBedRender(ProjectionContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", @@ -285,7 +284,8 @@ describe("Renderer E2E", () => { }); it("applies component styles from single source", () => { - return testApp.loadComponent(StyledLabelCmp).then((componentRef) => { + return nTestBedRender(StyledLabelCmp).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; const label = (componentRoot).getChildAt(0); assert.equal(Red, label.style.color.hex); @@ -293,7 +293,8 @@ describe("Renderer E2E", () => { }); it("applies component styles from multiple sources", () => { - return testApp.loadComponent(StyledLabelCmp2).then((componentRef) => { + return nTestBedRender(StyledLabelCmp2).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; const layout = (componentRoot).getChildAt(0); @@ -316,15 +317,18 @@ describe("Renderer E2E", () => { done(); }; - testApp.zone.run(() => { - testApp.renderer.listen(view, eventName, callback); + nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + fixture.componentInstance.renderer.listen(view, eventName, callback); + }); + + setTimeout(() => { + fixture.ngZone.runOutsideAngular(() => { + view.notify(eventArg); + }); + }, 10); }); - setTimeout(() => { - testApp.zone.runOutsideAngular(() => { - view.notify(eventArg); - }); - }, 10); }); it("executes events inside NgZone when listen is called outside NgZone", (done) => { @@ -336,40 +340,44 @@ describe("Renderer E2E", () => { assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone"); done(); }; + nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.runOutsideAngular(() => { + fixture.componentInstance.renderer.listen(view, eventName, callback); - testApp.zone.runOutsideAngular(() => { - testApp.renderer.listen(view, eventName, callback); - - view.notify(eventArg); + view.notify(eventArg); + }); }); }); describe("Structural directives", () => { it("ngIf hides component when false", () => { - return testApp.loadComponent(NgIfLabel).then((componentRef) => { + return nTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer)", dumpView(componentRoot)); }); }); it("ngIf show component when true", () => { - return testApp.loadComponent(NgIfLabel).then((componentRef) => { + return nTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot)); }); }); it("ngIf shows elements in correct order when two are rendered", () => { - return testApp.loadComponent(NgIfTwoElements).then((componentRef) => { + return nTestBedRender(NgIfTwoElements).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer (StackLayout (Label), (Button)))", dumpView(componentRoot)); @@ -377,12 +385,13 @@ describe("Renderer E2E", () => { }); it("ngIf shows elements in correct order when multiple are rendered and there's *ngIf", () => { - return testApp.loadComponent(NgIfMultiple).then((componentRef) => { + return nTestBedRender(NgIfMultiple).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -398,11 +407,12 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'if' template when condition is true", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nTestBedRender(NgIfElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + @@ -416,12 +426,13 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nTestBedRender(NgIfElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = false; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -434,11 +445,12 @@ describe("Renderer E2E", () => { }); it("ngIfThenElse show 'then' template when condition is true", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nTestBedRender(NgIfThenElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -452,12 +464,13 @@ describe("Renderer E2E", () => { it("ngIfThenElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nTestBedRender(NgIfThenElseComponent).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = false; - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + "(StackLayout " + @@ -470,7 +483,8 @@ describe("Renderer E2E", () => { }); it("ngFor creates element for each item", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( "(ProxyViewContainer (Label[text=one]), (Label[text=two]), (Label[text=three]))", @@ -479,12 +493,13 @@ describe("Renderer E2E", () => { }); it("ngFor updates when item is removed", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.items.splice(1, 1); - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer (Label[text=one]), (Label[text=three]))", @@ -493,12 +508,13 @@ describe("Renderer E2E", () => { }); it("ngFor updates when item is inserted", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nTestBedRender(NgForLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.items.splice(1, 0, "new"); - testApp.appRef.tick(); + fixture.detectChanges(); assert.equal( "(ProxyViewContainer " + @@ -510,20 +526,17 @@ describe("Renderer E2E", () => { }); describe("Renderer createElement", () => { - let testApp: TestApp = null; let renderer: Renderer2 = null; - - before(() => { - return TestApp.create().then((app) => { - testApp = app; - renderer = testApp.renderer; + beforeEach(nTestBedBeforeEach([ZonedRenderer])); + afterEach(nTestBedAfterEach(false)); + beforeEach(() => { + return nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + renderer = fixture.componentInstance.renderer; + }); }); }); - after(() => { - testApp.dispose(); - }); - it("creates element from CamelCase", () => { const result = renderer.createElement("StackLayout"); assert.instanceOf(result, StackLayout, "Renderer should create StackLayout form 'StackLayout'"); @@ -546,20 +559,17 @@ describe("Renderer createElement", () => { }); describe("Renderer attach/detach", () => { - let testApp: TestApp = null; let renderer: Renderer2 = null; - - before(() => { - return TestApp.create().then((app) => { - testApp = app; - renderer = testApp.renderer; + beforeEach(nTestBedBeforeEach([ZonedRenderer])); + afterEach(nTestBedAfterEach(false)); + beforeEach(() => { + return nTestBedRender(ZonedRenderer).then((fixture: ComponentFixture) => { + fixture.ngZone.run(() => { + renderer = fixture.componentInstance.renderer; + }); }); }); - after(() => { - testApp.dispose(); - }); - it("createElement element with parent attaches element to content view", () => { const parent = renderer.createElement("ContentView"); const button = + // because the onclick function is internal raw uncompiled handler + // the onclick will be evaluated when first time event was triggered or + // the property is accessed, https://github.com/angular/zone.js/issues/525 + // so we should use original native get to retrieve the handler + if (r === null) { + if (originalDesc && originalDesc.get) { + r = originalDesc.get.apply(this, arguments); + if (r) { + desc.set.apply(this, [r]); + if (typeof this['removeAttribute'] === 'function') { + this.removeAttribute(prop); + } + } + } + } + return this[_prop] || null; + }; + Object.defineProperty(obj, prop, desc); +} -function isPropertyWritable(propertyDesc) { - if (!propertyDesc) { - return true; +function patchOnProperties(obj, properties) { + var onProperties = []; + for (var prop in obj) { + if (prop.substr(0, 2) == 'on') { + onProperties.push(prop); + } } - if (propertyDesc.writable === false) { - return false; + for (var j = 0; j < onProperties.length; j++) { + patchProperty(obj, onProperties[j]); } - if (typeof propertyDesc.get === FUNCTION && typeof propertyDesc.set === UNDEFINED) { - return false; + if (properties) { + for (var i = 0; i < properties.length; i++) { + patchProperty(obj, 'on' + properties[i]); + } } - return true; } -var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); -// Make sure to access `process` through `_global` so that WebPack does not accidently browserify -// this code. -var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && - {}.toString.call(_global.process) === '[object process]'); -// we are in electron of nw, so we are both browser and nodejs -// Make sure to access `process` through `_global` so that WebPack does not accidently browserify -// this code. -var isMix = typeof _global.process !== 'undefined' && - {}.toString.call(_global.process) === '[object process]' && !isWebWorker && - !!(typeof window !== 'undefined' && window['HTMLElement']); +var EVENT_TASKS = zoneSymbol('eventTasks'); +// For EventTarget +var ADD_EVENT_LISTENER = 'addEventListener'; +var REMOVE_EVENT_LISTENER = 'removeEventListener'; +function findExistingRegisteredTask(target, handler, name, capture, remove) { + var eventTasks = target[EVENT_TASKS]; + if (eventTasks) { + for (var i = 0; i < eventTasks.length; i++) { + var eventTask = eventTasks[i]; + var data = eventTask.data; + var listener = data.handler; + if ((data.handler === handler || listener.listener === handler) && + data.useCapturing === capture && data.eventName === name) { + if (remove) { + eventTasks.splice(i, 1); + } + return eventTask; + } + } + } + return null; +} +function attachRegisteredEvent(target, eventTask, isPrepend) { + var eventTasks = target[EVENT_TASKS]; + if (!eventTasks) { + eventTasks = target[EVENT_TASKS] = []; + } + if (isPrepend) { + eventTasks.unshift(eventTask); + } + else { + eventTasks.push(eventTask); + } +} +var defaultListenerMetaCreator = function (self, args) { + return { + useCapturing: args[2], + eventName: args[0], + handler: args[1], + target: self || _global, + name: args[0], + invokeAddFunc: function (addFnSymbol, delegate) { + if (delegate && delegate.invoke) { + return this.target[addFnSymbol](this.eventName, delegate.invoke, this.useCapturing); + } + else { + return this.target[addFnSymbol](this.eventName, delegate, this.useCapturing); + } + }, + invokeRemoveFunc: function (removeFnSymbol, delegate) { + if (delegate && delegate.invoke) { + return this.target[removeFnSymbol](this.eventName, delegate.invoke, this.useCapturing); + } + else { + return this.target[removeFnSymbol](this.eventName, delegate, this.useCapturing); + } + } + }; +}; +function makeZoneAwareAddListener(addFnName, removeFnName, useCapturingParam, allowDuplicates, isPrepend, metaCreator) { + if (useCapturingParam === void 0) { useCapturingParam = true; } + if (allowDuplicates === void 0) { allowDuplicates = false; } + if (isPrepend === void 0) { isPrepend = false; } + if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } + var addFnSymbol = zoneSymbol(addFnName); + var removeFnSymbol = zoneSymbol(removeFnName); + var defaultUseCapturing = useCapturingParam ? false : undefined; + function scheduleEventListener(eventTask) { + var meta = eventTask.data; + attachRegisteredEvent(meta.target, eventTask, isPrepend); + return meta.invokeAddFunc(addFnSymbol, eventTask); + } + function cancelEventListener(eventTask) { + var meta = eventTask.data; + findExistingRegisteredTask(meta.target, eventTask.invoke, meta.eventName, meta.useCapturing, true); + return meta.invokeRemoveFunc(removeFnSymbol, eventTask); + } + return function zoneAwareAddListener(self, args) { + var data = metaCreator(self, args); + data.useCapturing = data.useCapturing || defaultUseCapturing; + // - Inside a Web Worker, `this` is undefined, the context is `global` + // - When `addEventListener` is called on the global context in strict mode, `this` is undefined + // see https://github.com/angular/zone.js/issues/190 + var delegate = null; + if (typeof data.handler == 'function') { + delegate = data.handler; + } + else if (data.handler && data.handler.handleEvent) { + delegate = function (event) { return data.handler.handleEvent(event); }; + } + var validZoneHandler = false; + try { + // In cross site contexts (such as WebDriver frameworks like Selenium), + // accessing the handler object here will cause an exception to be thrown which + // will fail tests prematurely. + validZoneHandler = data.handler && data.handler.toString() === '[object FunctionWrapper]'; + } + catch (error) { + // Returning nothing here is fine, because objects in a cross-site context are unusable + return; + } + // Ignore special listeners of IE11 & Edge dev tools, see + // https://github.com/angular/zone.js/issues/150 + if (!delegate || validZoneHandler) { + return data.invokeAddFunc(addFnSymbol, data.handler); + } + if (!allowDuplicates) { + var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, false); + if (eventTask) { + // we already registered, so this will have noop. + return data.invokeAddFunc(addFnSymbol, eventTask); + } + } + var zone = Zone.current; + var source = data.target.constructor['name'] + '.' + addFnName + ':' + data.eventName; + zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener); + }; +} +function makeZoneAwareRemoveListener(fnName, useCapturingParam, metaCreator) { + if (useCapturingParam === void 0) { useCapturingParam = true; } + if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } + var symbol = zoneSymbol(fnName); + var defaultUseCapturing = useCapturingParam ? false : undefined; + return function zoneAwareRemoveListener(self, args) { + var data = metaCreator(self, args); + data.useCapturing = data.useCapturing || defaultUseCapturing; + // - Inside a Web Worker, `this` is undefined, the context is `global` + // - When `addEventListener` is called on the global context in strict mode, `this` is undefined + // see https://github.com/angular/zone.js/issues/190 + var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, true); + if (eventTask) { + eventTask.zone.cancelTask(eventTask); + } + else { + data.invokeRemoveFunc(symbol, data.handler); + } + }; +} +var zoneAwareAddEventListener = makeZoneAwareAddListener(ADD_EVENT_LISTENER, REMOVE_EVENT_LISTENER); +var zoneAwareRemoveEventListener = makeZoneAwareRemoveListener(REMOVE_EVENT_LISTENER); +function patchEventTargetMethods(obj, addFnName, removeFnName, metaCreator) { + if (addFnName === void 0) { addFnName = ADD_EVENT_LISTENER; } + if (removeFnName === void 0) { removeFnName = REMOVE_EVENT_LISTENER; } + if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } + if (obj && obj[addFnName]) { + patchMethod(obj, addFnName, function () { return makeZoneAwareAddListener(addFnName, removeFnName, true, false, false, metaCreator); }); + patchMethod(obj, removeFnName, function () { return makeZoneAwareRemoveListener(removeFnName, true, metaCreator); }); + return true; + } + else { + return false; + } +} var originalInstanceKey = zoneSymbol('originalInstance'); // wrap some native API on `window` + +function createNamedFn(name, delegate) { + try { + return (Function('f', "return function " + name + "(){return f(this, arguments)}"))(delegate); + } + catch (error) { + // if we fail, we must be CSP, just return delegate. + return function () { + return delegate(this, arguments); + }; + } +} function patchMethod(target, name, patchFn) { var proto = target; - while (proto && !proto.hasOwnProperty(name)) { + while (proto && Object.getOwnPropertyNames(proto).indexOf(name) === -1) { proto = Object.getPrototypeOf(proto); } if (!proto && target[name]) { @@ -706,25 +1605,16 @@ function patchMethod(target, name, patchFn) { var delegate; if (proto && !(delegate = proto[delegateName])) { delegate = proto[delegateName] = proto[name]; - // check whether proto[name] is writable - // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob - var desc = proto && Object.getOwnPropertyDescriptor(proto, name); - if (isPropertyWritable(desc)) { - var patchDelegate_1 = patchFn(delegate, delegateName, name); - proto[name] = function () { - return patchDelegate_1(this, arguments); - }; - attachOriginToPatched(proto[name], delegate); - } + proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); } return delegate; } // TODO: @JiaLiPassion, support cancel task later if necessary -function attachOriginToPatched(patched, original) { - patched[zoneSymbol('OriginalDelegate')] = original; -} + +Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; +Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; /** * @license @@ -733,23 +1623,12 @@ function attachOriginToPatched(patched, original) { * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -/** - * @fileoverview - * @suppress {missingRequire} - */ -var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; setName += nameSuffix; cancelName += nameSuffix; var tasksByHandleId = {}; - var NUMBER = 'number'; - var STRING = 'string'; - var FUNCTION = 'function'; - var INTERVAL = 'Interval'; - var TIMEOUT = 'Timeout'; - var NOT_SCHEDULED = 'notScheduled'; function scheduleTask(task) { var data = task.data; function timer() { @@ -757,39 +1636,27 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - if (task.data && task.data.isPeriodic) { - // issue-934, task will be cancelled - // even it is a periodic task such as - // setInterval - return; - } - if (typeof data.handleId === NUMBER) { - // in non-nodejs env, we remove timerId - // from local cache - delete tasksByHandleId[data.handleId]; - } - else if (data.handleId) { - // Node returns complex objects as handleIds - // we remove task reference from timer object - data.handleId[taskSymbol] = null; - } + delete tasksByHandleId[data.handleId]; } } + data.args[0] = timer; data.handleId = setNative.apply(window, data.args); + tasksByHandleId[data.handleId] = task; return task; } function clearTask(task) { + delete tasksByHandleId[task.data.handleId]; return clearNative(task.data.handleId); } setNative = patchMethod(window, setName, function (delegate) { return function (self, args) { - if (typeof args[0] === FUNCTION) { + if (typeof args[0] === 'function') { var zone = Zone.current; var options = { handleId: null, - isPeriodic: nameSuffix === INTERVAL, - delay: (nameSuffix === TIMEOUT || nameSuffix === INTERVAL) ? args[1] || 0 : null, + isPeriodic: nameSuffix === 'Interval', + delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, args: args }; var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); @@ -798,26 +1665,13 @@ function patchTimer(window, setName, cancelName, nameSuffix) { } // Node.js must additionally support the ref and unref functions. var handle = task.data.handleId; - if (typeof handle === NUMBER) { - // for non nodejs env, we save handleId: task - // mapping in local cache for clearTimeout - tasksByHandleId[handle] = task; - } - else if (handle) { - // for nodejs env, we save task - // reference in timerId Object for clearTimeout - handle[taskSymbol] = task; - } // check whether handle is null, because some polyfill or browser // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame - if (handle && handle.ref && handle.unref && typeof handle.ref === FUNCTION && - typeof handle.unref === FUNCTION) { + if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && + typeof handle.unref === 'function') { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } - if (typeof handle === NUMBER || handle) { - return handle; - } return task; } else { @@ -827,29 +1681,10 @@ function patchTimer(window, setName, cancelName, nameSuffix) { }; }); clearNative = patchMethod(window, cancelName, function (delegate) { return function (self, args) { - var id = args[0]; - var task; - if (typeof id === NUMBER) { - // non nodejs env. - task = tasksByHandleId[id]; - } - else { - // nodejs env. - task = id && id[taskSymbol]; - // other environments. - if (!task) { - task = id; - } - } - if (task && typeof task.type === STRING) { - if (task.state !== NOT_SCHEDULED && + var task = typeof args[0] === 'number' ? tasksByHandleId[args[0]] : args[0]; + if (task && typeof task.type === 'string') { + if (task.state !== 'notScheduled' && (task.cancelFn && task.data.isPeriodic || task.runCount === 0)) { - if (typeof id === NUMBER) { - delete tasksByHandleId[id]; - } - else if (id) { - id[taskSymbol] = null; - } // Do not cancel already canceled functions task.zone.cancelTask(task); } diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js index 522244227..0aa13a09f 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -11,437 +11,6 @@ (factory()); }(this, (function () { 'use strict'; -var __values = (undefined && undefined.__values) || function (o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; - if (m) return m.call(o); - return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; -}; -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { - function readableObjectToString(obj) { - if (obj && obj.toString === Object.prototype.toString) { - var className = obj.constructor && obj.constructor.name; - return (className ? className : '') + ': ' + JSON.stringify(obj); - } - return obj ? obj.toString() : Object.prototype.toString.call(obj); - } - var __symbol__ = api.symbol; - var _uncaughtPromiseErrors = []; - var symbolPromise = __symbol__('Promise'); - var symbolThen = __symbol__('then'); - var creationTrace = '__creationTrace__'; - api.onUnhandledError = function (e) { - if (api.showUncaughtError()) { - var rejection = e && e.rejection; - if (rejection) { - console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); - } - else { - console.error(e); - } - } - }; - api.microtaskDrainDone = function () { - while (_uncaughtPromiseErrors.length) { - var _loop_1 = function () { - var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); - try { - uncaughtPromiseError.zone.runGuarded(function () { - throw uncaughtPromiseError; - }); - } - catch (error) { - handleUnhandledRejection(error); - } - }; - while (_uncaughtPromiseErrors.length) { - _loop_1(); - } - } - }; - var UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); - function handleUnhandledRejection(e) { - api.onUnhandledError(e); - try { - var handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; - if (handler && typeof handler === 'function') { - handler.apply(this, [e]); - } - } - catch (err) { - } - } - function isThenable(value) { - return value && value.then; - } - function forwardResolution(value) { - return value; - } - function forwardRejection(rejection) { - return ZoneAwarePromise.reject(rejection); - } - var symbolState = __symbol__('state'); - var symbolValue = __symbol__('value'); - var source = 'Promise.then'; - var UNRESOLVED = null; - var RESOLVED = true; - var REJECTED = false; - var REJECTED_NO_CATCH = 0; - function makeResolver(promise, state) { - return function (v) { - try { - resolvePromise(promise, state, v); - } - catch (err) { - resolvePromise(promise, false, err); - } - // Do not return value or you will break the Promise spec. - }; - } - var once = function () { - var wasCalled = false; - return function wrapper(wrappedFunction) { - return function () { - if (wasCalled) { - return; - } - wasCalled = true; - wrappedFunction.apply(null, arguments); - }; - }; - }; - var TYPE_ERROR = 'Promise resolved with itself'; - var OBJECT = 'object'; - var FUNCTION = 'function'; - var CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace'); - // Promise Resolution - function resolvePromise(promise, state, value) { - var onceWrapper = once(); - if (promise === value) { - throw new TypeError(TYPE_ERROR); - } - if (promise[symbolState] === UNRESOLVED) { - // should only get value.then once based on promise spec. - var then = null; - try { - if (typeof value === OBJECT || typeof value === FUNCTION) { - then = value && value.then; - } - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - return promise; - } - // if (value instanceof ZoneAwarePromise) { - if (state !== REJECTED && value instanceof ZoneAwarePromise && - value.hasOwnProperty(symbolState) && value.hasOwnProperty(symbolValue) && - value[symbolState] !== UNRESOLVED) { - clearRejectedNoCatch(value); - resolvePromise(promise, value[symbolState], value[symbolValue]); - } - else if (state !== REJECTED && typeof then === FUNCTION) { - try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); - } - catch (err) { - onceWrapper(function () { - resolvePromise(promise, false, err); - })(); - } - } - else { - promise[symbolState] = state; - var queue = promise[symbolValue]; - promise[symbolValue] = value; - // record task information in value when error occurs, so we can - // do some additional work such as render longStackTrace - if (state === REJECTED && value instanceof Error) { - // check if longStackTraceZone is here - var trace = Zone.currentTask && Zone.currentTask.data && - Zone.currentTask.data[creationTrace]; - if (trace) { - // only keep the long stack trace into error when in longStackTraceZone - Object.defineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { configurable: true, enumerable: false, writable: true, value: trace }); - } - } - for (var i = 0; i < queue.length;) { - scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); - } - if (queue.length == 0 && state == REJECTED) { - promise[symbolState] = REJECTED_NO_CATCH; - try { - // try to print more readable error log - throw new Error('Uncaught (in promise): ' + readableObjectToString(value) + - (value && value.stack ? '\n' + value.stack : '')); - } - catch (err) { - var error_1 = err; - error_1.rejection = value; - error_1.promise = promise; - error_1.zone = Zone.current; - error_1.task = Zone.currentTask; - _uncaughtPromiseErrors.push(error_1); - api.scheduleMicroTask(); // to make sure that it is running - } - } - } - } - // Resolving an already resolved promise is a noop. - return promise; - } - var REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); - function clearRejectedNoCatch(promise) { - if (promise[symbolState] === REJECTED_NO_CATCH) { - // if the promise is rejected no catch status - // and queue.length > 0, means there is a error handler - // here to handle the rejected promise, we should trigger - // windows.rejectionhandled eventHandler or nodejs rejectionHandled - // eventHandler - try { - var handler = Zone[REJECTION_HANDLED_HANDLER]; - if (handler && typeof handler === FUNCTION) { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); - } - } - catch (err) { - } - promise[symbolState] = REJECTED; - for (var i = 0; i < _uncaughtPromiseErrors.length; i++) { - if (promise === _uncaughtPromiseErrors[i].promise) { - _uncaughtPromiseErrors.splice(i, 1); - } - } - } - } - function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { - clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? - (typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution : - (typeof onRejected === FUNCTION) ? onRejected : forwardRejection; - zone.scheduleMicroTask(source, function () { - try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); - } - catch (error) { - resolvePromise(chainPromise, false, error); - } - }); - } - var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; - var ZoneAwarePromise = /** @class */ (function () { - function ZoneAwarePromise(executor) { - var promise = this; - if (!(promise instanceof ZoneAwarePromise)) { - throw new Error('Must be an instanceof Promise.'); - } - promise[symbolState] = UNRESOLVED; - promise[symbolValue] = []; // queue; - try { - executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); - } - catch (error) { - resolvePromise(promise, false, error); - } - } - ZoneAwarePromise.toString = function () { - return ZONE_AWARE_PROMISE_TO_STRING; - }; - ZoneAwarePromise.resolve = function (value) { - return resolvePromise(new this(null), RESOLVED, value); - }; - ZoneAwarePromise.reject = function (error) { - return resolvePromise(new this(null), REJECTED, error); - }; - ZoneAwarePromise.race = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - function onResolve(value) { - promise && (promise = null || resolve(value)); - } - function onReject(error) { - promise && (promise = null || reject(error)); - } - try { - for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { - var value = values_1_1.value; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then(onResolve, onReject); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1); - } - finally { if (e_1) throw e_1.error; } - } - return promise; - var e_1, _a; - }; - ZoneAwarePromise.all = function (values) { - var resolve; - var reject; - var promise = new this(function (res, rej) { - resolve = res; - reject = rej; - }); - var count = 0; - var resolvedValues = []; - try { - for (var values_2 = __values(values), values_2_1 = values_2.next(); !values_2_1.done; values_2_1 = values_2.next()) { - var value = values_2_1.value; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then((function (index) { return function (value) { - resolvedValues[index] = value; - count--; - if (!count) { - resolve(resolvedValues); - } - }; })(count), reject); - count++; - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (values_2_1 && !values_2_1.done && (_a = values_2.return)) _a.call(values_2); - } - finally { if (e_2) throw e_2.error; } - } - if (!count) - resolve(resolvedValues); - return promise; - var e_2, _a; - }; - ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { - var chainPromise = new this.constructor(null); - var zone = Zone.current; - if (this[symbolState] == UNRESOLVED) { - this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); - } - else { - scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); - } - return chainPromise; - }; - ZoneAwarePromise.prototype.catch = function (onRejected) { - return this.then(null, onRejected); - }; - return ZoneAwarePromise; - }()); - // Protect against aggressive optimizers dropping seemingly unused properties. - // E.g. Closure Compiler in advanced mode. - ZoneAwarePromise['resolve'] = ZoneAwarePromise.resolve; - ZoneAwarePromise['reject'] = ZoneAwarePromise.reject; - ZoneAwarePromise['race'] = ZoneAwarePromise.race; - ZoneAwarePromise['all'] = ZoneAwarePromise.all; - var NativePromise = global[symbolPromise] = global['Promise']; - var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); - var desc = Object.getOwnPropertyDescriptor(global, 'Promise'); - if (!desc || desc.configurable) { - desc && delete desc.writable; - desc && delete desc.value; - if (!desc) { - desc = { configurable: true, enumerable: true }; - } - desc.get = function () { - // if we already set ZoneAwarePromise, use patched one - // otherwise return native one. - return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; - }; - desc.set = function (NewNativePromise) { - if (NewNativePromise === ZoneAwarePromise) { - // if the NewNativePromise is ZoneAwarePromise - // save to global - global[ZONE_AWARE_PROMISE] = NewNativePromise; - } - else { - // if the NewNativePromise is not ZoneAwarePromise - // for example: after load zone.js, some library just - // set es6-promise to global, if we set it to global - // directly, assertZonePatched will fail and angular - // will not loaded, so we just set the NewNativePromise - // to global[symbolPromise], so the result is just like - // we load ES6 Promise before zone.js - global[symbolPromise] = NewNativePromise; - if (!NewNativePromise.prototype[symbolThen]) { - patchThen(NewNativePromise); - } - api.setNativePromise(NewNativePromise); - } - }; - Object.defineProperty(global, 'Promise', desc); - } - global['Promise'] = ZoneAwarePromise; - var symbolThenPatched = __symbol__('thenPatched'); - function patchThen(Ctor) { - var proto = Ctor.prototype; - var originalThen = proto.then; - // Keep a reference to the original method. - proto[symbolThen] = originalThen; - // check Ctor.prototype.then propertyDescritor is writable or not - // in meteor env, writable is false, we have to make it to be true. - var prop = Object.getOwnPropertyDescriptor(Ctor.prototype, 'then'); - if (prop && prop.writable === false && prop.configurable) { - Object.defineProperty(Ctor.prototype, 'then', { writable: true }); - } - Ctor.prototype.then = function (onResolve, onReject) { - var _this = this; - var wrapped = new ZoneAwarePromise(function (resolve, reject) { - originalThen.call(_this, resolve, reject); - }); - return wrapped.then(onResolve, onReject); - }; - Ctor[symbolThenPatched] = true; - } - function zoneify(fn) { - return function () { - var resultPromise = fn.apply(this, arguments); - if (resultPromise instanceof ZoneAwarePromise) { - return resultPromise; - } - var ctor = resultPromise.constructor; - if (!ctor[symbolThenPatched]) { - patchThen(ctor); - } - return resultPromise; - }; - } - if (NativePromise) { - patchThen(NativePromise); - var fetch_1 = global['fetch']; - if (typeof fetch_1 == FUNCTION) { - global['fetch'] = zoneify(fetch_1); - } - } - // This is not part of public API, but it is useful for tests, so we expose it. - Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; - return ZoneAwarePromise; -}); - /** * @license * Copyright Google Inc. All Rights Reserved. @@ -458,8 +27,8 @@ var IGNORE_FRAMES = {}; var creationTrace = '__creationTrace__'; var ERROR_TAG = 'STACKTRACE TRACKING'; var SEP_TAG = '__SEP_TAG__'; -var sepTemplate = SEP_TAG + '@[native]'; -var LongStackTrace = /** @class */ (function () { +var sepTemplate = ''; +var LongStackTrace = (function () { function LongStackTrace() { this.error = getStacktrace(); this.timestamp = new Date(); @@ -498,7 +67,7 @@ function addErrorStack(lines, error) { } } function renderLongStackTrace(frames, stack) { - var longTrace = [stack ? stack.trim() : '']; + var longTrace = [stack.trim()]; if (frames) { var timestamp = new Date().getTime(); for (var i = 0; i < frames.length; i++) { @@ -522,44 +91,33 @@ Zone['longStackTraceZoneSpec'] = { if (!error) { return undefined; } - var trace = error[Zone.__symbol__('currentTaskTrace')]; + var task = error[Zone.__symbol__('currentTask')]; + var trace = task && task.data && task.data[creationTrace]; if (!trace) { return error.stack; } return renderLongStackTrace(trace, error.stack); }, onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - if (Error.stackTraceLimit > 0) { - // if Error.stackTraceLimit is 0, means stack trace - // is disabled, so we don't need to generate long stack trace - // this will improve performance in some test(some test will - // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 - var currentTask = Zone.currentTask; - var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; - trace = [new LongStackTrace()].concat(trace); - if (trace.length > this.longStackTraceLimit) { - trace.length = this.longStackTraceLimit; - } - if (!task.data) - task.data = {}; - task.data[creationTrace] = trace; - } + var currentTask = Zone.currentTask; + var trace = currentTask && currentTask.data && currentTask.data[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) + task.data = {}; + task.data[creationTrace] = trace; return parentZoneDelegate.scheduleTask(targetZone, task); }, onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) { - if (Error.stackTraceLimit > 0) { - // if Error.stackTraceLimit is 0, means stack trace - // is disabled, so we don't need to generate long stack trace - // this will improve performance in some test(some test will - // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 - var parentTask = Zone.currentTask || error.task; - if (error instanceof Error && parentTask) { - var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); - try { - error.stack = error.longStack = longStack; - } - catch (err) { - } + var parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + var longStack = renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = error.longStack = longStack; + } + catch (err) { } } return parentZoneDelegate.handleError(targetZone, error); @@ -572,32 +130,27 @@ function captureStackTraces(stackTraces, count) { } } function computeIgnoreFrames() { - if (Error.stackTraceLimit <= 0) { - return; - } var frames = []; captureStackTraces(frames, 2); var frames1 = frames[0]; var frames2 = frames[1]; - for (var i = 0; i < frames1.length; i++) { - var frame1 = frames1[i]; - if (frame1.indexOf(ERROR_TAG) == -1) { - var match = frame1.match(/^\s*at\s+/); - if (match) { - sepTemplate = match[0] + SEP_TAG + ' (http://localhost)'; - break; - } - } - } for (var i = 0; i < frames1.length; i++) { var frame1 = frames1[i]; var frame2 = frames2[i]; + if (!sepTemplate && frame1.indexOf(ERROR_TAG) == -1) { + sepTemplate = frame1.replace(/^(\s*(at)?\s*)([\w\/\<]+)/, '$1' + SEP_TAG); + } if (frame1 === frame2) { IGNORE_FRAMES[frame1] = true; } else { break; } + console.log('>>>>>>', sepTemplate, frame1); + } + if (!sepTemplate) { + // If we could not find it default to this text. + sepTemplate = SEP_TAG + '@[native code]'; } } computeIgnoreFrames(); @@ -609,7 +162,7 @@ computeIgnoreFrames(); * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var ProxyZoneSpec = /** @class */ (function () { +var ProxyZoneSpec = (function () { function ProxyZoneSpec(defaultSpecDelegate) { if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } this.defaultSpecDelegate = defaultSpecDelegate; @@ -723,7 +276,7 @@ Zone['ProxyZoneSpec'] = ProxyZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var SyncTestZoneSpec = /** @class */ (function () { +var SyncTestZoneSpec = (function () { function SyncTestZoneSpec(namePrefix) { this.runZone = Zone.current; this.name = 'syncTestZone for ' + namePrefix; @@ -752,7 +305,7 @@ Zone['SyncTestZoneSpec'] = SyncTestZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var AsyncTestZoneSpec = /** @class */ (function () { +var AsyncTestZoneSpec = (function () { function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { this._pendingMicroTasks = false; this._pendingMacroTasks = false; @@ -820,7 +373,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * found in the LICENSE file at https://angular.io/license */ (function (global) { - var Scheduler = /** @class */ (function () { + var Scheduler = (function () { function Scheduler() { // Next scheduler id. this.nextId = 0; @@ -829,23 +382,13 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; // Current simulated time in millis. this._currentTime = 0; } - Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + Scheduler.prototype.scheduleFunction = function (cb, delay, args, id) { if (args === void 0) { args = []; } - if (isPeriodic === void 0) { isPeriodic = false; } - if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } if (id === void 0) { id = -1; } var currentId = id < 0 ? this.nextId++ : id; var endTime = this._currentTime + delay; // Insert so that scheduler queue remains sorted by end time. - var newEntry = { - endTime: endTime, - id: currentId, - func: cb, - args: args, - delay: delay, - isPeriodic: isPeriodic, - isRequestAnimationFrame: isRequestAnimationFrame - }; + var newEntry = { endTime: endTime, id: currentId, func: cb, args: args, delay: delay }; var i = 0; for (; i < this._schedulerQueue.length; i++) { var currentEntry = this._schedulerQueue[i]; @@ -864,14 +407,9 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } } }; - Scheduler.prototype.tick = function (millis, doTick) { + Scheduler.prototype.tick = function (millis) { if (millis === void 0) { millis = 0; } var finalTime = this._currentTime + millis; - var lastCurrentTime = 0; - if (this._schedulerQueue.length === 0 && doTick) { - doTick(millis); - return; - } while (this._schedulerQueue.length > 0) { var current = this._schedulerQueue[0]; if (finalTime < current.endTime) { @@ -881,11 +419,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; else { // Time to run scheduled function. Remove it from the head of queue. var current_1 = this._schedulerQueue.shift(); - lastCurrentTime = this._currentTime; this._currentTime = current_1.endTime; - if (doTick) { - doTick(this._currentTime - lastCurrentTime); - } var retval = current_1.func.apply(global, current_1.args); if (!retval) { // Uncaught exception in the current scheduled function. Stop processing the queue. @@ -895,65 +429,10 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; } this._currentTime = finalTime; }; - Scheduler.prototype.flush = function (limit, flushPeriodic, doTick) { - if (limit === void 0) { limit = 20; } - if (flushPeriodic === void 0) { flushPeriodic = false; } - if (flushPeriodic) { - return this.flushPeriodic(doTick); - } - else { - return this.flushNonPeriodic(limit, doTick); - } - }; - Scheduler.prototype.flushPeriodic = function (doTick) { - if (this._schedulerQueue.length === 0) { - return 0; - } - // Find the last task currently queued in the scheduler queue and tick - // till that time. - var startTime = this._currentTime; - var lastTask = this._schedulerQueue[this._schedulerQueue.length - 1]; - this.tick(lastTask.endTime - startTime, doTick); - return this._currentTime - startTime; - }; - Scheduler.prototype.flushNonPeriodic = function (limit, doTick) { - var startTime = this._currentTime; - var lastCurrentTime = 0; - var count = 0; - while (this._schedulerQueue.length > 0) { - count++; - if (count > limit) { - throw new Error('flush failed after reaching the limit of ' + limit + - ' tasks. Does your code use a polling timeout?'); - } - // flush only non-periodic timers. - // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. - if (this._schedulerQueue.filter(function (task) { return !task.isPeriodic && !task.isRequestAnimationFrame; }) - .length === 0) { - break; - } - var current = this._schedulerQueue.shift(); - lastCurrentTime = this._currentTime; - this._currentTime = current.endTime; - if (doTick) { - // Update any secondary schedulers like Jasmine mock Date. - doTick(this._currentTime - lastCurrentTime); - } - var retval = current.func.apply(global, current.args); - if (!retval) { - // Uncaught exception in the current scheduled function. Stop processing the queue. - break; - } - } - return this._currentTime - startTime; - }; return Scheduler; }()); - var FakeAsyncTestZoneSpec = /** @class */ (function () { - function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { - if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } - this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; - this.macroTaskOptions = macroTaskOptions; + var FakeAsyncTestZoneSpec = (function () { + function FakeAsyncTestZoneSpec(namePrefix) { this._scheduler = new Scheduler(); this._microtasks = []; this._lastError = null; @@ -962,11 +441,6 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this.pendingTimers = []; this.properties = { 'FakeAsyncTestZoneSpec': this }; this.name = 'fakeAsyncTestZone for ' + namePrefix; - // in case user can't access the construction of FakyAsyncTestSpec - // user can also define macroTaskOptions by define a global variable. - if (!this.macroTaskOptions) { - this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')]; - } } FakeAsyncTestZoneSpec.assertInZone = function () { if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { @@ -1014,7 +488,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; return function () { // Requeue the timer callback if it's not been canceled. if (_this.pendingPeriodicTimers.indexOf(id) !== -1) { - _this._scheduler.scheduleFunction(fn, interval, args, true, false, id); + _this._scheduler.scheduleFunction(fn, interval, args, id); } }; }; @@ -1024,15 +498,12 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec._removeTimer(_this.pendingPeriodicTimers, id); }; }; - FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args, isTimer) { - if (isTimer === void 0) { isTimer = true; } + FakeAsyncTestZoneSpec.prototype._setTimeout = function (fn, delay, args) { var removeTimerFn = this._dequeueTimer(this._scheduler.nextId); // Queue the callback and dequeue the timer on success and error. var cb = this._fnAndFlush(fn, { onSuccess: removeTimerFn, onError: removeTimerFn }); - var id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer); - if (isTimer) { - this.pendingTimers.push(id); - } + var id = this._scheduler.scheduleFunction(cb, delay, args); + this.pendingTimers.push(id); return id; }; FakeAsyncTestZoneSpec.prototype._clearTimeout = function (id) { @@ -1050,7 +521,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; // Use the callback created above to requeue on success. completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); // Queue the callback and dequeue the periodic timer only on error. - this._scheduler.scheduleFunction(cb, interval, args, true); + this._scheduler.scheduleFunction(cb, interval, args); this.pendingPeriodicTimers.push(id); return id; }; @@ -1064,11 +535,11 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._lastError = null; throw error; }; - FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + FakeAsyncTestZoneSpec.prototype.tick = function (millis) { if (millis === void 0) { millis = 0; } FakeAsyncTestZoneSpec.assertInZone(); this.flushMicrotasks(); - this._scheduler.tick(millis, doTick); + this._scheduler.tick(millis); if (this._lastError !== null) { this._resetLastErrorAndThrow(); } @@ -1084,38 +555,14 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; }; while (this._microtasks.length > 0) { var microtask = this._microtasks.shift(); - microtask.func.apply(microtask.target, microtask.args); + microtask(); } flushErrors(); }; - FakeAsyncTestZoneSpec.prototype.flush = function (limit, flushPeriodic, doTick) { - FakeAsyncTestZoneSpec.assertInZone(); - this.flushMicrotasks(); - var elapsed = this._scheduler.flush(limit, flushPeriodic, doTick); - if (this._lastError !== null) { - this._resetLastErrorAndThrow(); - } - return elapsed; - }; FakeAsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { switch (task.type) { case 'microTask': - var args = task.data && task.data.args; - // should pass additional arguments to callback if have any - // currently we know process.nextTick will have such additional - // arguments - var addtionalArgs = void 0; - if (args) { - var callbackIndex = task.data.callbackIndex; - if (typeof args.length === 'number' && args.length > callbackIndex + 1) { - addtionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); - } - } - this._microtasks.push({ - func: task.invoke, - args: addtionalArgs, - target: task.data && task.data.target - }); + this._microtasks.push(task.invoke); break; case 'macroTask': switch (task.source) { @@ -1128,35 +575,9 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; this._setInterval(task.invoke, task.data['delay'], task.data['args']); break; case 'XMLHttpRequest.send': - throw new Error('Cannot make XHRs from within a fake async test. Request URL: ' + - task.data['url']); - case 'requestAnimationFrame': - case 'webkitRequestAnimationFrame': - case 'mozRequestAnimationFrame': - // Simulate a requestAnimationFrame by using a setTimeout with 16 ms. - // (60 frames per second) - task.data['handleId'] = this._setTimeout(task.invoke, 16, task.data['args'], this.trackPendingRequestAnimationFrame); - break; + throw new Error('Cannot make XHRs from within a fake async test.'); default: - // user can define which macroTask they want to support by passing - // macroTaskOptions - var macroTaskOption = this.findMacroTaskOption(task); - if (macroTaskOption) { - var args_1 = task.data && task.data['args']; - var delay = args_1 && args_1.length > 1 ? args_1[1] : 0; - var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1; - if (!!macroTaskOption.isPeriodic) { - // periodic macroTask, use setInterval to simulate - task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs); - task.data.isPeriodic = true; - } - else { - // not periodic, use setTimout to simulate - task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); - } - break; - } - throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + task = delegate.scheduleTask(target, task); } break; case 'eventTask': @@ -1168,36 +589,13 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; FakeAsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { switch (task.source) { case 'setTimeout': - case 'requestAnimationFrame': - case 'webkitRequestAnimationFrame': - case 'mozRequestAnimationFrame': return this._clearTimeout(task.data['handleId']); case 'setInterval': return this._clearInterval(task.data['handleId']); default: - // user can define which macroTask they want to support by passing - // macroTaskOptions - var macroTaskOption = this.findMacroTaskOption(task); - if (macroTaskOption) { - var handleId = task.data['handleId']; - return macroTaskOption.isPeriodic ? this._clearInterval(handleId) : - this._clearTimeout(handleId); - } return delegate.cancelTask(target, task); } }; - FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) { - if (!this.macroTaskOptions) { - return null; - } - for (var i = 0; i < this.macroTaskOptions.length; i++) { - var macroTaskOption = this.macroTaskOptions[i]; - if (macroTaskOption.source === task.source) { - return macroTaskOption; - } - } - return null; - }; FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { this._lastError = error; return false; // Don't propagate error to parent zone. @@ -1222,7 +620,7 @@ Zone['AsyncTestZoneSpec'] = AsyncTestZoneSpec; * This is useful in tests. For example to see which tasks are preventing a test from completing * or an automated way of releasing all of the event listeners at the end of the test. */ -var TaskTrackingZoneSpec = /** @class */ (function () { +var TaskTrackingZoneSpec = (function () { function TaskTrackingZoneSpec() { this.name = 'TaskTrackingZone'; this.microTasks = []; @@ -1290,10 +688,6 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -/** - * @fileoverview - * @suppress {missingRequire} - */ (function (global) { // Detect and setup WTF. var wtfTrace = null; @@ -1309,7 +703,7 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; } return false; })(); - var WtfZoneSpec = /** @class */ (function () { + var WtfZoneSpec = (function () { function WtfZoneSpec() { this.name = 'WTF'; } @@ -1360,13 +754,14 @@ Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec; instance(zonePathName(targetZone), shallowObj(task.data, 2)); return retValue; }; - WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); - WtfZoneSpec.scheduleInstance = {}; - WtfZoneSpec.cancelInstance = {}; - WtfZoneSpec.invokeScope = {}; - WtfZoneSpec.invokeTaskScope = {}; + return WtfZoneSpec; }()); + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; function shallowObj(obj, depth) { if (!depth) return null;