diff --git a/.travis.yml b/.travis.yml
index 93c1c1f27..1e16c2e58 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,8 +39,12 @@ script:
- node_modules/.bin/karma start karma-build-sauce-mocha.conf.js --single-run
- node_modules/.bin/karma start karma-dist-sauce-selenium3-jasmine.conf.js --single-run
- node_modules/.bin/karma start karma-build-sauce-selenium3-mocha.conf.js --single-run
+ - node_modules/.bin/karma start karma-dist-sauce-jasmine3.conf.js --single-run --errorpolicy=disable
+ - node_modules/.bin/karma start karma-dist-sauce-jasmine3.conf.js --single-run --errorpolicy=lazy
- node_modules/.bin/gulp test/node
- node_modules/.bin/gulp test/bluebird
+ - node_modules/.bin/gulp test/node/disableerror
+ - node_modules/.bin/gulp test/node/lazyerror
- node simple-server.js 2>&1> server.log&
- node ./test/webdriver/test.sauce.js
- yarn add jasmine@3.0.0 jasmine-core@3.0.0 mocha@5.0.1
diff --git a/MODULE.md b/MODULE.md
index 377f66fa9..51519a03e 100644
--- a/MODULE.md
+++ b/MODULE.md
@@ -76,6 +76,57 @@ you can do like this.
```
+- Error
+
+By default, `zone.js/dist/zone-error` will not be loaded for performance concern.
+This package will provide following functionality.
+
+ 1. Error inherit: handle `extend Error` issue.
+ ```
+ class MyError extends Error {}
+ const myError = new MyError();
+ console.log('is MyError instanceof Error', (myError instanceof Error));
+ ```
+
+ without `zone-error` patch, the example above will output `false`, with the patch, the reuslt will be `true`.
+
+ 2. BlacklistZoneStackFrames: remove zone.js stack from `stackTrace`, and add `zone` information. Without this patch, a lot of `zone.js` invocation stack will be shown
+ in stack frames.
+
+ ```
+ at zone.run (polyfill.bundle.js: 3424)
+ at zoneDelegate.invokeTask (polyfill.bundle.js: 3424)
+ at zoneDelegate.runTask (polyfill.bundle.js: 3424)
+ at zone.drainMicroTaskQueue (polyfill.bundle.js: 3424)
+ at a.b.c (vendor.bundle.js: 12345 )
+ at d.e.f (main.bundle.js: 23456)
+ ```
+
+ with this patch, those zone frames will be removed,
+ and the zone information `/` will be added
+
+ ```
+ at a.b.c (vendor.bundle.js: 12345 )
+ at d.e.f (main.bundle.js: 23456 )
+ ```
+
+ The second feature will slow down the `Error` performance, so `zone.js` provide a flag to let you be able to control the behavior.
+ The flag is `__Zone_Error_BlacklistedStackFrames_policy`. And the available options is:
+
+ 1. default: this is the default one, if you load `zone.js/dist/zone-error` without
+ setting the flag, `default` will be used, and `BlackListStackFrames` will be available
+ when `new Error()`, you can get a `error.stack` which is `zone stack free`. But this
+ will slow down `new Error()` a little bit.
+
+ 2. disable: this will disable `BlackListZoneStackFrame` feature, and if you load
+ `zone.js/dist/zone-error`, you will only get a `wrapped Error` which can handle
+ `Error inherit` issue.
+
+ 3. lazy: this is a feature to let you be able to get `BlackListZoneStackFrame` feature,
+ but not impact performance. But as a trade off, you can't get the `zone free stack
+ frames` by access `error.stack`. You can only get it by access `error.zoneAwareStack`.
+
+
- Angular(2+)
Angular uses zone.js to manage async operations and decide when to perform change detection. Thus, in Angular,
diff --git a/gulpfile.js b/gulpfile.js
index 2bab13d6f..aa6e0f9a1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -221,19 +221,23 @@ gulp.task('build/zone-patch-socket-io.min.js', ['compile-esm'], function(cb) {
});
gulp.task('build/zone-patch-promise-testing.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/testing/promise-testing.ts', 'zone-patch-promise-test.js', false, cb);
+ return generateScript(
+ './lib/testing/promise-testing.ts', 'zone-patch-promise-test.js', false, cb);
});
gulp.task('build/zone-patch-promise-testing.min.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/testing/promise-testing.ts', 'zone-patch-promise-test.min.js', true, cb);
+ return generateScript(
+ './lib/testing/promise-testing.ts', 'zone-patch-promise-test.min.js', true, cb);
});
gulp.task('build/zone-patch-resize-observer.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.js', false, cb);
+ return generateScript(
+ './lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.js', false, cb);
});
gulp.task('build/zone-patch-resize-observer.min.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.min.js', true, cb);
+ return generateScript(
+ './lib/browser/webapis-resize-observer.ts', 'zone-patch-resize-observer.min.js', true, cb);
});
gulp.task('build/bluebird.js', ['compile-esm'], function(cb) {
@@ -245,11 +249,11 @@ gulp.task('build/bluebird.min.js', ['compile-esm'], function(cb) {
});
gulp.task('build/zone-patch-jsonp.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/extra/jsonp.ts', 'zone-patch-jsonp.js', false, cb);
+ return generateScript('./lib/extra/jsonp.ts', 'zone-patch-jsonp.js', false, cb);
});
gulp.task('build/zone-patch-jsonp.min.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/extra/jsonp.ts', 'zone-patch-jsonp.min.js', true, cb);
+ return generateScript('./lib/extra/jsonp.ts', 'zone-patch-jsonp.min.js', true, cb);
});
gulp.task('build/jasmine-patch.js', ['compile-esm'], function(cb) {
@@ -323,11 +327,13 @@ gulp.task('build/rxjs.min.js', ['compile-esm'], function(cb) {
});
gulp.task('build/rxjs-fake-async.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/rxjs/rxjs-fake-async.ts', 'zone-patch-rxjs-fake-async.js', false, cb);
+ return generateScript(
+ './lib/rxjs/rxjs-fake-async.ts', 'zone-patch-rxjs-fake-async.js', false, cb);
});
gulp.task('build/rxjs-fake-async.min.js', ['compile-esm'], function(cb) {
- return generateScript('./lib/rxjs/rxjs-fake-async.ts', 'zone-patch-rxjs-fake-async.min.js', true, cb);
+ return generateScript(
+ './lib/rxjs/rxjs-fake-async.ts', 'zone-patch-rxjs-fake-async.min.js', true, cb);
});
gulp.task('build/closure.js', function() {
@@ -427,6 +433,18 @@ gulp.task('test/bluebird', ['compile-node'], function(cb) {
nodeTest(specFiles, cb);
});
+gulp.task('test/node/disableerror', ['compile-node'], function(cb) {
+ process.env.errorpolicy = 'disable';
+ var specFiles = ['build/test/node_error_entry_point.js'];
+ nodeTest(specFiles, cb);
+});
+
+gulp.task('test/node/lazyerror', ['compile-node'], function(cb) {
+ process.env.errorpolicy = 'lazy';
+ var specFiles = ['build/test/node_error_entry_point.js'];
+ nodeTest(specFiles, cb);
+});
+
// Check the coding standards and programming errors
gulp.task('lint', () => {
const tslint = require('gulp-tslint');
diff --git a/karma-base.conf.js b/karma-base.conf.js
index 26ffa771a..8a5a7fd2e 100644
--- a/karma-base.conf.js
+++ b/karma-base.conf.js
@@ -6,40 +6,35 @@
* found in the LICENSE file at https://angular.io/license
*/
-module.exports = function (config) {
+module.exports = function(config) {
config.set({
basePath: '',
+ client: {errorpolicy: config.errorpolicy},
files: [
- 'node_modules/systemjs/dist/system-polyfills.js',
- 'node_modules/systemjs/dist/system.src.js',
+ 'node_modules/systemjs/dist/system-polyfills.js', 'node_modules/systemjs/dist/system.src.js',
'node_modules/whatwg-fetch/fetch.js',
- {pattern: 'node_modules/rxjs/**/**/*.js', included: false, watched: false },
- {pattern: 'node_modules/rxjs/**/**/*.js.map', included: false, watched: false },
- {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
- {pattern: 'node_modules/es6-promise/**/*.js', included: false, watched: false },
- {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
+ {pattern: 'node_modules/rxjs/**/**/*.js', included: false, watched: false},
+ {pattern: 'node_modules/rxjs/**/**/*.js.map', included: false, watched: false},
+ {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
+ {pattern: 'node_modules/es6-promise/**/*.js', included: false, watched: false},
+ {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},
{pattern: 'test/assets/**/*.*', watched: true, served: true, included: false},
{pattern: 'build/**/*.js.map', watched: true, served: true, included: false},
{pattern: 'build/**/*.js', watched: true, served: true, included: false}
],
plugins: [
- require('karma-chrome-launcher'),
- require('karma-firefox-launcher'),
+ require('karma-chrome-launcher'), require('karma-firefox-launcher'),
require('karma-sourcemap-loader')
],
- preprocessors: {
- '**/*.js': ['sourcemap']
- },
+ preprocessors: {'**/*.js': ['sourcemap']},
- exclude: [
- 'test/microtasks.spec.ts'
- ],
+ exclude: ['test/microtasks.spec.ts'],
reporters: ['progress'],
- //port: 9876,
+ // port: 9876,
colors: true,
logLevel: config.LOG_INFO,
diff --git a/karma-build.conf.js b/karma-build.conf.js
index 87c87a5ea..aa2d3113c 100644
--- a/karma-build.conf.js
+++ b/karma-build.conf.js
@@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
-module.exports = function (config) {
+module.exports = function(config) {
require('./karma-base.conf.js')(config);
config.files.push('build/test/wtf_mock.js');
config.files.push('build/test/test_fake_polyfill.js');
config.files.push('build/lib/zone.js');
config.files.push('build/lib/common/promise.js');
- config.files.push('build/lib/common/error-rewrite.js');
config.files.push('build/test/main.js');
};
diff --git a/karma-dist.conf.js b/karma-dist.conf.js
index a96fe0993..5843b478d 100644
--- a/karma-dist.conf.js
+++ b/karma-dist.conf.js
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
-module.exports = function (config) {
+module.exports = function(config) {
require('./karma-base.conf.js')(config);
config.files.push('build/test/wtf_mock.js');
config.files.push('build/test/test_fake_polyfill.js');
diff --git a/lib/common/error-rewrite.ts b/lib/common/error-rewrite.ts
index dcd5b3970..4bcb32176 100644
--- a/lib/common/error-rewrite.ts
+++ b/lib/common/error-rewrite.ts
@@ -46,10 +46,71 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
// We must find the frame where Error was created, otherwise we assume we don't understand stack
let zoneAwareFrame1: string;
let zoneAwareFrame2: string;
+ let zoneAwareFrame1WithoutNew: string;
+ let zoneAwareFrame2WithoutNew: string;
+ let zoneAwareFrame3WithoutNew: string;
global['Error'] = ZoneAwareError;
const stackRewrite = 'stackRewrite';
+ type BlackListedStackFramesPolicy = 'default'|'disable'|'lazy';
+ const blackListedStackFramesPolicy: BlackListedStackFramesPolicy =
+ global['__Zone_Error_BlacklistedStackFrames_policy'] || 'default';
+
+ interface ZoneFrameName {
+ zoneName: string;
+ parent?: ZoneFrameName;
+ }
+
+ function buildZoneFrameNames(zoneFrame: _ZoneFrame) {
+ let zoneFrameName: ZoneFrameName = {zoneName: zoneFrame.zone.name};
+ let result = zoneFrameName;
+ while (zoneFrame.parent) {
+ zoneFrame = zoneFrame.parent;
+ const parentZoneFrameName = {zoneName: zoneFrame.zone.name};
+ zoneFrameName.parent = parentZoneFrameName;
+ zoneFrameName = parentZoneFrameName;
+ }
+ return result;
+ }
+
+ function buildZoneAwareStackFrames(
+ originalStack: string, zoneFrame: _ZoneFrame|ZoneFrameName, isZoneFrame = true) {
+ let frames: string[] = originalStack.split('\n');
+ let i = 0;
+ // Find the first frame
+ while (!(frames[i] === zoneAwareFrame1 || frames[i] === zoneAwareFrame2 ||
+ frames[i] === zoneAwareFrame1WithoutNew || frames[i] === zoneAwareFrame2WithoutNew ||
+ frames[i] === zoneAwareFrame3WithoutNew) &&
+ i < frames.length) {
+ i++;
+ }
+ for (; i < frames.length && zoneFrame; i++) {
+ let frame = frames[i];
+ if (frame.trim()) {
+ switch (blackListedStackFrames[frame]) {
+ case FrameType.blackList:
+ frames.splice(i, 1);
+ i--;
+ break;
+ case 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.splice(i, 1);
+ i--;
+ break;
+ default:
+ frames[i] += isZoneFrame ? ` [${(zoneFrame as _ZoneFrame).zone.name}]` :
+ ` [${(zoneFrame as ZoneFrameName).zoneName}]`;
+ }
+ }
+ }
+ return frames.join('\n');
+ }
/**
* This is ZoneAwareError which processes the stack frame and cleans up extra frames as well as
* adds zone information to it.
@@ -62,42 +123,17 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
// Process the stack trace and rewrite the frames.
if ((ZoneAwareError as any)[stackRewrite] && originalStack) {
- let frames: string[] = originalStack.split('\n');
let zoneFrame = api.currentZoneFrame();
- let i = 0;
- // Find the first frame
- while (!(frames[i] === zoneAwareFrame1 || frames[i] === zoneAwareFrame2) &&
- i < frames.length) {
- i++;
- }
- for (; i < frames.length && zoneFrame; i++) {
- let frame = frames[i];
- if (frame.trim()) {
- switch (blackListedStackFrames[frame]) {
- case FrameType.blackList:
- frames.splice(i, 1);
- i--;
- break;
- case 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.splice(i, 1);
- i--;
- break;
- default:
- frames[i] += ` [${zoneFrame.zone.name}]`;
- }
+ if (blackListedStackFramesPolicy === 'lazy') {
+ // don't handle stack trace now
+ (error as any)[api.symbol('zoneFrameNames')] = buildZoneFrameNames(zoneFrame);
+ } else {
+ try {
+ error.stack = error.zoneAwareStack = buildZoneAwareStackFrames(originalStack, zoneFrame);
+ } catch (e) {
+ // ignore as some browsers don't allow overriding of stack
}
}
- try {
- error.stack = error.zoneAwareStack = frames.join('\n');
- } catch (e) {
- // ignore as some browsers don't allow overriding of stack
- }
}
if (this instanceof NativeError && this.constructor != NativeError) {
@@ -123,6 +159,29 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
(ZoneAwareError as any)[blacklistedStackFramesSymbol] = blackListedStackFrames;
(ZoneAwareError as any)[stackRewrite] = false;
+ const zoneAwareStackSymbol = api.symbol('zoneAwareStack');
+
+ // try to define zoneAwareStack property when blackListed
+ // policy is delay
+ if (blackListedStackFramesPolicy === 'lazy') {
+ Object.defineProperty(ZoneAwareError.prototype, 'zoneAwareStack', {
+ configurable: true,
+ enumerable: true,
+ get: function() {
+ if (!this[zoneAwareStackSymbol]) {
+ this[zoneAwareStackSymbol] = buildZoneAwareStackFrames(
+ this.originalStack, this[api.symbol('zoneFrameNames')], false);
+ }
+ return this[zoneAwareStackSymbol];
+ },
+ set: function(newStack: string) {
+ this.originalStack = newStack;
+ this[zoneAwareStackSymbol] = buildZoneAwareStackFrames(
+ this.originalStack, this[api.symbol('zoneFrameNames')], false);
+ }
+ });
+ }
+
// those properties need special handling
const specialPropertyNames = ['stackTraceLimit', 'captureStackTrace', 'prepareStackTrace'];
// those properties of NativeError should be set to ZoneAwareError
@@ -194,65 +253,71 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
}
});
+ if (blackListedStackFramesPolicy === 'disable') {
+ // don't need to run detectZone to populate
+ // blacklisted stack frames
+ return;
+ }
// Now we need to populate the `blacklistedStackFrames` as well as find the
// 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.
- const ZONE_AWARE_ERROR = 'ZoneAwareError';
- const ERROR_DOT = 'Error.';
- const EMPTY = '';
- const RUN_GUARDED = 'runGuarded';
- const RUN_TASK = 'runTask';
- const RUN = 'run';
- const BRACKETS = '(';
- const AT = '@';
let detectZone: Zone = Zone.current.fork({
name: 'detect',
- onHandleError: function(parentZD: ZoneDelegate, current: Zone, target: Zone, error: any):
- boolean {
- if (error.originalStack && Error === ZoneAwareError) {
- let frames = error.originalStack.split(/\n/);
- let runFrame = false, runGuardedFrame = false, runTaskFrame = false;
- while (frames.length) {
- let 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
- // 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
- // Chrome: at Zone.run (zone.js:100)
- // Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24)
- // FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24
- // Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24
- let fnName: string = frame.split(BRACKETS)[0].split(AT)[0];
- let frameType = FrameType.transition;
- if (fnName.indexOf(ZONE_AWARE_ERROR) !== -1) {
- zoneAwareFrame1 = frame;
- zoneAwareFrame2 = frame.replace(ERROR_DOT, EMPTY);
- blackListedStackFrames[zoneAwareFrame2] = FrameType.blackList;
- }
- if (fnName.indexOf(RUN_GUARDED) !== -1) {
- runGuardedFrame = true;
- } else if (fnName.indexOf(RUN_TASK) !== -1) {
- runTaskFrame = true;
- } else if (fnName.indexOf(RUN) !== -1) {
- runFrame = true;
- } else {
- frameType = FrameType.blackList;
- }
- blackListedStackFrames[frame] = frameType;
- // Once we find all of the frames we can stop looking.
- if (runFrame && runGuardedFrame && runTaskFrame) {
- (ZoneAwareError as any)[stackRewrite] = true;
- break;
+ onHandleError: function(
+ parentZD: ZoneDelegate, current: Zone, target: Zone, error: any): boolean {
+ if (error.originalStack && Error === ZoneAwareError) {
+ let frames = error.originalStack.split(/\n/);
+ let runFrame = false, runGuardedFrame = false, runTaskFrame = false;
+ while (frames.length) {
+ let 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
+ // line number or exact equals to `ZoneAwareError`)
+ if (/:\d+:\d+/.test(frame) || frame === 'ZoneAwareError') {
+ // Get rid of the path so that we don't accidentally find function name in path.
+ // In chrome the separator is `(` and `@` in FF and safari
+ // Chrome: at Zone.run (zone.js:100)
+ // Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24)
+ // FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24
+ // Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24
+ let fnName: string = frame.split('(')[0].split('@')[0];
+ let frameType = FrameType.transition;
+ if (fnName.indexOf('ZoneAwareError') !== -1) {
+ if (fnName.indexOf('new ZoneAwareError') !== -1) {
+ zoneAwareFrame1 = frame;
+ zoneAwareFrame2 = frame.replace('new ZoneAwareError', 'new Error.ZoneAwareError');
+ } else {
+ zoneAwareFrame1WithoutNew = frame;
+ zoneAwareFrame2WithoutNew = frame.replace('Error.', '');
+ if (frame.indexOf('Error.ZoneAwareError') === -1) {
+ zoneAwareFrame3WithoutNew =
+ frame.replace('ZoneAwareError', 'Error.ZoneAwareError');
}
}
+ blackListedStackFrames[zoneAwareFrame2] = FrameType.blackList;
+ }
+ if (fnName.indexOf('runGuarded') !== -1) {
+ runGuardedFrame = true;
+ } else if (fnName.indexOf('runTask') !== -1) {
+ runTaskFrame = true;
+ } else if (fnName.indexOf('run') !== -1) {
+ runFrame = true;
+ } else {
+ frameType = FrameType.blackList;
+ }
+ blackListedStackFrames[frame] = frameType;
+ // Once we find all of the frames we can stop looking.
+ if (runFrame && runGuardedFrame && runTaskFrame) {
+ (ZoneAwareError as any)[stackRewrite] = true;
+ break;
}
}
- return false;
}
+ }
+ return false;
+ }
}) as Zone;
// carefully constructor a stack frame which contains all of the frames of interest which
// need to be detected and blacklisted.
@@ -293,7 +358,17 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
childDetectZone.scheduleMicroTask(
blacklistedStackFramesSymbol,
() => {
- throw new (ZoneAwareError as any)(ZoneAwareError, NativeError);
+ throw new Error();
+ },
+ null,
+ (t: Task) => {
+ (t as any)._transitionTo = fakeTransitionTo;
+ t.invoke();
+ });
+ childDetectZone.scheduleMicroTask(
+ blacklistedStackFramesSymbol,
+ () => {
+ throw Error();
},
null,
(t: Task) => {
@@ -316,5 +391,6 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
() => {});
});
});
+
Error.stackTraceLimit = originalStackTraceLimit;
});
diff --git a/test/common/Error.spec.ts b/test/common/Error.spec.ts
index 1f4727b8d..52195bc62 100644
--- a/test/common/Error.spec.ts
+++ b/test/common/Error.spec.ts
@@ -78,7 +78,15 @@ class TestMessageError extends WrappedError {
describe('ZoneAwareError', () => {
// If the environment does not supports stack rewrites, then these tests will fail
// and there is no point in running them.
- if (!(Error as any)['stackRewrite']) return;
+ const _global: any = typeof window !== 'undefined' ? window : global;
+ let config: any;
+ if (typeof __karma__ !== 'undefined') {
+ config = __karma__ && (__karma__ as any).config;
+ } else if (typeof process !== 'undefined') {
+ config = process.env;
+ }
+ const policy = (config && config['errorpolicy']) || 'default';
+ if (!(Error as any)['stackRewrite'] && policy !== 'disable') return;
it('should keep error prototype chain correctly', () => {
class MyError extends Error {}
@@ -180,6 +188,9 @@ describe('ZoneAwareError', () => {
});
it('should show zone names in stack frames and remove extra frames', () => {
+ if (policy === 'disable' || !(Error as any)['stackRewrite']) {
+ return;
+ }
const rootZone = Zone.root;
const innerZone = rootZone.fork({name: 'InnerZone'});
@@ -212,6 +223,13 @@ describe('ZoneAwareError', () => {
}
});
+ if (policy === 'lazy') {
+ outside.stack = outside.zoneAwareStack;
+ outsideWithoutNew.stack = outsideWithoutNew.zoneAwareStack;
+ inside.stack = inside.zoneAwareStack;
+ insideWithoutNew.stack = insideWithoutNew.zoneAwareStack;
+ }
+
expect(outside.stack).toEqual(outside.zoneAwareStack);
expect(outsideWithoutNew.stack).toEqual(outsideWithoutNew.zoneAwareStack);
expect(inside.stack).toEqual(inside.zoneAwareStack);
@@ -251,7 +269,6 @@ describe('ZoneAwareError', () => {
if (/Error /.test(insideWithoutNewFrames[0])) {
insideWithoutNewFrames.shift();
}
-
expect(outsideFrames[0]).toMatch(/testFn.*[]/);
expect(insideFrames[0]).toMatch(/insideRun.*[InnerZone]]/);
@@ -267,13 +284,28 @@ describe('ZoneAwareError', () => {
const zoneAwareFrames = [
'Zone.run', 'Zone.runGuarded', 'Zone.scheduleEventTask', 'Zone.scheduleMicroTask',
'Zone.scheduleMacroTask', 'Zone.runTask', 'ZoneDelegate.scheduleTask',
- 'ZoneDelegate.invokeTask', 'zoneAwareAddListener'
+ 'ZoneDelegate.invokeTask', 'zoneAwareAddListener', 'Zone.prototype.run',
+ 'Zone.prototype.runGuarded', 'Zone.prototype.scheduleEventTask',
+ 'Zone.prototype.scheduleMicroTask', 'Zone.prototype.scheduleMacroTask',
+ 'Zone.prototype.runTask', 'ZoneDelegate.prototype.scheduleTask',
+ 'ZoneDelegate.prototype.invokeTask', 'ZoneTask.invokeTask'
];
function assertStackDoesNotContainZoneFrames(err: Error) {
- const frames = err.stack.split('\n');
- for (let i = 0; i < frames.length; i++) {
- expect(zoneAwareFrames.filter(f => frames[i].indexOf(f) !== -1)).toEqual([]);
+ const frames = policy === 'lazy' ? err.zoneAwareStack.split('\n') : err.stack.split('\n');
+ if (policy === 'disable') {
+ let hasZoneStack = false;
+ for (let i = 0; i < frames.length; i++) {
+ if (hasZoneStack) {
+ break;
+ }
+ hasZoneStack = zoneAwareFrames.filter(f => frames[i].indexOf(f) !== -1).length > 0;
+ }
+ expect(hasZoneStack).toBe(true);
+ } else {
+ for (let i = 0; i < frames.length; i++) {
+ expect(zoneAwareFrames.filter(f => frames[i].indexOf(f) !== -1)).toEqual([]);
+ }
}
};
diff --git a/test/main.ts b/test/main.ts
index f8c33f3ea..6a895c789 100644
--- a/test/main.ts
+++ b/test/main.ts
@@ -13,6 +13,14 @@ declare const __karma__: {
};
__karma__.loaded = function() {};
+
+if (typeof __karma__ !== 'undefined') {
+ (window as any)['__Zone_Error_BlacklistedStackFrames_policy'] =
+ (__karma__ as any).config.errorpolicy;
+} else if (typeof process !== 'undefined') {
+ (window as any)['__Zone_Error_BlacklistedStackFrames_policy'] = process.env.errorpolicy;
+}
+
(window as any).global = window;
System.config({
defaultJSExtensions: true,
@@ -37,13 +45,15 @@ browserPatchedPromise.then(() => {
'/base/build/test/test-env-setup-jasmine';
// Setup test environment
System.import(testFrameworkPatch).then(() => {
- System.import('/base/build/test/browser_entry_point')
- .then(
- () => {
- __karma__.start();
- },
- (error) => {
- console.error(error.stack || error);
- });
+ System.import('/base/build/lib/common/error-rewrite').then(() => {
+ System.import('/base/build/test/browser_entry_point')
+ .then(
+ () => {
+ __karma__.start();
+ },
+ (error) => {
+ console.error(error.stack || error);
+ });
+ });
});
});
diff --git a/test/node_entry_point.ts b/test/node_entry_point.ts
index 27ba367ca..fb7c5dcf9 100644
--- a/test/node_entry_point.ts
+++ b/test/node_entry_point.ts
@@ -14,6 +14,15 @@ import './test_fake_polyfill';
import '../lib/zone';
import '../lib/common/promise';
import '../lib/common/to-string';
+
+if (typeof __karma__ !== 'undefined') {
+ (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] =
+ (__karma__ as any).config.errorpolicy;
+} else if (typeof process !== 'undefined') {
+ (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] = process.env.errorpolicy;
+}
+
+import '../lib/common/error-rewrite';
import '../lib/node/node';
import '../lib/zone-spec/async-test';
import '../lib/zone-spec/fake-async-test';
diff --git a/test/node_error_entry_point.ts b/test/node_error_entry_point.ts
new file mode 100644
index 000000000..f70682ed9
--- /dev/null
+++ b/test/node_error_entry_point.ts
@@ -0,0 +1,41 @@
+/**
+ * @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
+ */
+
+// Must be loaded before zone loads, so that zone can detect WTF.
+import './wtf_mock';
+import './test_fake_polyfill';
+
+// Setup tests for Zone without microtask support
+import '../lib/zone';
+import '../lib/common/promise';
+import '../lib/common/to-string';
+
+if (typeof __karma__ !== 'undefined') {
+ (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] =
+ (__karma__ as any).config.errorpolicy;
+} else if (typeof process !== 'undefined') {
+ (global as any)['__Zone_Error_BlacklistedStackFrames_policy'] = process.env.errorpolicy;
+}
+
+import '../lib/common/error-rewrite';
+import '../lib/node/node';
+import '../lib/zone-spec/async-test';
+import '../lib/zone-spec/fake-async-test';
+import '../lib/zone-spec/long-stack-trace';
+import '../lib/zone-spec/proxy';
+import '../lib/zone-spec/sync-test';
+import '../lib/zone-spec/task-tracking';
+import '../lib/zone-spec/wtf';
+import '../lib/rxjs/rxjs';
+
+import '../lib/testing/promise-testing';
+// Setup test environment
+import './test-env-setup-jasmine';
+
+// List all tests here:
+import './common/Error.spec';