diff --git a/.gitignore b/.gitignore index 31a96cbc5..e64188546 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,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/e2e/config/appium.capabilities.json b/e2e/config/appium.capabilities.json index befcaf973..4b002b8bc 100644 --- a/e2e/config/appium.capabilities.json +++ b/e2e/config/appium.capabilities.json @@ -35,6 +35,16 @@ "noReset": false, "fullReset": false }, + "android22": { + "platformName": "Android", + "platformVersion": "5.0", + "deviceName": "Emulator-Api22-Default", + "avd": "Emulator-Api22-Default", + "lt": 60000, + "newCommandTimeout": 720, + "noReset": false, + "fullReset": false + }, "android23": { "platformName": "Android", "platformVersion": "6.0", @@ -114,4 +124,4 @@ "noReset": true, "fullReset": false } -} +} \ No newline at end of file diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index a7d337fde..ff068f37f 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -52,7 +52,8 @@ "@angular/http": "~6.0.0-rc.0", "@angular/platform-browser": "~6.0.0-rc.0", "@angular/router": "~6.0.0-rc.0", - "rxjs": "~6.0.0-rc.1", + "rxjs": "~6.0.0-rc.1 || >=6.1.0 || >=6.0.0", + "rxjs-compat": "~6.0.0-rc.1 || >=6.1.0 || >=6.0.0", "tns-core-modules": "^4.0.0 || >4.0.0- || >4.1.0-", "zone.js": "^0.8.4", "typescript": "~2.7.2" @@ -70,6 +71,7 @@ "@angular/router": "~6.0.0-rc.0", "codelyzer": "^4.0.0", "rxjs": "~6.0.0-rc.1", + "rxjs-compat": "~6.0.0-rc.1", "tns-core-modules": "next", "tslint": "^5.5.0", "typescript": "~2.7.2", diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index 91ce4df2d..7b0c04476 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -67,7 +67,10 @@ export class NativeScriptSanitizer extends Sanitizer { } // Add a fake polyfill for the document object -(global).document = (global).document || {}; +(global).document = (global).document || { + getElementById: () => { return undefined; } +}; + const doc = (global).document; doc.body = Object.assign(doc.body || {}, { isOverride: true, diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 35d379a6c..cad922ac2 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 { profile } from "tns-core-modules/profiling"; @@ -109,6 +109,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..3cc5b8f8f --- /dev/null +++ b/nativescript-angular/testing/src/util.ts @@ -0,0 +1,168 @@ + +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 nsTestBedInit() { + 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(nsTestBedBeforeEach([MyComponent,MyFailComponent])); + * ``` + * + * **NOTE*** Remember to pair with {@see nsTestBedAfterEach} + * + * @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 nsTestBedBeforeEach( + 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 nsTestBedAfterEach(resetEnv = true, resetFn = nsTestBedInit) { + return () => { + if (activeTestFixtures.length === 0) { + throw new Error( + `There are no more declared fixtures.` + + `Did you call "nsTestBedBeforeEach" and "nsTestBedAfterEach" an equal number of times?` + ); + } + const root = testingRootView() as LayoutBase; + const fixtures = activeTestFixtures.pop(); + fixtures.forEach((fixture) => { + const fixtureView = fixture.nativeElement; + if (fixtureView.parent === root) { + root.removeChild(fixtureView); + } + 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 nsTestBedRender(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( + "nsTestBedRender called without nsTestBedBeforeEach/nsTestBedAfter 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..98bac8f12 --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.jasmine.js @@ -0,0 +1,1452 @@ +/** +* @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 + */ +/** + * @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 = /** @class */ (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 trace = error[Zone.__symbol__('currentTaskTrace')]; + 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 = /** @class */ (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.lastTaskState = null; + this.isNeedToTriggerHasTask = false; + this.tasks = []; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!ProxyZoneSpec.isLoaded()) { + throw new Error("Expected to be running in 'ProxyZone', but it was not found."); + } + return ProxyZoneSpec.get(); + }; + ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { + var _this = this; + var isNewDelegate = this._delegateSpec !== delegateSpec; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + // if set a new delegateSpec, shoulde check whether need to + // trigger hasTask or not + if (isNewDelegate && this.lastTaskState && + (this.lastTaskState.macroTask || this.lastTaskState.microTask)) { + this.isNeedToTriggerHasTask = true; + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + var delegateSpec = this.getDelegate(); + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.tryTriggerHasTask = function (parentZoneDelegate, currentZone, targetZone) { + if (this.isNeedToTriggerHasTask && this.lastTaskState) { + // last delegateSpec has microTask or macroTask + // should call onHasTask in current delegateSpec + this.isNeedToTriggerHasTask = false; + this.onHasTask(parentZoneDelegate, currentZone, targetZone, this.lastTaskState); + } + }; + ProxyZoneSpec.prototype.removeFromTasks = function (task) { + if (!this.tasks) { + return; + } + for (var i = 0; i < this.tasks.length; i++) { + if (this.tasks[i] === task) { + this.tasks.splice(i, 1); + return; + } + } + }; + ProxyZoneSpec.prototype.getAndClearPendingTasksInfo = function () { + if (this.tasks.length === 0) { + return ''; + } + var taskInfo = this.tasks.map(function (task) { + var dataInfo = task.data && + Object.keys(task.data) + .map(function (key) { + return key + ':' + task.data[key]; + }) + .join(','); + return "type: " + task.type + ", source: " + task.source + ", args: {" + dataInfo + "}"; + }); + var pendingTasksInfo = '--Pendng async tasks are: [' + taskInfo + ']'; + // clear tasks + this.tasks = []; + return pendingTasksInfo; + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + 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) { + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + 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 (task.type !== 'eventTask') { + this.tasks.push(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 (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onInvokeTask) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + this.lastTaskState = 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 = /** @class */ (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 _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; +var AsyncTestZoneSpec = /** @class */ (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this.finishCallback = finishCallback; + this.failCallback = failCallback; + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this._isSync = false; + this.runZone = Zone.current; + this.unresolvedChainedPromiseCount = 0; + this.supportWaitUnresolvedChainedPromise = false; + this.name = 'asyncTestZone for ' + namePrefix; + this.properties = { 'AsyncTestZoneSpec': this }; + this.supportWaitUnresolvedChainedPromise = + _global[Zone.__symbol__('supportWaitUnResolvedChainedPromise')] === true; + } + AsyncTestZoneSpec.prototype.isUnresolvedChainedPromisePending = function () { + return this.unresolvedChainedPromiseCount > 0; + }; + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks || + (this.supportWaitUnresolvedChainedPromise && this.isUnresolvedChainedPromisePending()))) { + // We do this because we would like to catch unhandled rejected promises. + this.runZone.run(function () { + setTimeout(function () { + if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { + _this.finishCallback(); + } + }, 0); + }); + } + }; + AsyncTestZoneSpec.prototype.patchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var patchPromiseForTest = Promise[Zone.__symbol__('patchPromiseForTest')]; + if (patchPromiseForTest) { + patchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.unPatchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var unPatchPromiseForTest = Promise[Zone.__symbol__('unPatchPromiseForTest')]; + if (unPatchPromiseForTest) { + unPatchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + if (task.type === 'microTask' && task.data && task.data instanceof Promise) { + // check whether the promise is a chained promise + if (task.data[AsyncTestZoneSpec.symbolParentUnresolved] === true) { + // chained promise is being scheduled + this.unresolvedChainedPromiseCount--; + } + } + return delegate.scheduleTask(target, task); + }; + AsyncTestZoneSpec.prototype.onInvokeTask = function (delegate, current, target, task, applyThis, applyArgs) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.invokeTask(target, task, applyThis, applyArgs); + }; + AsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.cancelTask(target, task); + }; + // Note - we need to use onInvoke at the moment to call finish when a test is + // fully synchronous. TODO(juliemr): remove this when the logic for + // onHasTask changes and it calls whenever the task queues are dirty. + // updated by(JiaLiPassion), only call finish callback when no task + // was scheduled/invoked/canceled. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + this._isSync = true; + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + var afterTaskCounts = parentZoneDelegate._taskCounts; + if (this._isSync) { + this._finishCallbackIfDone(); + } + } + }; + AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + // Let the parent try to handle the error. + var result = parentZoneDelegate.handleError(targetZone, error); + if (result) { + this.failCallback(error); + this._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(); + } + }; + AsyncTestZoneSpec.symbolParentUnresolved = Zone.__symbol__('parentUnresolved'); + 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 + */ +var __read = (undefined && undefined.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spread = (undefined && undefined.__spread) || function () { + for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); + return ar; +}; +(function (global) { + var OriginalDate = global.Date; + var FakeDate = /** @class */ (function () { + function FakeDate() { + if (arguments.length === 0) { + var d = new OriginalDate(); + d.setTime(FakeDate.now()); + return d; + } + else { + var args = Array.prototype.slice.call(arguments); + return new (OriginalDate.bind.apply(OriginalDate, __spread([void 0], args)))(); + } + } + FakeDate.now = function () { + var fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncTestZoneSpec) { + return fakeAsyncTestZoneSpec.getCurrentRealTime() + fakeAsyncTestZoneSpec.getCurrentTime(); + } + return OriginalDate.now.apply(this, arguments); + }; + return FakeDate; + }()); + FakeDate.UTC = OriginalDate.UTC; + FakeDate.parse = OriginalDate.parse; + // keep a reference for zone patched timer function + var timers = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval + }; + var Scheduler = /** @class */ (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 1; + // Scheduler queue with the tuple of end time and callback function - sorted by end time. + this._schedulerQueue = []; + // Current simulated time in millis. + this._currentTime = 0; + // Current real time in millis. + this._currentRealTime = OriginalDate.now(); + } + Scheduler.prototype.getCurrentTime = function () { + return this._currentTime; + }; + Scheduler.prototype.getCurrentRealTime = function () { + return this._currentRealTime; + }; + Scheduler.prototype.setCurrentRealTime = function (realTime) { + this._currentRealTime = realTime; + }; + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } + if (id === void 0) { id = -1; } + var currentId = id < 0 ? this.nextId++ : id; + var endTime = this._currentTime + delay; + // Insert so that scheduler queue remains sorted by end time. + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + 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 = /** @class */ (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this.macroTaskOptions = macroTaskOptions; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.patchDateLocked = false; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + // in case user can't access the construction of FakeAsyncTestSpec + // user can also define macroTaskOptions by define a global variable. + if (!this.macroTaskOptions) { + this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')]; + } + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + 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, args) { + var id = this._scheduler.nextId; + var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; + var cb = this._fnAndFlush(fn, completers); + // Use the callback created above to requeue on success. + completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); + // Queue the callback and dequeue the periodic timer only on error. + this._scheduler.scheduleFunction(cb, interval, args, 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.getCurrentTime = function () { + return this._scheduler.getCurrentTime(); + }; + FakeAsyncTestZoneSpec.prototype.getCurrentRealTime = function () { + return this._scheduler.getCurrentRealTime(); + }; + FakeAsyncTestZoneSpec.prototype.setCurrentRealTime = function (realTime) { + this._scheduler.setCurrentRealTime(realTime); + }; + FakeAsyncTestZoneSpec.patchDate = function () { + if (global['Date'] === FakeDate) { + // already patched + return; + } + global['Date'] = FakeDate; + FakeDate.prototype = OriginalDate.prototype; + // try check and reset timers + // because jasmine.clock().install() may + // have replaced the global timer + FakeAsyncTestZoneSpec.checkTimerPatch(); + }; + FakeAsyncTestZoneSpec.resetDate = function () { + if (global['Date'] === FakeDate) { + global['Date'] = OriginalDate; + } + }; + FakeAsyncTestZoneSpec.checkTimerPatch = function () { + if (global.setTimeout !== timers.setTimeout) { + global.setTimeout = timers.setTimeout; + global.clearTimeout = timers.clearTimeout; + } + if (global.setInterval !== timers.setInterval) { + global.setInterval = timers.setInterval; + global.clearInterval = timers.clearInterval; + } + }; + FakeAsyncTestZoneSpec.prototype.lockDatePatch = function () { + this.patchDateLocked = true; + FakeAsyncTestZoneSpec.patchDate(); + }; + FakeAsyncTestZoneSpec.prototype.unlockDatePatch = function () { + this.patchDateLocked = false; + FakeAsyncTestZoneSpec.resetDate(); + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, 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 additionalArgs = void 0; + if (args) { + var callbackIndex = task.data.cbIdx; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + additionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: additionalArgs, + target: task.data && task.data.target + }); + break; + case 'macroTask': + switch (task.source) { + case 'setTimeout': + task.data['handleId'] = this._setTimeout(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + break; + case 'setImmediate': + task.data['handleId'] = this._setTimeout(task.invoke, 0, Array.prototype.slice.call(task.data['args'], 1)); + break; + case 'setInterval': + task.data['handleId'] = this._setInterval(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + 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: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var args_1 = task.data && task.data['args']; + var delay = args_1 && args_1.length > 1 ? args_1[1] : 0; + var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1; + if (!!macroTaskOption.isPeriodic) { + // periodic macroTask, use setInterval to simulate + task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs); + task.data.isPeriodic = true; + } + else { + // not periodic, use setTimeout to simulate + task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); + } + break; + } + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + 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: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var handleId = task.data['handleId']; + return macroTaskOption.isPeriodic ? this._clearInterval(handleId) : + this._clearTimeout(handleId); + } + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onInvoke = function (delegate, current, target, callback, applyThis, applyArgs, source) { + try { + FakeAsyncTestZoneSpec.patchDate(); + return delegate.invoke(target, callback, applyThis, applyArgs, source); + } + finally { + if (!this.patchDateLocked) { + FakeAsyncTestZoneSpec.resetDate(); + } + } + }; + FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) { + if (!this.macroTaskOptions) { + return null; + } + for (var i = 0; i < this.macroTaskOptions.length; i++) { + var macroTaskOption = this.macroTaskOptions[i]; + if (macroTaskOption.source === task.source) { + return macroTaskOption; + } + } + return null; + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + 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 = /** @class */ (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 = /** @class */ (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; + }; + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; + return WtfZoneSpec; + }()); + 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 + */ +(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 __()); + }; + var _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; + // Patch jasmine's describe/it/beforeEach/afterEach functions so test code always runs + // in a testZone (ProxyZone). (See: angular/zone.js#91 & angular/angular#10503) + if (!Zone) + 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')); + var symbol = Zone.__symbol__; + // whether patch jasmine clock when in fakeAsync + var enableClockPatch = _global[symbol('fakeAsyncPatchLock')] === true; + // Monkey patch all of the jasmine DSL so that each function runs in appropriate zone. + var jasmineEnv = jasmine.getEnv(); + ['describe', 'xdescribe', 'fdescribe'].forEach(function (methodName) { + 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[symbol(methodName)] = originalJasmineFn; + 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[symbol(methodName)] = originalJasmineFn; + jasmineEnv[methodName] = function (specDefinitions, timeout) { + arguments[0] = wrapTestInZone(specDefinitions); + return originalJasmineFn.apply(this, arguments); + }; + }); + // need to patch jasmine.clock().mockDate and jasmine.clock().tick() so + // they can work properly in FakeAsyncTest + var originalClockFn = (jasmine[symbol('clock')] = jasmine['clock']); + jasmine['clock'] = function () { + var clock = originalClockFn.apply(this, arguments); + if (!clock[symbol('patched')]) { + clock[symbol('patched')] = symbol('patched'); + var originalTick_1 = (clock[symbol('tick')] = clock.tick); + clock.tick = function () { + var fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + return fakeAsyncZoneSpec.tick.apply(fakeAsyncZoneSpec, arguments); + } + return originalTick_1.apply(this, arguments); + }; + var originalMockDate_1 = (clock[symbol('mockDate')] = clock.mockDate); + clock.mockDate = function () { + var fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncZoneSpec) { + var dateTime = arguments.length > 0 ? arguments[0] : new Date(); + return fakeAsyncZoneSpec.setCurrentRealTime.apply(fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] : + arguments); + } + return originalMockDate_1.apply(this, arguments); + }; + // for auto go into fakeAsync feature, we need the flag to enable it + if (enableClockPatch) { + ['install', 'uninstall'].forEach(function (methodName) { + var originalClockFn = (clock[symbol(methodName)] = clock[methodName]); + clock[methodName] = function () { + var FakeAsyncTestZoneSpec = Zone['FakeAsyncTestZoneSpec']; + if (FakeAsyncTestZoneSpec) { + jasmine[symbol('clockInstalled')] = 'install' === methodName; + return; + } + return originalClockFn.apply(this, arguments); + }; + }); + } + } + return clock; + }; + /** + * Gets a function wrapping the body of a Jasmine `describe` block to execute in a + * synchronous-only zone. + */ + function wrapDescribeInZone(describeBody) { + return function () { + return syncZone.run(describeBody, this, arguments); + }; + } + function runInTestZone(testBody, applyThis, queueRunner, done) { + var isClockInstalled = !!jasmine[symbol('clockInstalled')]; + var testProxyZoneSpec = queueRunner.testProxyZoneSpec; + var testProxyZone = queueRunner.testProxyZone; + if (isClockInstalled && enableClockPatch) { + // auto run a fakeAsync + var fakeAsyncModule = Zone[Zone.__symbol__('fakeAsyncTest')]; + if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') { + testBody = fakeAsyncModule.fakeAsync(testBody); + } + } + if (done) { + return testProxyZone.run(testBody, applyThis, [done]); + } + else { + return testProxyZone.run(testBody, applyThis); + } + } + /** + * Gets a function wrapping the body of a Jasmine `it/beforeEach/afterEach` block to + * execute in a ProxyZone zone. + * 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 runInTestZone(testBody, this, this.queueRunner, done); + } : function () { + return runInTestZone(testBody, this, this.queueRunner); + })); + } + var QueueRunner = jasmine.QueueRunner; + jasmine.QueueRunner = (function (_super) { + __extends(ZoneQueueRunner, _super); + function ZoneQueueRunner(attrs) { + var _this = this; + attrs.onComplete = (function (fn) { return function () { + // All functions are done, clear the test zone. + _this.testProxyZone = null; + _this.testProxyZoneSpec = null; + ambientZone.scheduleMicroTask('jasmine.onComplete', fn); + }; })(attrs.onComplete); + var nativeSetTimeout = _global['__zone_symbol__setTimeout']; + var nativeClearTimeout = _global['__zone_symbol__clearTimeout']; + if (nativeSetTimeout) { + // should run setTimeout inside jasmine outside of zone + attrs.timeout = { + setTimeout: nativeSetTimeout ? nativeSetTimeout : _global.setTimeout, + clearTimeout: nativeClearTimeout ? nativeClearTimeout : _global.clearTimeout + }; + } + // create a userContext to hold the queueRunner itself + // so we can access the testProxy in it/xit/beforeEach ... + if (jasmine.UserContext) { + if (!attrs.userContext) { + attrs.userContext = new jasmine.UserContext(); + } + attrs.userContext.queueRunner = this; + } + else { + if (!attrs.userContext) { + attrs.userContext = {}; + } + attrs.userContext.queueRunner = this; + } + // patch attrs.onException + var onException = attrs.onException; + attrs.onException = function (error) { + if (error && + error.message === + 'Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.') { + // jasmine timeout, we can make the error message more + // reasonable to tell what tasks are pending + var proxyZoneSpec = this && this.testProxyZoneSpec; + if (proxyZoneSpec) { + var pendingTasksInfo = proxyZoneSpec.getAndClearPendingTasksInfo(); + error.message += pendingTasksInfo; + } + } + if (onException) { + onException.call(this, error); + } + }; + _super.call(this, attrs); + } + ZoneQueueRunner.prototype.execute = function () { + var _this = this; + var zone = Zone.current; + var isChildOfAmbientZone = false; + while (zone) { + if (zone === ambientZone) { + isChildOfAmbientZone = true; + break; + } + zone = zone.parent; + } + if (!isChildOfAmbientZone) + throw new Error('Unexpected Zone: ' + Zone.current.name); + // This is the zone which will be used for running individual tests. + // It will be a proxy zone, so that the tests function can retroactively install + // different zones. + // Example: + // - In beforeEach() do childZone = Zone.current.fork(...); + // - In it() try to do fakeAsync(). The issue is that because the beforeEach forked the + // zone outside of fakeAsync it will be able to escape the fakeAsync rules. + // - Because ProxyZone is parent fo `childZone` fakeAsync can retroactively add + // fakeAsync behavior to the childZone. + this.testProxyZoneSpec = new ProxyZoneSpec(); + this.testProxyZone = ambientZone.fork(this.testProxyZoneSpec); + if (!Zone.currentTask) { + // if we are not running in a task then if someone would register a + // element.addEventListener and then calling element.click() the + // 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..95756327a 100644 --- a/nativescript-angular/zone-js/dist/zone-nativescript.js +++ b/nativescript-angular/zone-js/dist/zone-nativescript.js @@ -19,13 +19,19 @@ * found in the LICENSE file at https://angular.io/license */ var Zone$1 = (function (global) { + var FUNCTION = 'function'; + var performance = global['performance']; + function mark(name) { + performance && performance['mark'] && performance['mark'](name); + } + function performanceMeasure(name, label) { + performance && performance['measure'] && performance['measure'](name, label); + } + mark('Zone'); if (global['Zone']) { throw new Error('Zone already loaded.'); } - var NO_ZONE = { name: 'NO ZONE' }; - var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; - var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; - var Zone = (function () { + var Zone = /** @class */ (function () { function Zone(parent, zoneSpec) { this._properties = null; this._parent = parent; @@ -35,7 +41,7 @@ var Zone$1 = (function (global) { new ZoneDelegate(this, this._parent && this._parent._zoneDelegate, zoneSpec); } Zone.assertZonePatched = function () { - if (global.Promise !== ZoneAwarePromise) { + if (global['Promise'] !== patches['ZoneAwarePromise']) { throw new Error('Zone.js has detected that ZoneAwarePromise `(window|global).Promise` ' + 'has been overwritten.\n' + 'Most likely cause is that a Promise polyfill has been loaded ' + @@ -61,7 +67,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Object.defineProperty(Zone, "currentTask", { get: function () { return _currentTask; @@ -69,7 +74,17 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - + Zone.__load_patch = function (name, fn) { + if (patches.hasOwnProperty(name)) { + throw Error('Already loaded patch: ' + name); + } + else if (!global['__Zone_disable_' + name]) { + var perfName = 'Zone:' + name; + mark(perfName); + patches[name] = fn(global, Zone, _api); + performanceMeasure(perfName, perfName); + } + }; Object.defineProperty(Zone.prototype, "parent", { get: function () { return this._parent; @@ -77,7 +92,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Object.defineProperty(Zone.prototype, "name", { get: function () { return this._name; @@ -85,7 +99,6 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); - Zone.prototype.get = function (key) { var zone = this.getZoneWith(key); if (zone) @@ -107,7 +120,7 @@ var Zone$1 = (function (global) { return this._zoneDelegate.fork(this, zoneSpec); }; Zone.prototype.wrap = function (callback, source) { - if (typeof callback !== 'function') { + if (typeof callback !== FUNCTION) { throw new Error('Expecting function got: ' + callback); } var _callback = this._zoneDelegate.intercept(this, callback, source); @@ -120,7 +133,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = undefined; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); } @@ -132,7 +145,7 @@ var Zone$1 = (function (global) { if (applyThis === void 0) { applyThis = null; } if (applyArgs === void 0) { applyArgs = null; } if (source === void 0) { source = null; } - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { try { return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); @@ -148,15 +161,25 @@ var Zone$1 = (function (global) { } }; Zone.prototype.runTask = function (task, applyThis, applyArgs) { - if (task.zone != this) + if (task.zone != this) { throw new Error('A task can only be run in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); + } + // https://github.com/angular/zone.js/issues/778, sometimes eventTask + // will run in notScheduled(canceled) state, we should not try to + // run such kind of task but just return + // we have to define an variable here, if not + // typescript compiler will complain below + var isNotScheduled = task.state === notScheduled; + if (isNotScheduled && task.type === eventTask) { + return; + } var reEntryGuard = task.state != running; reEntryGuard && task._transitionTo(running, scheduled); task.runCount++; var previousTask = _currentTask; _currentTask = task; - _currentZoneFrame = new ZoneFrame(_currentZoneFrame, this); + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { if (task.type == macroTask && task.data && !task.data.isPeriodic) { task.cancelFn = null; @@ -262,9 +285,9 @@ var Zone$1 = (function (global) { zoneDelegates[i]._updateTaskCount(task.type, count); } }; + Zone.__symbol__ = __symbol__; return Zone; }()); - Zone.__symbol__ = __symbol__; var DELEGATE_ZS = { name: '', onHasTask: function (delegate, _, target, hasTaskState) { @@ -278,7 +301,7 @@ var Zone$1 = (function (global) { return delegate.cancelTask(target, task); } }; - var ZoneDelegate = (function () { + var ZoneDelegate = /** @class */ (function () { function ZoneDelegate(zone, parentDelegate, zoneSpec) { this._taskCounts = { 'microTask': 0, 'macroTask': 0, 'eventTask': 0 }; this.zone = zone; @@ -418,6 +441,7 @@ var Zone$1 = (function (global) { this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty); } catch (err) { + this.handleError(targetZone, err); } }; ZoneDelegate.prototype._updateTaskCount = function (type, count) { @@ -429,18 +453,17 @@ var Zone$1 = (function (global) { } if (prev == 0 || next == 0) { var isEmpty = { - microTask: counts.microTask > 0, - macroTask: counts.macroTask > 0, - eventTask: counts.eventTask > 0, + microTask: counts['microTask'] > 0, + macroTask: counts['macroTask'] > 0, + eventTask: counts['eventTask'] > 0, change: type }; - // TODO(misko): what should happen if it throws? this.hasTask(this.zone, isEmpty); } }; return ZoneDelegate; }()); - var ZoneTask = (function () { + var ZoneTask = /** @class */ (function () { function ZoneTask(type, source, callback, options, scheduleFn, cancelFn) { this._zone = null; this.runCount = 0; @@ -453,20 +476,32 @@ var Zone$1 = (function (global) { this.cancelFn = cancelFn; this.callback = callback; var self = this; - this.invoke = function () { - _numberOfNestedTaskFrames++; - try { - self.runCount++; - return self.zone.runTask(self, this, arguments); - } - finally { - if (_numberOfNestedTaskFrames == 1) { - drainMicroTaskQueue(); - } - _numberOfNestedTaskFrames--; - } - }; + // TODO: @JiaLiPassion options should have interface + if (type === eventTask && options && options.useG) { + this.invoke = ZoneTask.invokeTask; + } + else { + this.invoke = function () { + return ZoneTask.invokeTask.call(global, self, this, arguments); + }; + } } + ZoneTask.invokeTask = function (task, target, args) { + if (!task) { + task = this; + } + _numberOfNestedTaskFrames++; + try { + task.runCount++; + return task.zone.runTask(task, target, args); + } + finally { + if (_numberOfNestedTaskFrames == 1) { + drainMicroTaskQueue(); + } + _numberOfNestedTaskFrames--; + } + }; Object.defineProperty(ZoneTask.prototype, "zone", { get: function () { return this._zone; @@ -512,74 +547,41 @@ var Zone$1 = (function (global) { type: this.type, state: this.state, source: this.source, - data: this.data, zone: this.zone.name, - invoke: this.invoke, - scheduleFn: this.scheduleFn, - cancelFn: this.cancelFn, - runCount: this.runCount, - callback: this.callback + runCount: this.runCount }; }; return ZoneTask; }()); - var ZoneFrame = (function () { - function ZoneFrame(parent, zone) { - this.parent = parent; - this.zone = zone; - } - return ZoneFrame; - }()); - function __symbol__(name) { - return '__zone_symbol__' + name; - } - + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// MICROTASK QUEUE + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// var symbolSetTimeout = __symbol__('setTimeout'); var symbolPromise = __symbol__('Promise'); var symbolThen = __symbol__('then'); - var _currentZoneFrame = new ZoneFrame(null, new Zone(null, null)); - var _currentTask = null; var _microTaskQueue = []; var _isDrainingMicrotaskQueue = false; - var _uncaughtPromiseErrors = []; - var _numberOfNestedTaskFrames = 0; - function scheduleQueueDrain() { + var nativeMicroTaskQueuePromise; + function scheduleMicroTask(task) { // if we are not running in any task, and there has not been anything scheduled // we must bootstrap the initial task creation by manually scheduling the drain if (_numberOfNestedTaskFrames === 0 && _microTaskQueue.length === 0) { // We are not running in Task, so we need to kickstart the microtask queue. - if (global[symbolPromise]) { - global[symbolPromise].resolve(0)[symbolThen](drainMicroTaskQueue); + if (!nativeMicroTaskQueuePromise) { + if (global[symbolPromise]) { + nativeMicroTaskQueuePromise = global[symbolPromise].resolve(0); + } + } + if (nativeMicroTaskQueuePromise) { + nativeMicroTaskQueuePromise[symbolThen](drainMicroTaskQueue); } else { global[symbolSetTimeout](drainMicroTaskQueue, 0); } } - } - function scheduleMicroTask(task) { - scheduleQueueDrain(); - _microTaskQueue.push(task); - } - function consoleError(e) { - if (Zone[__symbol__('ignoreConsoleErrorUncaughtError')]) { - return; - } - var rejection = e && e.rejection; - if (rejection) { - console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); - } - console.error(e); - } - function handleUnhandledRejection(e) { - consoleError(e); - try { - var handler = Zone[__symbol__('unhandledPromiseRejectionHandler')]; - if (handler && typeof handler === 'function') { - handler.apply(this, [e]); - } - } - catch (err) { - } + task && _microTaskQueue.push(task); } function drainMicroTaskQueue() { if (!_isDrainingMicrotaskQueue) { @@ -593,30 +595,121 @@ var Zone$1 = (function (global) { task.zone.runTask(task, null, null); } catch (error) { - consoleError(error); + _api.onUnhandledError(error); } } } - while (_uncaughtPromiseErrors.length) { - var _loop_1 = function () { - var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); - try { - uncaughtPromiseError.zone.runGuarded(function () { - throw uncaughtPromiseError; - }); - } - catch (error) { - handleUnhandledRejection(error); - } - }; - while (_uncaughtPromiseErrors.length) { - _loop_1(); - } - } + _api.microtaskDrainDone(); _isDrainingMicrotaskQueue = false; } } Zone.drainMicroTaskQueue = drainMicroTaskQueue; + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// BOOTSTRAP + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + var NO_ZONE = { name: 'NO ZONE' }; + var notScheduled = 'notScheduled', scheduling = 'scheduling', scheduled = 'scheduled', running = 'running', canceling = 'canceling', unknown = 'unknown'; + var microTask = 'microTask', macroTask = 'macroTask', eventTask = 'eventTask'; + var patches = {}; + var _api = { + symbol: __symbol__, + currentZoneFrame: function () { return _currentZoneFrame; }, + onUnhandledError: noop, + microtaskDrainDone: noop, + scheduleMicroTask: scheduleMicroTask, + showUncaughtError: function () { return !Zone[__symbol__('ignoreConsoleErrorUncaughtError')]; }, + patchEventTarget: function () { return []; }, + patchOnProperties: noop, + patchMethod: function () { return noop; }, + bindArguments: function () { return null; }, + setNativePromise: function (NativePromise) { + // sometimes NativePromise.resolve static function + // is not ready yet, (such as core-js/es6.promise) + // so we need to check here. + if (NativePromise && typeof NativePromise.resolve === FUNCTION) { + nativeMicroTaskQueuePromise = NativePromise.resolve(0); + } + }, + }; + var _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + var _currentTask = null; + var _numberOfNestedTaskFrames = 0; + function noop() { } + function __symbol__(name) { + return '__zone_symbol__' + name; + } + performanceMeasure('Zone', 'Zone'); + return global['Zone'] = Zone; +})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); + +var __values = (undefined && undefined.__values) || function (o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +}; +Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { + var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + var ObjectDefineProperty = Object.defineProperty; + function readableObjectToString(obj) { + if (obj && obj.toString === Object.prototype.toString) { + var className = obj.constructor && obj.constructor.name; + return (className ? className : '') + ': ' + JSON.stringify(obj); + } + return obj ? obj.toString() : Object.prototype.toString.call(obj); + } + var __symbol__ = api.symbol; + var _uncaughtPromiseErrors = []; + var symbolPromise = __symbol__('Promise'); + var symbolThen = __symbol__('then'); + var creationTrace = '__creationTrace__'; + api.onUnhandledError = function (e) { + if (api.showUncaughtError()) { + var rejection = e && e.rejection; + if (rejection) { + console.error('Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', e.zone.name, '; Task:', e.task && e.task.source, '; Value:', rejection, rejection instanceof Error ? rejection.stack : undefined); + } + else { + console.error(e); + } + } + }; + api.microtaskDrainDone = function () { + while (_uncaughtPromiseErrors.length) { + var _loop_1 = function () { + var uncaughtPromiseError = _uncaughtPromiseErrors.shift(); + try { + uncaughtPromiseError.zone.runGuarded(function () { + throw uncaughtPromiseError; + }); + } + catch (error) { + handleUnhandledRejection(error); + } + }; + while (_uncaughtPromiseErrors.length) { + _loop_1(); + } + } + }; + var UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler'); + function handleUnhandledRejection(e) { + api.onUnhandledError(e); + try { + var handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; + if (handler && typeof handler === 'function') { + handler.call(this, e); + } + } + catch (err) { + } + } function isThenable(value) { return value && value.then; } @@ -628,6 +721,9 @@ var Zone$1 = (function (global) { } var symbolState = __symbol__('state'); var symbolValue = __symbol__('value'); + var symbolFinally = __symbol__('finally'); + var symbolParentPromiseValue = __symbol__('parentPromiseValue'); + var symbolParentPromiseState = __symbol__('parentPromiseState'); var source = 'Promise.then'; var UNRESOLVED = null; var RESOLVED = true; @@ -656,11 +752,13 @@ var Zone$1 = (function (global) { }; }; }; + var TYPE_ERROR = 'Promise resolved with itself'; + var CURRENT_TASK_TRACE_SYMBOL = __symbol__('currentTaskTrace'); // Promise Resolution function resolvePromise(promise, state, value) { var onceWrapper = once(); if (promise === value) { - throw new TypeError('Promise resolved with itself'); + throw new TypeError(TYPE_ERROR); } if (promise[symbolState] === UNRESOLVED) { // should only get value.then once based on promise spec. @@ -685,9 +783,7 @@ var Zone$1 = (function (global) { } else if (state !== REJECTED && typeof then === 'function') { try { - then.apply(value, [ - onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false)) - ]); + then.call(value, onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false))); } catch (err) { onceWrapper(function () { @@ -699,10 +795,25 @@ var Zone$1 = (function (global) { promise[symbolState] = state; var queue = promise[symbolValue]; promise[symbolValue] = value; + if (promise[symbolFinally] === symbolFinally) { + // the promise is generated by Promise.prototype.finally + if (state === RESOLVED) { + // the state is resolved, should ignore the value + // and use parent promise value + promise[symbolState] = promise[symbolParentPromiseState]; + promise[symbolValue] = promise[symbolParentPromiseValue]; + } + } // record task information in value when error occurs, so we can // do some additional work such as render longStackTrace if (state === REJECTED && value instanceof Error) { - value[__symbol__('currentTask')] = Zone.currentTask; + // check if longStackTraceZone is here + var trace = Zone.currentTask && Zone.currentTask.data && + Zone.currentTask.data[creationTrace]; + if (trace) { + // only keep the long stack trace into error when in longStackTraceZone + ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { configurable: true, enumerable: false, writable: true, value: trace }); + } } for (var i = 0; i < queue.length;) { scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); @@ -710,7 +821,8 @@ var Zone$1 = (function (global) { if (queue.length == 0 && state == REJECTED) { promise[symbolState] = REJECTED_NO_CATCH; try { - throw new Error('Uncaught (in promise): ' + value + + // try to print more readable error log + throw new Error('Uncaught (in promise): ' + readableObjectToString(value) + (value && value.stack ? '\n' + value.stack : '')); } catch (err) { @@ -720,7 +832,7 @@ var Zone$1 = (function (global) { error_1.zone = Zone.current; error_1.task = Zone.currentTask; _uncaughtPromiseErrors.push(error_1); - scheduleQueueDrain(); + api.scheduleMicroTask(); // to make sure that it is running } } } @@ -728,6 +840,7 @@ var Zone$1 = (function (global) { // Resolving an already resolved promise is a noop. return promise; } + var REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler'); function clearRejectedNoCatch(promise) { if (promise[symbolState] === REJECTED_NO_CATCH) { // if the promise is rejected no catch status @@ -736,9 +849,9 @@ var Zone$1 = (function (global) { // windows.rejectionhandled eventHandler or nodejs rejectionHandled // eventHandler try { - var handler = Zone[__symbol__('rejectionHandledHandler')]; + var handler = Zone[REJECTION_HANDLED_HANDLER]; if (handler && typeof handler === 'function') { - handler.apply(this, [{ rejection: promise[symbolValue], promise: promise }]); + handler.call(this, { rejection: promise[symbolValue], promise: promise }); } } catch (err) { @@ -753,19 +866,31 @@ var Zone$1 = (function (global) { } function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { clearRejectedNoCatch(promise); - var delegate = promise[symbolState] ? + var promiseState = promise[symbolState]; + var delegate = promiseState ? (typeof onFulfilled === 'function') ? onFulfilled : forwardResolution : (typeof onRejected === 'function') ? onRejected : forwardRejection; zone.scheduleMicroTask(source, function () { try { - resolvePromise(chainPromise, true, zone.run(delegate, undefined, [promise[symbolValue]])); + var parentPromiseValue = promise[symbolValue]; + var isFinallyPromise = chainPromise && symbolFinally === chainPromise[symbolFinally]; + if (isFinallyPromise) { + // if the promise is generated from finally call, keep parent promise's state and value + chainPromise[symbolParentPromiseValue] = parentPromiseValue; + chainPromise[symbolParentPromiseState] = promiseState; + } + // should not pass value to finally callback + var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]); + resolvePromise(chainPromise, true, value); } catch (error) { + // if error occurs, should always return this error resolvePromise(chainPromise, false, error); } - }); + }, chainPromise); } - var ZoneAwarePromise = (function () { + var ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }'; + var ZoneAwarePromise = /** @class */ (function () { function ZoneAwarePromise(executor) { var promise = this; if (!(promise instanceof ZoneAwarePromise)) { @@ -781,7 +906,7 @@ var Zone$1 = (function (global) { } } ZoneAwarePromise.toString = function () { - return 'function ZoneAwarePromise() { [native code] }'; + return ZONE_AWARE_PROMISE_TO_STRING; }; ZoneAwarePromise.resolve = function (value) { return resolvePromise(new this(null), RESOLVED, value); @@ -793,8 +918,8 @@ var Zone$1 = (function (global) { var resolve; var reject; var promise = new this(function (res, rej) { - _a = [res, rej], resolve = _a[0], reject = _a[1]; - var _a; + resolve = res; + reject = rej; }); function onResolve(value) { promise && (promise = null || resolve(value)); @@ -802,14 +927,24 @@ var Zone$1 = (function (global) { function onReject(error) { promise && (promise = null || reject(error)); } - for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { - var value = values_1[_i]; - if (!isThenable(value)) { - value = this.resolve(value); + try { + for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { + var value = values_1_1.value; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1); } - value.then(onResolve, onReject); + finally { if (e_1) throw e_1.error; } } return promise; + var e_1, _a; }; ZoneAwarePromise.all = function (values) { var resolve; @@ -820,23 +955,33 @@ var Zone$1 = (function (global) { }); var count = 0; var resolvedValues = []; - for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { - var value = values_2[_i]; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then((function (index) { return function (value) { - resolvedValues[index] = value; - count--; - if (!count) { - resolve(resolvedValues); + try { + for (var values_2 = __values(values), values_2_1 = values_2.next(); !values_2_1.done; values_2_1 = values_2.next()) { + var value = values_2_1.value; + if (!isThenable(value)) { + value = this.resolve(value); } - }; })(count), reject); - count++; + value.then((function (index) { return function (value) { + resolvedValues[index] = value; + count--; + if (!count) { + resolve(resolvedValues); + } + }; })(count), reject); + count++; + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (values_2_1 && !values_2_1.done && (_a = values_2.return)) _a.call(values_2); + } + finally { if (e_2) throw e_2.error; } } if (!count) resolve(resolvedValues); return promise; + var e_2, _a; }; ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) { var chainPromise = new this.constructor(null); @@ -852,6 +997,18 @@ var Zone$1 = (function (global) { ZoneAwarePromise.prototype.catch = function (onRejected) { return this.then(null, onRejected); }; + ZoneAwarePromise.prototype.finally = function (onFinally) { + var chainPromise = new this.constructor(null); + chainPromise[symbolFinally] = symbolFinally; + var zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFinally, onFinally); + } + else { + scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally); + } + return chainPromise; + }; return ZoneAwarePromise; }()); // Protect against aggressive optimizers dropping seemingly unused properties. @@ -861,10 +1018,53 @@ var Zone$1 = (function (global) { ZoneAwarePromise['race'] = ZoneAwarePromise.race; ZoneAwarePromise['all'] = ZoneAwarePromise.all; var NativePromise = global[symbolPromise] = global['Promise']; + var ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + // NOTE(NativeScript): Defining the Promise property descriptor this way causes + // problems with V8 snapshot for Android. Just skip it. + // let desc = ObjectGetOwnPropertyDescriptor(global, 'Promise'); + // if (!desc || desc.configurable) { + // desc && delete desc.writable; + // desc && delete desc.value; + // if (!desc) { + // desc = {configurable: true, enumerable: true}; + // } + // desc.get = function() { + // // if we already set ZoneAwarePromise, use patched one + // // otherwise return native one. + // return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; + // }; + // desc.set = function(NewNativePromise) { + // if (NewNativePromise === ZoneAwarePromise) { + // // if the NewNativePromise is ZoneAwarePromise + // // save to global + // global[ZONE_AWARE_PROMISE] = NewNativePromise; + // } else { + // // if the NewNativePromise is not ZoneAwarePromise + // // for example: after load zone.js, some library just + // // set es6-promise to global, if we set it to global + // // directly, assertZonePatched will fail and angular + // // will not loaded, so we just set the NewNativePromise + // // to global[symbolPromise], so the result is just like + // // we load ES6 Promise before zone.js + // global[symbolPromise] = NewNativePromise; + // if (!NewNativePromise.prototype[symbolThen]) { + // patchThen(NewNativePromise); + // } + // api.setNativePromise(NewNativePromise); + // } + // }; + // ObjectDefineProperty(global, 'Promise', desc); + // } global['Promise'] = ZoneAwarePromise; var symbolThenPatched = __symbol__('thenPatched'); function patchThen(Ctor) { var proto = Ctor.prototype; + var prop = ObjectGetOwnPropertyDescriptor(proto, 'then'); + if (prop && (prop.writable === false || !prop.configurable)) { + // check Ctor.prototype.then propertyDescriptor is writable or not + // in meteor env, writable is false, we should ignore such case + return; + } var originalThen = proto.then; // Keep a reference to the original method. proto[symbolThen] = originalThen; @@ -883,9 +1083,9 @@ var Zone$1 = (function (global) { if (resultPromise instanceof ZoneAwarePromise) { return resultPromise; } - var Ctor = resultPromise.constructor; - if (!Ctor[symbolThenPatched]) { - patchThen(Ctor); + var ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); } return resultPromise; }; @@ -897,120 +1097,269 @@ var Zone$1 = (function (global) { global['fetch'] = zoneify(fetch_1); } } - // This is not part of public API, but it is usefull for tests, so we expose it. + // This is not part of public API, but it is useful for tests, so we expose it. Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; + return ZoneAwarePromise; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * Suppress closure compiler errors about unknown 'Zone' variable + * @fileoverview + * @suppress {undefinedVars,globalThis,missingRequire} + */ +// issue #989, to reduce bundle size, use short name +/** Object.getOwnPropertyDescriptor */ +var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +/** Object.defineProperty */ + +/** Object.getPrototypeOf */ +var ObjectGetPrototypeOf = Object.getPrototypeOf; +/** Object.create */ + +/** Array.prototype.slice */ + +/** addEventListener string const */ +var ADD_EVENT_LISTENER_STR = 'addEventListener'; +/** removeEventListener string const */ +var REMOVE_EVENT_LISTENER_STR = 'removeEventListener'; +/** zoneSymbol addEventListener */ +var ZONE_SYMBOL_ADD_EVENT_LISTENER = Zone.__symbol__(ADD_EVENT_LISTENER_STR); +/** zoneSymbol removeEventListener */ +var ZONE_SYMBOL_REMOVE_EVENT_LISTENER = Zone.__symbol__(REMOVE_EVENT_LISTENER_STR); +/** true string const */ + +/** false string const */ + +/** __zone_symbol__ string const */ + + +function scheduleMacroTaskWithCurrentZone(source, callback, data, customSchedule, customCancel) { + return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel); +} +var zoneSymbol = Zone.__symbol__; +var isWindowExists = typeof window !== 'undefined'; +var internalWindow = isWindowExists ? window : undefined; +var _global = isWindowExists && internalWindow || typeof self === 'object' && self || global; + + +function isPropertyWritable(propertyDesc) { + if (!propertyDesc) { + return true; + } + if (propertyDesc.writable === false) { + return false; + } + return !(typeof propertyDesc.get === 'function' && typeof propertyDesc.set === 'undefined'); +} +var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); +// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify +// this code. +var isNode = (!('nw' in _global) && typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]'); +var isBrowser = !isNode && !isWebWorker && !!(isWindowExists && internalWindow['HTMLElement']); +// we are in electron of nw, so we are both browser and nodejs +// Make sure to access `process` through `_global` so that WebPack does not accidentally browserify +// this code. +var isMix = typeof _global.process !== 'undefined' && + {}.toString.call(_global.process) === '[object process]' && !isWebWorker && + !!(isWindowExists && internalWindow['HTMLElement']); + + +var originalInstanceKey = zoneSymbol('originalInstance'); +// wrap some native API on `window` + +function patchMethod(target, name, patchFn) { + var proto = target; + while (proto && !proto.hasOwnProperty(name)) { + proto = ObjectGetPrototypeOf(proto); + } + if (!proto && target[name]) { + // somehow we did not find it, but we can see it. This happens on IE for Window properties. + proto = target; + } + var delegateName = zoneSymbol(name); + var delegate; + if (proto && !(delegate = proto[delegateName])) { + delegate = proto[delegateName] = proto[name]; + // check whether proto[name] is writable + // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob + var desc = proto && ObjectGetOwnPropertyDescriptor(proto, name); + if (isPropertyWritable(desc)) { + var patchDelegate_1 = patchFn(delegate, delegateName, name); + proto[name] = function () { + return patchDelegate_1(this, arguments); + }; + attachOriginToPatched(proto[name], delegate); + } + } + return delegate; +} +// TODO: @JiaLiPassion, support cancel task later if necessary + + +function attachOriginToPatched(patched, original) { + patched[zoneSymbol('OriginalDelegate')] = original; +} + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +// override Function.prototype.toString to make zone.js patched function +// look like native function +Zone.__load_patch('toString', function (global) { + // patch Func.prototype.toString to let them look like native + var originalFunctionToString = Function.prototype.toString; + var ORIGINAL_DELEGATE_SYMBOL = zoneSymbol('OriginalDelegate'); + var PROMISE_SYMBOL = zoneSymbol('Promise'); + var ERROR_SYMBOL = zoneSymbol('Error'); + var newFunctionToString = function toString() { + if (typeof this === 'function') { + var originalDelegate = this[ORIGINAL_DELEGATE_SYMBOL]; + if (originalDelegate) { + if (typeof originalDelegate === 'function') { + return originalFunctionToString.apply(this[ORIGINAL_DELEGATE_SYMBOL], arguments); + } + else { + return Object.prototype.toString.call(originalDelegate); + } + } + if (this === Promise) { + var nativePromise = global[PROMISE_SYMBOL]; + if (nativePromise) { + return originalFunctionToString.apply(nativePromise, arguments); + } + } + if (this === Error) { + var nativeError = global[ERROR_SYMBOL]; + if (nativeError) { + return originalFunctionToString.apply(nativeError, arguments); + } + } + } + return originalFunctionToString.apply(this, arguments); + }; + newFunctionToString[ORIGINAL_DELEGATE_SYMBOL] = originalFunctionToString; + Function.prototype.toString = newFunctionToString; + // patch Object.prototype.toString to let them look like native + var originalObjectToString = Object.prototype.toString; + var PROMISE_OBJECT_TO_STRING = '[object Promise]'; + Object.prototype.toString = function () { + if (this instanceof Promise) { + return PROMISE_OBJECT_TO_STRING; + } + return originalObjectToString.apply(this, arguments); + }; +}); + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * @fileoverview + * @suppress {globalThis,undefinedVars} + */ +Zone.__load_patch('Error', function (global, Zone, api) { /* * This code patches Error so that: * - It ignores un-needed stack frames. * - It Shows the associated Zone for reach frame. */ - var FrameType; - (function (FrameType) { - /// Skip this frame when printing out stack - FrameType[FrameType["blackList"] = 0] = "blackList"; - /// This frame marks zone transition - FrameType[FrameType["transition"] = 1] = "transition"; - })(FrameType || (FrameType = {})); - var NativeError = global[__symbol__('Error')] = global.Error; + var blacklistedStackFramesSymbol = api.symbol('blacklistedStackFrames'); + var NativeError = global[api.symbol('Error')] = global['Error']; // Store the frames which should be removed from the stack frames var blackListedStackFrames = {}; // We must find the frame where Error was created, otherwise we assume we don't understand stack - // the frame will be an array, because Error with new or without new will - // have different stack frames. - var zoneAwareErrorStartFrames = []; - global.Error = ZoneAwareError; + var zoneAwareFrame1; + var zoneAwareFrame2; + global['Error'] = ZoneAwareError; var stackRewrite = 'stackRewrite'; - // some functions are not easily to be detected here, - // for example Timeout.ZoneTask.invoke, if we want to detect those functions - // by detect zone, we have to run all patched APIs, it is too risky - // so for those functions, just check whether the stack contains the string or not. - var otherZoneAwareFunctionNames = [ - 'ZoneTask.invoke', 'ZoneAware', 'getStacktraceWithUncaughtError', 'new LongStackTrace', - 'long-stack-trace' - ]; - function attachZoneAndRemoveInternalZoneFrames(error) { - // Save original stack trace - error.originalStack = error.stack; - // Process the stack trace and rewrite the frames. - if (ZoneAwareError[stackRewrite] && error.originalStack) { - var frames_1 = error.originalStack.split('\n'); - var zoneFrame = _currentZoneFrame; - var i_1 = 0; - // Find the first frame - while (i_1 < frames_1.length && - zoneAwareErrorStartFrames.filter(function (zf) { return zf.trim() === frames_1[i_1].trim(); }).length === 0) { - i_1++; - } - var _loop_2 = function () { - // trim here because blackListedStackFrames store the trimmed frames - var frame = frames_1[i_1].trim(); - if (frame) { - var frameType = blackListedStackFrames.hasOwnProperty(frame) && blackListedStackFrames[frame]; - if (frameType === FrameType.blackList) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (otherZoneAwareFunctionNames - .filter(function (f) { return frame.toLowerCase().indexOf(f.toLowerCase()) !== -1; }) - .length > 0) { - frames_1.splice(i_1, 1); - i_1--; - } - else if (frameType === FrameType.transition) { - if (zoneFrame.parent) { - // This is the special frame where zone changed. Print and process it accordingly - zoneFrame = zoneFrame.parent; - } - else { - zoneFrame = null; - } - frames_1.splice(i_1, 1); - i_1--; - } - else { - frames_1[i_1] += " [" + zoneFrame.zone.name + "]"; - } - } - }; - for (; i_1 < frames_1.length && zoneFrame; i_1++) { - _loop_2(); - } - var finalStack = frames_1.join('\n'); - try { - error.stack = error.zoneAwareStack = finalStack; - } - catch (nonWritableErr) { - // in some browser, the error.stack is readonly such as PhantomJS - // so we need to store the stack frames to zoneAwareError directly - } - } - } /** * This is ZoneAwareError which processes the stack frame and cleans up extra frames as well as * adds zone information to it. */ function ZoneAwareError() { + var _this = this; // We always have to return native error otherwise the browser console will not work. var error = NativeError.apply(this, arguments); - if (!error.stack) { - // in IE, the error.stack will be undefined - // when error was constructed, it will only - // be available when throw + // Save original stack trace + var originalStack = error['originalStack'] = error.stack; + // Process the stack trace and rewrite the frames. + if (ZoneAwareError[stackRewrite] && originalStack) { + var frames_1 = originalStack.split('\n'); + var zoneFrame = api.currentZoneFrame(); + var i = 0; + // Find the first frame + while (!(frames_1[i] === zoneAwareFrame1 || frames_1[i] === zoneAwareFrame2) && + i < frames_1.length) { + i++; + } + for (; i < frames_1.length && zoneFrame; i++) { + var frame = frames_1[i]; + if (frame.trim()) { + switch (blackListedStackFrames[frame]) { + case 0 /* blackList */: + frames_1.splice(i, 1); + i--; + break; + case 1 /* transition */: + if (zoneFrame.parent) { + // This is the special frame where zone changed. Print and process it accordingly + zoneFrame = zoneFrame.parent; + } + else { + zoneFrame = null; + } + frames_1.splice(i, 1); + i--; + break; + default: + frames_1[i] += " [" + zoneFrame.zone.name + "]"; + } + } + } try { - throw error; + error.stack = error.zoneAwareStack = frames_1.join('\n'); } - catch (err) { - error = err; + catch (e) { + // ignore as some browsers don't allow overriding of stack } } - // 1. attach zone information to stack frame - // 2. remove zone internal stack frames - attachZoneAndRemoveInternalZoneFrames(error); + if (this instanceof NativeError && this.constructor != NativeError) { + // We got called with a `new` operator AND we are subclass of ZoneAwareError + // in that case we have to copy all of our properties to `this`. + Object.keys(error).concat('stack', 'message').forEach(function (key) { + var value = error[key]; + if (value !== undefined) { + try { + _this[key] = value; + } + catch (e) { + // ignore the assignment in case it is a setter and it throws. + } + } + }); + return this; + } return error; } // Copy the prototype so that instanceof operator works as expected ZoneAwareError.prototype = NativeError.prototype; - ZoneAwareError[Zone.__symbol__('blacklistedStackFrames')] = blackListedStackFrames; + ZoneAwareError[blacklistedStackFramesSymbol] = blackListedStackFrames; ZoneAwareError[stackRewrite] = false; // those properties need special handling var specialPropertyNames = ['stackTraceLimit', 'captureStackTrace', 'prepareStackTrace']; @@ -1052,6 +1401,7 @@ var Zone$1 = (function (global) { } }); } + var ZONE_CAPTURESTACKTRACE = 'zoneCaptureStackTrace'; Object.defineProperty(ZoneAwareError, 'prepareStackTrace', { get: function () { return NativeError.prepareStackTrace; @@ -1066,26 +1416,30 @@ var Zone$1 = (function (global) { for (var i = 0; i < structuredStackTrace.length; i++) { var st = structuredStackTrace[i]; // remove the first function which name is zoneCaptureStackTrace - if (st.getFunctionName() === 'zoneCaptureStackTrace') { + if (st.getFunctionName() === ZONE_CAPTURESTACKTRACE) { structuredStackTrace.splice(i, 1); break; } } } - return value.apply(this, [error, structuredStackTrace]); + return value.call(this, error, structuredStackTrace); }; } }); // Now we need to populate the `blacklistedStackFrames` as well as find the - // run/runGuraded/runTask frames. This is done by creating a detect zone and then threading + // run/runGuarded/runTask frames. This is done by creating a detect zone and then threading // the execution through all of the above methods so that we can look at the stack trace and // find the frames of interest. + var ZONE_AWARE_ERROR = 'ZoneAwareError'; + var ERROR_DOT = 'Error.'; + var EMPTY = ''; + var RUN_GUARDED = 'runGuarded'; + var RUN_TASK = 'runTask'; + var RUN = 'run'; + var BRACKETS = '('; + var AT = '@'; var detectZone = Zone.current.fork({ name: 'detect', - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - // Here only so that it will show up in the stack frame so that it can be black listed. - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); - }, onHandleError: function (parentZD, current, target, error) { if (error.originalStack && Error === ZoneAwareError) { var frames_2 = error.originalStack.split(/\n/); @@ -1094,7 +1448,7 @@ var Zone$1 = (function (global) { var frame = frames_2.shift(); // On safari it is possible to have stack frame with no line number. // This check makes sure that we don't filter frames on name only (must have - // linenumber) + // line number) if (/:\d+:\d+/.test(frame)) { // Get rid of the path so that we don't accidentally find function name in path. // In chrome the separator is `(` and `@` in FF and safari @@ -1102,27 +1456,26 @@ var Zone$1 = (function (global) { // Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24) // FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24 // Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24 - var fnName = frame.split('(')[0].split('@')[0]; - var frameType = FrameType.transition; - if (fnName.indexOf('ZoneAwareError') !== -1) { - // we found the ZoneAwareError start frame - // the frame will be different when call Error(...) - // and new Error(...), so we store them both - zoneAwareErrorStartFrames.push(frame); + var fnName = frame.split(BRACKETS)[0].split(AT)[0]; + var frameType = 1; + if (fnName.indexOf(ZONE_AWARE_ERROR) !== -1) { + zoneAwareFrame1 = frame; + zoneAwareFrame2 = frame.replace(ERROR_DOT, EMPTY); + blackListedStackFrames[zoneAwareFrame2] = 0 /* blackList */; } - if (fnName.indexOf('runGuarded') !== -1) { + if (fnName.indexOf(RUN_GUARDED) !== -1) { runGuardedFrame = true; } - else if (fnName.indexOf('runTask') !== -1) { + else if (fnName.indexOf(RUN_TASK) !== -1) { runTaskFrame = true; } - else if (fnName.indexOf('run') !== -1) { + else if (fnName.indexOf(RUN) !== -1) { runFrame = true; } else { - frameType = FrameType.blackList; + frameType = 0 /* blackList */; } - blackListedStackFrames[frame.trim()] = frameType; + blackListedStackFrames[frame] = frameType; // Once we find all of the frames we can stop looking. if (runFrame && runGuardedFrame && runTaskFrame) { ZoneAwareError[stackRewrite] = true; @@ -1136,190 +1489,52 @@ var Zone$1 = (function (global) { }); // carefully constructor a stack frame which contains all of the frames of interest which // need to be detected and blacklisted. - // use this method to handle - // 1. IE issue, the error.stack can only be not undefined after throw - // 2. handle Error(...) without new options - var throwError = function (message, withNew) { - try { - if (withNew) { - throw new Error(message); - } - else { - throw Error(message); - } - } - catch (err) { - return err; - } - }; - var nativeStackTraceLimit = NativeError.stackTraceLimit; - // in some system/browser, some additional stack frames - // will be generated (such as inline function) - // so the the stack frame to check ZoneAwareError Start - // maybe ignored because the frame's number will exceed - // stackTraceLimit, so we just set stackTraceLimit to 100 - // and reset after all detect work is done. - NativeError.stackTraceLimit = 100; - var detectRunFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames', true); - }); - }); - }; - var detectRunWithoutNewFn = function () { - detectZone.run(function () { - detectZone.runGuarded(function () { - throw throwError('blacklistStackFrames'); - }); - }); - }; - // Cause the error to extract the stack frames. - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunFn, null, function () { return null; }, null)); - detectZone.runTask(detectZone.scheduleMacroTask('detect', detectRunWithoutNewFn, null, function () { return null; }, null)); - function handleDetectError(error) { - var frames = error.stack ? error.stack.split(/\n/) : []; - while (frames.length) { - var frame = frames.shift(); - // On safari it is possible to have stack frame with no line number. - // This check makes sure that we don't filter frames on name only (must have - // linenumber) - var trimmedFrame = frame.trim().split('[')[0].trim(); - if (/:\d+:\d+/.test(trimmedFrame) && !blackListedStackFrames.hasOwnProperty(trimmedFrame)) { - blackListedStackFrames[trimmedFrame] = FrameType.blackList; - } - // when we found runGuarded or runTask, we should stop - // otherwise we will store some stack frames like - // module.load, require and something like that - var fnName = frame.split('(')[0].split('@')[0]; - if (fnName.indexOf('runGuarded') !== -1) { - break; - } - else if (fnName.indexOf('runTask') !== -1) { - break; - } - } - } - var detectEmptyZone = Zone.root.fork({ - name: 'detectEmptyZone', - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; - } - }); - var detectZoneWithCallbacks = Zone.root.fork({ - name: 'detectCallbackZone', - onFork: function (parentDelegate, currentZone, targetZone, zoneSpec) { - // we need to generate Error with or without new - handleDetectError(throwError('onFork')); - handleDetectError(throwError('onFork', false)); - return parentDelegate.fork(targetZone, zoneSpec); - }, - onIntercept: function (parentDelegate, currentZone, targetZone, delegate, source) { - handleDetectError(throwError('onIntercept')); - handleDetectError(throwError('onIntercept', false)); - return parentDelegate.intercept(targetZone, delegate, source); - }, - onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { - handleDetectError(throwError('onInvoke')); - handleDetectError(throwError('onInvoke', false)); - return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); - }, - onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onScheduleTask')); - handleDetectError(throwError('onScheduleTask', false)); - return parentZoneDelegate.scheduleTask(targetZone, task); - }, - onInvokeTask: function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) { - handleDetectError(throwError('onInvokeTask')); - handleDetectError(throwError('onInvokeTask', false)); - return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + var childDetectZone = detectZone.fork({ + name: 'child', + onScheduleTask: function (delegate, curr, target, task) { + return delegate.scheduleTask(target, task); }, - onCancelTask: function (parentZoneDelegate, currentZone, targetZone, task) { - handleDetectError(throwError('onCancelTask')); - handleDetectError(throwError('onCancelTask', false)); - return parentZoneDelegate.cancelTask(targetZone, task); + onInvokeTask: function (delegate, curr, target, task, applyThis, applyArgs) { + return delegate.invokeTask(target, task, applyThis, applyArgs); }, - onHasTask: function (delegate, current, target, hasTaskState) { - handleDetectError(throwError('onHasTask')); - handleDetectError(throwError('onHasTask', false)); - return delegate.hasTask(target, hasTaskState); + onCancelTask: function (delegate, curr, target, task) { + return delegate.cancelTask(target, task); }, - onHandleError: function (parentDelegate, currentZone, targetZone, error) { - parentDelegate.handleError(targetZone, error); - handleDetectError(error); - return false; + onInvoke: function (delegate, curr, target, callback, applyThis, applyArgs, source) { + return delegate.invoke(target, callback, applyThis, applyArgs, source); } }); - var detectFn = function () { - throw throwError('zoneAwareFrames'); - }; - var detectWithoutNewFn = function () { - throw throwError('zoneAwareFrames', false); - }; - var detectPromiseFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - }; - var detectPromiseWithoutNewFn = function () { - new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); + // we need to detect all zone related frames, it will + // exceed default stackTraceLimit, so we set it to + // larger number here, and restore it after detect finish. + var originalStackTraceLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 100; + // we schedule event/micro/macro task, and invoke them + // when onSchedule, so we can get all stack traces for + // all kinds of tasks with one error thrown. + childDetectZone.run(function () { + childDetectZone.runGuarded(function () { + var fakeTransitionTo = function () { }; + childDetectZone.scheduleEventTask(blacklistedStackFramesSymbol, function () { + childDetectZone.scheduleMacroTask(blacklistedStackFramesSymbol, function () { + childDetectZone.scheduleMicroTask(blacklistedStackFramesSymbol, function () { + throw new ZoneAwareError(ZoneAwareError, NativeError); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }, function () { }); + }, null, function (t) { + t._transitionTo = fakeTransitionTo; + t.invoke(); + }, function () { }); }); - }; - var detectPromiseCaughtFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames')); - }); - p.catch(function (err) { - throw err; - }); - }; - var detectPromiseCaughtWithoutNewFn = function () { - var p = new Promise(function (resolve, reject) { - reject(throwError('zoneAwareFrames', false)); - }); - p.catch(function (err) { - throw err; - }); - }; - // Cause the error to extract the stack frames. - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectFn); }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectFn); - }); - detectEmptyZone.runTask(detectEmptyZone.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectWithoutNewFn, null, function () { return null; }, null)); - detectEmptyZone.runTask(detectEmptyZone.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectWithoutNewFn, null, function () { return null; })); - detectEmptyZone.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectZoneWithCallbacks.runGuarded(function () { - detectEmptyZone.run(detectWithoutNewFn); - }); - detectEmptyZone.runGuarded(detectPromiseFn); - detectZoneWithCallbacks.runGuarded(detectPromiseFn); - detectEmptyZone.runGuarded(detectPromiseWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseWithoutNewFn); - detectEmptyZone.runGuarded(detectPromiseCaughtFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtFn); - detectEmptyZone.runGuarded(detectPromiseCaughtWithoutNewFn); - detectZoneWithCallbacks.runGuarded(detectPromiseCaughtWithoutNewFn); - NativeError.stackTraceLimit = nativeStackTraceLimit; - return global['Zone'] = Zone; -})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); + Error.stackTraceLimit = originalStackTraceLimit; +}); /** * @license @@ -1329,300 +1544,10 @@ var Zone$1 = (function (global) { * found in the LICENSE file at https://angular.io/license */ /** - * Suppress closure compiler errors about unknown 'Zone' variable * @fileoverview - * @suppress {undefinedVars,globalThis} - */ -var zoneSymbol = function (n) { return "__zone_symbol__" + n; }; -var _global = typeof window === 'object' && window || typeof self === 'object' && self || global; - - -var isWebWorker = (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); -var isNode = (!('nw' in _global) && typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]'); - -// we are in electron of nw, so we are both browser and nodejs -var isMix = typeof process !== 'undefined' && - {}.toString.call(process) === '[object process]' && !isWebWorker && - !!(typeof window !== 'undefined' && window['HTMLElement']); -function patchProperty(obj, prop) { - var desc = Object.getOwnPropertyDescriptor(obj, prop) || { enumerable: true, configurable: true }; - var originalDesc = Object.getOwnPropertyDescriptor(obj, 'original' + prop); - if (!originalDesc && desc.get) { - Object.defineProperty(obj, 'original' + prop, { enumerable: false, configurable: true, get: desc.get }); - } - // A property descriptor cannot have getter/setter and be writable - // deleting the writable and value properties avoids this error: - // - // TypeError: property descriptors must not specify a value or be writable when a - // getter or setter has been specified - delete desc.writable; - delete desc.value; - // substr(2) cuz 'onclick' -> 'click', etc - var eventName = prop.substr(2); - var _prop = zoneSymbol('_' + prop); - desc.set = function (fn) { - if (this[_prop]) { - this.removeEventListener(eventName, this[_prop]); - } - if (typeof fn === 'function') { - var wrapFn = function (event) { - var result; - result = fn.apply(this, arguments); - if (result != undefined && !result) - event.preventDefault(); - }; - this[_prop] = wrapFn; - this.addEventListener(eventName, wrapFn, false); - } - else { - this[_prop] = null; - } - }; - // The getter would return undefined for unassigned properties but the default value of an - // unassigned property is null - desc.get = function () { - var r = this[_prop] || null; - // result will be null when use inline event attribute, - // such as - // because the onclick function is internal raw uncompiled handler - // the onclick will be evaluated when first time event was triggered or - // the property is accessed, https://github.com/angular/zone.js/issues/525 - // so we should use original native get to retrieve the handler - if (r === null) { - if (originalDesc && originalDesc.get) { - r = originalDesc.get.apply(this, arguments); - if (r) { - desc.set.apply(this, [r]); - if (typeof this['removeAttribute'] === 'function') { - this.removeAttribute(prop); - } - } - } - } - return this[_prop] || null; - }; - Object.defineProperty(obj, prop, desc); -} - -function patchOnProperties(obj, properties) { - var onProperties = []; - for (var prop in obj) { - if (prop.substr(0, 2) == 'on') { - onProperties.push(prop); - } - } - for (var j = 0; j < onProperties.length; j++) { - patchProperty(obj, onProperties[j]); - } - if (properties) { - for (var i = 0; i < properties.length; i++) { - patchProperty(obj, 'on' + properties[i]); - } - } -} - -var EVENT_TASKS = zoneSymbol('eventTasks'); -// For EventTarget -var ADD_EVENT_LISTENER = 'addEventListener'; -var REMOVE_EVENT_LISTENER = 'removeEventListener'; -function findExistingRegisteredTask(target, handler, name, capture, remove) { - var eventTasks = target[EVENT_TASKS]; - if (eventTasks) { - for (var i = 0; i < eventTasks.length; i++) { - var eventTask = eventTasks[i]; - var data = eventTask.data; - var listener = data.handler; - if ((data.handler === handler || listener.listener === handler) && - data.useCapturing === capture && data.eventName === name) { - if (remove) { - eventTasks.splice(i, 1); - } - return eventTask; - } - } - } - return null; -} -function attachRegisteredEvent(target, eventTask, isPrepend) { - var eventTasks = target[EVENT_TASKS]; - if (!eventTasks) { - eventTasks = target[EVENT_TASKS] = []; - } - if (isPrepend) { - eventTasks.unshift(eventTask); - } - else { - eventTasks.push(eventTask); - } -} -var defaultListenerMetaCreator = function (self, args) { - return { - useCapturing: args[2], - eventName: args[0], - handler: args[1], - target: self || _global, - name: args[0], - invokeAddFunc: function (addFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[addFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[addFnSymbol](this.eventName, delegate, this.useCapturing); - } - }, - invokeRemoveFunc: function (removeFnSymbol, delegate) { - if (delegate && delegate.invoke) { - return this.target[removeFnSymbol](this.eventName, delegate.invoke, this.useCapturing); - } - else { - return this.target[removeFnSymbol](this.eventName, delegate, this.useCapturing); - } - } - }; -}; -function makeZoneAwareAddListener(addFnName, removeFnName, useCapturingParam, allowDuplicates, isPrepend, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (allowDuplicates === void 0) { allowDuplicates = false; } - if (isPrepend === void 0) { isPrepend = false; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var addFnSymbol = zoneSymbol(addFnName); - var removeFnSymbol = zoneSymbol(removeFnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - function scheduleEventListener(eventTask) { - var meta = eventTask.data; - attachRegisteredEvent(meta.target, eventTask, isPrepend); - return meta.invokeAddFunc(addFnSymbol, eventTask); - } - function cancelEventListener(eventTask) { - var meta = eventTask.data; - findExistingRegisteredTask(meta.target, eventTask.invoke, meta.eventName, meta.useCapturing, true); - return meta.invokeRemoveFunc(removeFnSymbol, eventTask); - } - return function zoneAwareAddListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var delegate = null; - if (typeof data.handler == 'function') { - delegate = data.handler; - } - else if (data.handler && data.handler.handleEvent) { - delegate = function (event) { return data.handler.handleEvent(event); }; - } - var validZoneHandler = false; - try { - // In cross site contexts (such as WebDriver frameworks like Selenium), - // accessing the handler object here will cause an exception to be thrown which - // will fail tests prematurely. - validZoneHandler = data.handler && data.handler.toString() === '[object FunctionWrapper]'; - } - catch (error) { - // Returning nothing here is fine, because objects in a cross-site context are unusable - return; - } - // Ignore special listeners of IE11 & Edge dev tools, see - // https://github.com/angular/zone.js/issues/150 - if (!delegate || validZoneHandler) { - return data.invokeAddFunc(addFnSymbol, data.handler); - } - if (!allowDuplicates) { - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, false); - if (eventTask) { - // we already registered, so this will have noop. - return data.invokeAddFunc(addFnSymbol, eventTask); - } - } - var zone = Zone.current; - var source = data.target.constructor['name'] + '.' + addFnName + ':' + data.eventName; - zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener); - }; -} -function makeZoneAwareRemoveListener(fnName, useCapturingParam, metaCreator) { - if (useCapturingParam === void 0) { useCapturingParam = true; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - var symbol = zoneSymbol(fnName); - var defaultUseCapturing = useCapturingParam ? false : undefined; - return function zoneAwareRemoveListener(self, args) { - var data = metaCreator(self, args); - data.useCapturing = data.useCapturing || defaultUseCapturing; - // - Inside a Web Worker, `this` is undefined, the context is `global` - // - When `addEventListener` is called on the global context in strict mode, `this` is undefined - // see https://github.com/angular/zone.js/issues/190 - var eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, true); - if (eventTask) { - eventTask.zone.cancelTask(eventTask); - } - else { - data.invokeRemoveFunc(symbol, data.handler); - } - }; -} - - -var zoneAwareAddEventListener = makeZoneAwareAddListener(ADD_EVENT_LISTENER, REMOVE_EVENT_LISTENER); -var zoneAwareRemoveEventListener = makeZoneAwareRemoveListener(REMOVE_EVENT_LISTENER); -function patchEventTargetMethods(obj, addFnName, removeFnName, metaCreator) { - if (addFnName === void 0) { addFnName = ADD_EVENT_LISTENER; } - if (removeFnName === void 0) { removeFnName = REMOVE_EVENT_LISTENER; } - if (metaCreator === void 0) { metaCreator = defaultListenerMetaCreator; } - if (obj && obj[addFnName]) { - patchMethod(obj, addFnName, function () { return makeZoneAwareAddListener(addFnName, removeFnName, true, false, false, metaCreator); }); - patchMethod(obj, removeFnName, function () { return makeZoneAwareRemoveListener(removeFnName, true, metaCreator); }); - return true; - } - else { - return false; - } -} -var originalInstanceKey = zoneSymbol('originalInstance'); -// wrap some native API on `window` - - -function createNamedFn(name, delegate) { - try { - return (Function('f', "return function " + name + "(){return f(this, arguments)}"))(delegate); - } - catch (error) { - // if we fail, we must be CSP, just return delegate. - return function () { - return delegate(this, arguments); - }; - } -} -function patchMethod(target, name, patchFn) { - var proto = target; - while (proto && Object.getOwnPropertyNames(proto).indexOf(name) === -1) { - proto = Object.getPrototypeOf(proto); - } - if (!proto && target[name]) { - // somehow we did not find it, but we can see it. This happens on IE for Window properties. - proto = target; - } - var delegateName = zoneSymbol(name); - var delegate; - if (proto && !(delegate = proto[delegateName])) { - delegate = proto[delegateName] = proto[name]; - proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); - } - return delegate; -} -// TODO: @JiaLiPassion, support cancel task later if necessary - - - -Zone[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods; -Zone[zoneSymbol('patchOnProperties')] = patchOnProperties; - -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * @suppress {missingRequire} */ +var taskSymbol = zoneSymbol('zoneTask'); function patchTimer(window, setName, cancelName, nameSuffix) { var setNative = null; var clearNative = null; @@ -1636,35 +1561,55 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.invoke.apply(this, arguments); } finally { - delete tasksByHandleId[data.handleId]; + // issue-934, task will be cancelled + // even it is a periodic task such as + // setInterval + if (!(task.data && task.data.isPeriodic)) { + if (typeof data.handleId === 'number') { + // in non-nodejs env, we remove timerId + // from local cache + delete tasksByHandleId[data.handleId]; + } + else if (data.handleId) { + // Node returns complex objects as handleIds + // we remove task reference from timer object + data.handleId[taskSymbol] = null; + } + } } } - data.args[0] = timer; data.handleId = setNative.apply(window, data.args); - tasksByHandleId[data.handleId] = task; return task; } function clearTask(task) { - delete tasksByHandleId[task.data.handleId]; return clearNative(task.data.handleId); } setNative = patchMethod(window, setName, function (delegate) { return function (self, args) { if (typeof args[0] === 'function') { - var zone = Zone.current; var options = { handleId: null, isPeriodic: nameSuffix === 'Interval', delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, args: args }; - var task = zone.scheduleMacroTask(setName, args[0], options, scheduleTask, clearTask); + var task = scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask); if (!task) { return task; } // Node.js must additionally support the ref and unref functions. var handle = task.data.handleId; + if (typeof handle === 'number') { + // for non nodejs env, we save handleId: task + // mapping in local cache for clearTimeout + tasksByHandleId[handle] = task; + } + else if (handle) { + // for nodejs env, we save task + // reference in timerId Object for clearTimeout + handle[taskSymbol] = task; + } // check whether handle is null, because some polyfill or browser // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && @@ -1672,6 +1617,9 @@ function patchTimer(window, setName, cancelName, nameSuffix) { task.ref = handle.ref.bind(handle); task.unref = handle.unref.bind(handle); } + if (typeof handle === 'number' || handle) { + return handle; + } return task; } else { @@ -1681,10 +1629,29 @@ function patchTimer(window, setName, cancelName, nameSuffix) { }; }); clearNative = patchMethod(window, cancelName, function (delegate) { return function (self, args) { - var task = typeof args[0] === 'number' ? tasksByHandleId[args[0]] : args[0]; + var id = args[0]; + var task; + if (typeof id === 'number') { + // non nodejs env. + task = tasksByHandleId[id]; + } + else { + // nodejs env. + task = id && id[taskSymbol]; + // other environments. + if (!task) { + task = id; + } + } if (task && typeof task.type === 'string') { if (task.state !== 'notScheduled' && (task.cancelFn && task.data.isPeriodic || task.runCount === 0)) { + if (typeof id === 'number') { + delete tasksByHandleId[id]; + } + else if (id) { + id[taskSymbol] = null; + } // Do not cancel already canceled functions task.zone.cancelTask(task); } diff --git a/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js new file mode 100644 index 000000000..0bd1cf21f --- /dev/null +++ b/nativescript-angular/zone-js/dist/zone-nativescript.mocha.js @@ -0,0 +1,1353 @@ +/** +* @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 + */ +/** + * @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 = /** @class */ (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 trace = error[Zone.__symbol__('currentTaskTrace')]; + 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 = /** @class */ (function () { + function ProxyZoneSpec(defaultSpecDelegate) { + if (defaultSpecDelegate === void 0) { defaultSpecDelegate = null; } + this.defaultSpecDelegate = defaultSpecDelegate; + this.name = 'ProxyZone'; + this.properties = { 'ProxyZoneSpec': this }; + this.propertyKeys = null; + this.lastTaskState = null; + this.isNeedToTriggerHasTask = false; + this.tasks = []; + this.setDelegate(defaultSpecDelegate); + } + ProxyZoneSpec.get = function () { + return Zone.current.get('ProxyZoneSpec'); + }; + ProxyZoneSpec.isLoaded = function () { + return ProxyZoneSpec.get() instanceof ProxyZoneSpec; + }; + ProxyZoneSpec.assertPresent = function () { + if (!ProxyZoneSpec.isLoaded()) { + throw new Error("Expected to be running in 'ProxyZone', but it was not found."); + } + return ProxyZoneSpec.get(); + }; + ProxyZoneSpec.prototype.setDelegate = function (delegateSpec) { + var _this = this; + var isNewDelegate = this._delegateSpec !== delegateSpec; + this._delegateSpec = delegateSpec; + this.propertyKeys && this.propertyKeys.forEach(function (key) { return delete _this.properties[key]; }); + this.propertyKeys = null; + if (delegateSpec && delegateSpec.properties) { + this.propertyKeys = Object.keys(delegateSpec.properties); + this.propertyKeys.forEach(function (k) { return _this.properties[k] = delegateSpec.properties[k]; }); + } + // if set a new delegateSpec, shoulde check whether need to + // trigger hasTask or not + if (isNewDelegate && this.lastTaskState && + (this.lastTaskState.macroTask || this.lastTaskState.microTask)) { + this.isNeedToTriggerHasTask = true; + } + }; + ProxyZoneSpec.prototype.getDelegate = function () { + return this._delegateSpec; + }; + ProxyZoneSpec.prototype.resetDelegate = function () { + var delegateSpec = this.getDelegate(); + this.setDelegate(this.defaultSpecDelegate); + }; + ProxyZoneSpec.prototype.tryTriggerHasTask = function (parentZoneDelegate, currentZone, targetZone) { + if (this.isNeedToTriggerHasTask && this.lastTaskState) { + // last delegateSpec has microTask or macroTask + // should call onHasTask in current delegateSpec + this.isNeedToTriggerHasTask = false; + this.onHasTask(parentZoneDelegate, currentZone, targetZone, this.lastTaskState); + } + }; + ProxyZoneSpec.prototype.removeFromTasks = function (task) { + if (!this.tasks) { + return; + } + for (var i = 0; i < this.tasks.length; i++) { + if (this.tasks[i] === task) { + this.tasks.splice(i, 1); + return; + } + } + }; + ProxyZoneSpec.prototype.getAndClearPendingTasksInfo = function () { + if (this.tasks.length === 0) { + return ''; + } + var taskInfo = this.tasks.map(function (task) { + var dataInfo = task.data && + Object.keys(task.data) + .map(function (key) { + return key + ':' + task.data[key]; + }) + .join(','); + return "type: " + task.type + ", source: " + task.source + ", args: {" + dataInfo + "}"; + }); + var pendingTasksInfo = '--Pendng async tasks are: [' + taskInfo + ']'; + // clear tasks + this.tasks = []; + return pendingTasksInfo; + }; + ProxyZoneSpec.prototype.onFork = function (parentZoneDelegate, currentZone, targetZone, zoneSpec) { + if (this._delegateSpec && this._delegateSpec.onFork) { + return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); + } + 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) { + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + 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 (task.type !== 'eventTask') { + this.tasks.push(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 (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onInvokeTask) { + return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); + } + else { + return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); + } + }; + ProxyZoneSpec.prototype.onCancelTask = function (parentZoneDelegate, currentZone, targetZone, task) { + if (task.type !== 'eventTask') { + this.removeFromTasks(task); + } + this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); + if (this._delegateSpec && this._delegateSpec.onCancelTask) { + return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); + } + else { + return parentZoneDelegate.cancelTask(targetZone, task); + } + }; + ProxyZoneSpec.prototype.onHasTask = function (delegate, current, target, hasTaskState) { + this.lastTaskState = 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 = /** @class */ (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 _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global; +var AsyncTestZoneSpec = /** @class */ (function () { + function AsyncTestZoneSpec(finishCallback, failCallback, namePrefix) { + this.finishCallback = finishCallback; + this.failCallback = failCallback; + this._pendingMicroTasks = false; + this._pendingMacroTasks = false; + this._alreadyErrored = false; + this._isSync = false; + this.runZone = Zone.current; + this.unresolvedChainedPromiseCount = 0; + this.supportWaitUnresolvedChainedPromise = false; + this.name = 'asyncTestZone for ' + namePrefix; + this.properties = { 'AsyncTestZoneSpec': this }; + this.supportWaitUnresolvedChainedPromise = + _global[Zone.__symbol__('supportWaitUnResolvedChainedPromise')] === true; + } + AsyncTestZoneSpec.prototype.isUnresolvedChainedPromisePending = function () { + return this.unresolvedChainedPromiseCount > 0; + }; + AsyncTestZoneSpec.prototype._finishCallbackIfDone = function () { + var _this = this; + if (!(this._pendingMicroTasks || this._pendingMacroTasks || + (this.supportWaitUnresolvedChainedPromise && this.isUnresolvedChainedPromisePending()))) { + // We do this because we would like to catch unhandled rejected promises. + this.runZone.run(function () { + setTimeout(function () { + if (!_this._alreadyErrored && !(_this._pendingMicroTasks || _this._pendingMacroTasks)) { + _this.finishCallback(); + } + }, 0); + }); + } + }; + AsyncTestZoneSpec.prototype.patchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var patchPromiseForTest = Promise[Zone.__symbol__('patchPromiseForTest')]; + if (patchPromiseForTest) { + patchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.unPatchPromiseForTest = function () { + if (!this.supportWaitUnresolvedChainedPromise) { + return; + } + var unPatchPromiseForTest = Promise[Zone.__symbol__('unPatchPromiseForTest')]; + if (unPatchPromiseForTest) { + unPatchPromiseForTest(); + } + }; + AsyncTestZoneSpec.prototype.onScheduleTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + if (task.type === 'microTask' && task.data && task.data instanceof Promise) { + // check whether the promise is a chained promise + if (task.data[AsyncTestZoneSpec.symbolParentUnresolved] === true) { + // chained promise is being scheduled + this.unresolvedChainedPromiseCount--; + } + } + return delegate.scheduleTask(target, task); + }; + AsyncTestZoneSpec.prototype.onInvokeTask = function (delegate, current, target, task, applyThis, applyArgs) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.invokeTask(target, task, applyThis, applyArgs); + }; + AsyncTestZoneSpec.prototype.onCancelTask = function (delegate, current, target, task) { + if (task.type !== 'eventTask') { + this._isSync = false; + } + return delegate.cancelTask(target, task); + }; + // Note - we need to use onInvoke at the moment to call finish when a test is + // fully synchronous. TODO(juliemr): remove this when the logic for + // onHasTask changes and it calls whenever the task queues are dirty. + // updated by(JiaLiPassion), only call finish callback when no task + // was scheduled/invoked/canceled. + AsyncTestZoneSpec.prototype.onInvoke = function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { + try { + this._isSync = true; + return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); + } + finally { + var afterTaskCounts = parentZoneDelegate._taskCounts; + if (this._isSync) { + this._finishCallbackIfDone(); + } + } + }; + AsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + // Let the parent try to handle the error. + var result = parentZoneDelegate.handleError(targetZone, error); + if (result) { + this.failCallback(error); + this._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(); + } + }; + AsyncTestZoneSpec.symbolParentUnresolved = Zone.__symbol__('parentUnresolved'); + 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 + */ +var __read = (undefined && undefined.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spread = (undefined && undefined.__spread) || function () { + for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); + return ar; +}; +(function (global) { + var OriginalDate = global.Date; + var FakeDate = /** @class */ (function () { + function FakeDate() { + if (arguments.length === 0) { + var d = new OriginalDate(); + d.setTime(FakeDate.now()); + return d; + } + else { + var args = Array.prototype.slice.call(arguments); + return new (OriginalDate.bind.apply(OriginalDate, __spread([void 0], args)))(); + } + } + FakeDate.now = function () { + var fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); + if (fakeAsyncTestZoneSpec) { + return fakeAsyncTestZoneSpec.getCurrentRealTime() + fakeAsyncTestZoneSpec.getCurrentTime(); + } + return OriginalDate.now.apply(this, arguments); + }; + return FakeDate; + }()); + FakeDate.UTC = OriginalDate.UTC; + FakeDate.parse = OriginalDate.parse; + // keep a reference for zone patched timer function + var timers = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval + }; + var Scheduler = /** @class */ (function () { + function Scheduler() { + // Next scheduler id. + this.nextId = 1; + // Scheduler queue with the tuple of end time and callback function - sorted by end time. + this._schedulerQueue = []; + // Current simulated time in millis. + this._currentTime = 0; + // Current real time in millis. + this._currentRealTime = OriginalDate.now(); + } + Scheduler.prototype.getCurrentTime = function () { + return this._currentTime; + }; + Scheduler.prototype.getCurrentRealTime = function () { + return this._currentRealTime; + }; + Scheduler.prototype.setCurrentRealTime = function (realTime) { + this._currentRealTime = realTime; + }; + Scheduler.prototype.scheduleFunction = function (cb, delay, args, isPeriodic, isRequestAnimationFrame, id) { + if (args === void 0) { args = []; } + if (isPeriodic === void 0) { isPeriodic = false; } + if (isRequestAnimationFrame === void 0) { isRequestAnimationFrame = false; } + if (id === void 0) { id = -1; } + var currentId = id < 0 ? this.nextId++ : id; + var endTime = this._currentTime + delay; + // Insert so that scheduler queue remains sorted by end time. + var newEntry = { + endTime: endTime, + id: currentId, + func: cb, + args: args, + delay: delay, + 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 = /** @class */ (function () { + function FakeAsyncTestZoneSpec(namePrefix, trackPendingRequestAnimationFrame, macroTaskOptions) { + if (trackPendingRequestAnimationFrame === void 0) { trackPendingRequestAnimationFrame = false; } + this.trackPendingRequestAnimationFrame = trackPendingRequestAnimationFrame; + this.macroTaskOptions = macroTaskOptions; + this._scheduler = new Scheduler(); + this._microtasks = []; + this._lastError = null; + this._uncaughtPromiseErrors = Promise[Zone.__symbol__('uncaughtPromiseErrors')]; + this.pendingPeriodicTimers = []; + this.pendingTimers = []; + this.patchDateLocked = false; + this.properties = { 'FakeAsyncTestZoneSpec': this }; + this.name = 'fakeAsyncTestZone for ' + namePrefix; + // in case user can't access the construction of FakeAsyncTestSpec + // user can also define macroTaskOptions by define a global variable. + if (!this.macroTaskOptions) { + this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')]; + } + } + FakeAsyncTestZoneSpec.assertInZone = function () { + if (Zone.current.get('FakeAsyncTestZoneSpec') == null) { + 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, args) { + var id = this._scheduler.nextId; + var completers = { onSuccess: null, onError: this._dequeuePeriodicTimer(id) }; + var cb = this._fnAndFlush(fn, completers); + // Use the callback created above to requeue on success. + completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id); + // Queue the callback and dequeue the periodic timer only on error. + this._scheduler.scheduleFunction(cb, interval, args, 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.getCurrentTime = function () { + return this._scheduler.getCurrentTime(); + }; + FakeAsyncTestZoneSpec.prototype.getCurrentRealTime = function () { + return this._scheduler.getCurrentRealTime(); + }; + FakeAsyncTestZoneSpec.prototype.setCurrentRealTime = function (realTime) { + this._scheduler.setCurrentRealTime(realTime); + }; + FakeAsyncTestZoneSpec.patchDate = function () { + if (global['Date'] === FakeDate) { + // already patched + return; + } + global['Date'] = FakeDate; + FakeDate.prototype = OriginalDate.prototype; + // try check and reset timers + // because jasmine.clock().install() may + // have replaced the global timer + FakeAsyncTestZoneSpec.checkTimerPatch(); + }; + FakeAsyncTestZoneSpec.resetDate = function () { + if (global['Date'] === FakeDate) { + global['Date'] = OriginalDate; + } + }; + FakeAsyncTestZoneSpec.checkTimerPatch = function () { + if (global.setTimeout !== timers.setTimeout) { + global.setTimeout = timers.setTimeout; + global.clearTimeout = timers.clearTimeout; + } + if (global.setInterval !== timers.setInterval) { + global.setInterval = timers.setInterval; + global.clearInterval = timers.clearInterval; + } + }; + FakeAsyncTestZoneSpec.prototype.lockDatePatch = function () { + this.patchDateLocked = true; + FakeAsyncTestZoneSpec.patchDate(); + }; + FakeAsyncTestZoneSpec.prototype.unlockDatePatch = function () { + this.patchDateLocked = false; + FakeAsyncTestZoneSpec.resetDate(); + }; + FakeAsyncTestZoneSpec.prototype.tick = function (millis, doTick) { + if (millis === void 0) { millis = 0; } + FakeAsyncTestZoneSpec.assertInZone(); + this.flushMicrotasks(); + this._scheduler.tick(millis, 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 additionalArgs = void 0; + if (args) { + var callbackIndex = task.data.cbIdx; + if (typeof args.length === 'number' && args.length > callbackIndex + 1) { + additionalArgs = Array.prototype.slice.call(args, callbackIndex + 1); + } + } + this._microtasks.push({ + func: task.invoke, + args: additionalArgs, + target: task.data && task.data.target + }); + break; + case 'macroTask': + switch (task.source) { + case 'setTimeout': + task.data['handleId'] = this._setTimeout(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + break; + case 'setImmediate': + task.data['handleId'] = this._setTimeout(task.invoke, 0, Array.prototype.slice.call(task.data['args'], 1)); + break; + case 'setInterval': + task.data['handleId'] = this._setInterval(task.invoke, task.data['delay'], Array.prototype.slice.call(task.data['args'], 2)); + 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: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var args_1 = task.data && task.data['args']; + var delay = args_1 && args_1.length > 1 ? args_1[1] : 0; + var callbackArgs = macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args_1; + if (!!macroTaskOption.isPeriodic) { + // periodic macroTask, use setInterval to simulate + task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs); + task.data.isPeriodic = true; + } + else { + // not periodic, use setTimeout to simulate + task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); + } + break; + } + throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); + } + break; + case 'eventTask': + 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: + // user can define which macroTask they want to support by passing + // macroTaskOptions + var macroTaskOption = this.findMacroTaskOption(task); + if (macroTaskOption) { + var handleId = task.data['handleId']; + return macroTaskOption.isPeriodic ? this._clearInterval(handleId) : + this._clearTimeout(handleId); + } + return delegate.cancelTask(target, task); + } + }; + FakeAsyncTestZoneSpec.prototype.onInvoke = function (delegate, current, target, callback, applyThis, applyArgs, source) { + try { + FakeAsyncTestZoneSpec.patchDate(); + return delegate.invoke(target, callback, applyThis, applyArgs, source); + } + finally { + if (!this.patchDateLocked) { + FakeAsyncTestZoneSpec.resetDate(); + } + } + }; + FakeAsyncTestZoneSpec.prototype.findMacroTaskOption = function (task) { + if (!this.macroTaskOptions) { + return null; + } + for (var i = 0; i < this.macroTaskOptions.length; i++) { + var macroTaskOption = this.macroTaskOptions[i]; + if (macroTaskOption.source === task.source) { + return macroTaskOption; + } + } + return null; + }; + FakeAsyncTestZoneSpec.prototype.onHandleError = function (parentZoneDelegate, currentZone, targetZone, error) { + this._lastError = error; + return false; // Don't propagate error to parent zone. + }; + 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 = /** @class */ (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 = /** @class */ (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; + }; + WtfZoneSpec.forkInstance = wtfEnabled && wtfEvents.createInstance('Zone:fork(ascii zone, ascii newZone)'); + WtfZoneSpec.scheduleInstance = {}; + WtfZoneSpec.cancelInstance = {}; + WtfZoneSpec.invokeScope = {}; + WtfZoneSpec.invokeTaskScope = {}; + return WtfZoneSpec; + }()); + 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 + */ +(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) { + testZone = rootZone.fork(new ProxyZoneSpec()); + }); + this.on('fail', function (test, err) { + var proxyZoneSpec = testZone && testZone.get('ProxyZoneSpec'); + if (proxyZoneSpec && err) { + err.message += proxyZoneSpec.getAndClearPendingTasksInfo(); + } + }); + return originalRun.call(this, fn); + }; + })(Mocha.Runner.prototype.runTest, Mocha.Runner.prototype.run); +})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || 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..3e1e9fbf8 100644 --- a/tests/app/snippets/navigation/page-outlet.ts +++ b/tests/app/snippets/navigation/page-outlet.ts @@ -1,4 +1,3 @@ -import { TestApp, registerTestApp } from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; // >> page-outlet-example @@ -22,8 +21,6 @@ export class PageNavigationApp { public done: Promise; constructor(public router: Router, public appRef: ApplicationRef) { - registerTestApp(PageNavigationApp, this, appRef); - this.done = new Promise((resolve, reject) => { this.router.events.subscribe((e) => { if (e instanceof NavigationStart) { diff --git a/tests/app/snippets/navigation/router-outlet.ts b/tests/app/snippets/navigation/router-outlet.ts index bfd50fafc..df15730bf 100644 --- a/tests/app/snippets/navigation/router-outlet.ts +++ b/tests/app/snippets/navigation/router-outlet.ts @@ -1,4 +1,3 @@ -import {TestApp, registerTestApp} from "../../tests/test-app"; import { ApplicationRef } from "@angular/core"; // >> router-outlet-example import { Component, NgModule } from "@angular/core"; @@ -6,7 +5,7 @@ import { platformNativeScriptDynamic } from "nativescript-angular/platform"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { Router, NavigationStart, NavigationEnd } from "@angular/router"; import { routes } from "./app.routes"; -import {FirstComponent, SecondComponent} from "./navigation-common"; +import { FirstComponent, SecondComponent } from "./navigation-common"; @Component({ selector: "navigation-test", @@ -30,8 +29,6 @@ export class NavigationApp { public done: Promise; constructor(public router: Router, public appRef: ApplicationRef) { - registerTestApp(NavigationApp, this, appRef); - this.done = new Promise((resolve, reject) => { this.router.events.subscribe((e) => { if (e instanceof NavigationStart) { @@ -55,13 +52,13 @@ export class NavigationApp { NativeScriptRouterModule.forRoot(routes) ] }) -export class NavigationAppModule {} +export class NavigationAppModule { } // >> (hide) function start_snippet() { -// << (hide) -platformNativeScriptDynamic().bootstrapModule(NavigationAppModule); -// >> (hide) + // << (hide) + platformNativeScriptDynamic().bootstrapModule(NavigationAppModule); + // >> (hide) } // << (hide) // << router-outlet-example diff --git a/tests/app/tests/detached-loader-tests.ts b/tests/app/tests/detached-loader-tests.ts index 014217e35..e5dbe4ba7 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 { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } 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(nsTestBedBeforeEach([LoaderComponent, LoaderComponentOnPush], [], [], [TestComponent])); + afterEach(nsTestBedAfterEach()); - 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 nsTestBedRender(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 nsTestBedRender(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..7b425343b 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 { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } 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(nsTestBedBeforeEach([ + TestListViewComponent, + TestListViewSelectorComponent, + ItemTemplateComponent + ])); + afterEach(nsTestBedAfterEach(false)); it("setupItemView is called for every item", (done) => { - testApp.loadComponent(TestListViewComponent).then((componentRef) => { - const component = componentRef.instance; - setTimeout(() => { + nsTestBedRender(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); + nsTestBedRender(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 bc815ed4f..1ef92349a 100644 --- a/tests/app/tests/modal-dialog.ts +++ b/tests/app/tests/modal-dialog.ts @@ -1,12 +1,17 @@ // 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 { device, platformNames } from "platform"; + +import { ComponentFixture } from "@angular/core/testing"; +import { nsTestBedRender, nsTestBedAfterEach, nsTestBedBeforeEach } from "nativescript-angular/testing"; +import { NSLocationStrategy } from "nativescript-angular/router/ns-location-strategy"; +import { FrameService } from "nativescript-angular"; +import { FakeFrameService } from "./ns-location-strategy"; const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0; @Component({ @@ -47,20 +52,17 @@ export class SuccessComponent { } describe("modal-dialog", () => { - let testApp: TestApp = null; + beforeEach(nsTestBedBeforeEach( + [FailComponent, SuccessComponent], + [{ provide: FrameService, useValue: new FakeFrameService() }, NSLocationStrategy], + [], + [ModalComponent])); + afterEach(nsTestBedAfterEach()); 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 +71,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; + nsTestBedRender(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; + nsTestBedRender(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; + nsTestBedRender(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 +111,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/ns-location-strategy.ts b/tests/app/tests/ns-location-strategy.ts index 7ed33f074..5519075d1 100644 --- a/tests/app/tests/ns-location-strategy.ts +++ b/tests/app/tests/ns-location-strategy.ts @@ -7,7 +7,7 @@ import { Page } from "ui/page"; import { View } from "ui/core/view"; import { FrameService } from "nativescript-angular/platform-providers"; -class FakeFrameService extends FrameService { +export class FakeFrameService extends FrameService { private frame: Frame; constructor(private backCB?: () => void) { super(); @@ -19,7 +19,7 @@ class FakeFrameService extends FrameService { } } -class FakeFrame extends View implements Frame { +export class FakeFrame extends View implements Frame { backStack: Array; currentPage: Page; currentEntry: NavigationEntry; diff --git a/tests/app/tests/platform-filter-components.ts b/tests/app/tests/platform-filter-components.ts index 5665e1364..d239c495d 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 { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } 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(nsTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.ios)}] + )); + afterEach(nsTestBedAfterEach()); + it("does render ios specific content", () => { + return nsTestBedRender(IosSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(label[text=IOS])") >= 0); + }); + }); + it("does not render android specific content", () => { + return nsTestBedRender(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 nsTestBedRender(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(nsTestBedBeforeEach( + [PlatformSpecificAttributeComponent, AndroidSpecificComponent, IosSpecificComponent], + [{provide: DEVICE, useValue: createDevice(platformNames.android)}] + )); + afterEach(nsTestBedAfterEach()); + + it("does render android specific content", () => { + return nsTestBedRender(AndroidSpecificComponent).then((fixture) => { + const componentRef = fixture.componentRef; + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.isTrue(dumpView(componentRoot, true).indexOf("(label[text=ANDROID])") >= 0); + }); + }); + it("does not render ios specific content", () => { + return nsTestBedRender(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 nsTestBedRender(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 e1df13498..ae6c5346b 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -1,19 +1,28 @@ // make sure you import mocha-config before @angular/core + import { assert } from "./test-config"; -import { Component, ElementRef, Renderer2, NgZone, ViewChild } from "@angular/core"; +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 { TestApp } from "./test-app"; -import { isIOS } from "tns-core-modules/platform"; -import { View, fontInternalProperty, backgroundInternalProperty} from "tns-core-modules/ui/core/view" import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; import { ContentView } from "tns-core-modules/ui/content-view"; +import { NgView, registerElement } from "nativescript-angular/element-registry"; import { Button } from "tns-core-modules/ui/button"; -import { NgView } from "nativescript-angular/element-registry"; -import { registerElement } from "nativescript-angular/element-registry"; -// import * as view from "tns-core-modules/ui/core/view"; +import * as view from "tns-core-modules/ui/core/view"; +import { isIOS } from "tns-core-modules/platform"; +import { View, fontInternalProperty, backgroundInternalProperty } from "tns-core-modules/ui/core/view" +import { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } from "nativescript-angular/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { Observable, ReplaySubject } from "rxjs"; + +@Component({ + template: `` +}) +export class ZonedRenderer { + constructor(public elementRef: ElementRef, public renderer: Renderer2) { } +} @Component({ template: `` @@ -220,10 +229,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); } } @@ -238,55 +247,46 @@ 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(nsTestBedBeforeEach([ + LayoutWithLabel, LabelCmp, LabelContainer, + ProjectableCmp, ProjectionContainer, + StyledLabelCmp, StyledLabelCmp2, + NgIfLabel, NgIfThenElseComponent, NgIfMultiple, + NgIfTwoElements, NgIfMultiple, + NgIfElseComponent, NgIfThenElseComponent, + NgForLabel, ZonedRenderer + ])); + afterEach(nsTestBedAfterEach(false)); it("component with a layout", () => { - return testApp.loadComponent(LayoutWithLabel).then((componentRef) => { + return nsTestBedRender(LayoutWithLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer (stacklayout (label)))", dumpView(componentRoot)); }); }); it("component without a layout", () => { - return testApp.loadComponent(LabelContainer).then((componentRef) => { + return nsTestBedRender(LabelContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer (gridlayout (proxyviewcontainer (label))))", dumpView(componentRoot)); }); }); it("projects content into components", () => { - return testApp.loadComponent(ProjectionContainer).then((componentRef) => { + return nsTestBedRender(ProjectionContainer).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", + "(proxyviewcontainer (gridlayout (proxyviewcontainer (stacklayout (button)))))", dumpView(componentRoot)); }); }); it("applies component styles from single source", () => { - return testApp.loadComponent(StyledLabelCmp).then((componentRef) => { + return nsTestBedRender(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); @@ -294,7 +294,8 @@ describe("Renderer E2E", () => { }); it("applies component styles from multiple sources", () => { - return testApp.loadComponent(StyledLabelCmp2).then((componentRef) => { + return nsTestBedRender(StyledLabelCmp2).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; const layout = (componentRoot).getChildAt(0); @@ -317,15 +318,18 @@ describe("Renderer E2E", () => { done(); }; - testApp.zone.run(() => { - testApp.renderer.listen(view, eventName, callback); + nsTestBedRender(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) => { @@ -337,61 +341,66 @@ describe("Renderer E2E", () => { assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone"); done(); }; + nsTestBedRender(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 nsTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer)", dumpView(componentRoot)); + assert.equal("(proxyviewcontainer)", dumpView(componentRoot)); }); }); it("ngIf show component when true", () => { - return testApp.loadComponent(NgIfLabel).then((componentRef) => { + return nsTestBedRender(NgIfLabel).then((fixture) => { + const componentRef: ComponentRef = fixture.componentRef; const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - testApp.appRef.tick(); - assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot)); + 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 nsTestBedRender(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)))", + "(proxyviewcontainer (stacklayout (label), (button)))", dumpView(componentRoot)); }); }); it("ngIf shows elements in correct order when multiple are rendered and there's *ngIf", () => { - return testApp.loadComponent(NgIfMultiple).then((componentRef) => { + return nsTestBedRender(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 " + - "(Label[text=1]), " + - "(Label[text=2]), " + - "(Label[text=3]), " + - "(Label[text=4]), " + // the content to be conditionally displayed - "(Label[text=5])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=1]), " + + "(label[text=2]), " + + "(label[text=3]), " + + "(label[text=4]), " + // the content to be conditionally displayed + "(label[text=5])" + ")" + ")", dumpView(componentRoot, true)); @@ -399,16 +408,17 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'if' template when condition is true", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nsTestBedRender(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 " + - "(StackLayout " + - "(Label[text=If])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=If])" + ")" + ")", @@ -417,16 +427,17 @@ describe("Renderer E2E", () => { }); it("ngIfElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + return nsTestBedRender(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 " + - "(Label[text=Else])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Else])" + ")" + ")", @@ -435,15 +446,16 @@ describe("Renderer E2E", () => { }); it("ngIfThenElse show 'then' template when condition is true", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nsTestBedRender(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 " + - "(Label[text=Then])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Then])" + ")" + ")", @@ -453,16 +465,17 @@ describe("Renderer E2E", () => { it("ngIfThenElse show 'else' template when condition is false", () => { - return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + return nsTestBedRender(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 " + - "(Label[text=Else])" + + "(proxyviewcontainer " + + "(stacklayout " + + "(label[text=Else])" + ")" + ")", @@ -471,39 +484,42 @@ describe("Renderer E2E", () => { }); it("ngFor creates element for each item", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nsTestBedRender(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]))", + "(proxyviewcontainer (label[text=one]), (label[text=two]), (label[text=three]))", dumpView(componentRoot, true)); }); }); it("ngFor updates when item is removed", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nsTestBedRender(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]))", + "(proxyviewcontainer (label[text=one]), (label[text=three]))", dumpView(componentRoot, true)); }); }); it("ngFor updates when item is inserted", () => { - return testApp.loadComponent(NgForLabel).then((componentRef) => { + return nsTestBedRender(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 " + - "(Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", + "(proxyviewcontainer " + + "(label[text=one]), (label[text=new]), (label[text=two]), (label[text=three]))", dumpView(componentRoot, true)); }); }); @@ -511,20 +527,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(nsTestBedBeforeEach([ZonedRenderer])); + afterEach(nsTestBedAfterEach(false)); + beforeEach(() => { + return nsTestBedRender(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'"); @@ -547,20 +560,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(nsTestBedBeforeEach([ZonedRenderer])); + afterEach(nsTestBedAfterEach(false)); + beforeEach(() => { + return nsTestBedRender(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 =