feat(errorHandlingConfig): add option to exclude error params from url

Specific errors, such as those during nested module loading, can create very long
error urls because the error message includes the error stack. These urls create visual
clutter in the browser console, are often not clickable, and may be rejected
by the docs page because they are simply too long.

We've already made improvements to the error display in #16283, which excludes
the error url from error parameters, which results in cleaner error messages.

Further, modern browsers restrict console message length intelligently.

This option can still be useful for older browsers like Internet Explorer, or
in general to reduce visual clutter in the console.

Closes #14744
Closes #15707
Closes #16283
Closes #16299 
Closes #16591
This commit is contained in:
Martin Staffa
2018-06-06 15:03:09 +02:00
committed by GitHub
parent 0ed36430da
commit 03a4782a35
2 changed files with 65 additions and 20 deletions
+16 -3
View File
@@ -7,7 +7,8 @@
*/
var minErrConfig = {
objectMaxDepth: 5
objectMaxDepth: 5,
urlErrorParamsEnabled: true
};
/**
@@ -30,12 +31,21 @@ var minErrConfig = {
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
*
* * `urlErrorParamsEnabled` **{Boolean}** - Specifies wether the generated error url will
* contain the parameters of the thrown error. Disabling the parameters can be useful if the
* generated error url is very long.
*
* Default: true. When used without argument, it returns the current value.
*/
function errorHandlingConfig(config) {
if (isObject(config)) {
if (isDefined(config.objectMaxDepth)) {
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
}
if (isDefined(config.urlErrorParamsEnabled) && isBoolean(config.urlErrorParamsEnabled)) {
minErrConfig.urlErrorParamsEnabled = config.urlErrorParamsEnabled;
}
} else {
return minErrConfig;
}
@@ -50,6 +60,7 @@ function isValidObjectMaxDepth(maxDepth) {
return isNumber(maxDepth) && maxDepth > 0;
}
/**
* @description
*
@@ -113,8 +124,10 @@ function minErr(module, ErrorConstructor) {
message += '\n' + url + (module ? module + '/' : '') + code;
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
if (minErrConfig.urlErrorParamsEnabled) {
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
}
return new ErrorConstructor(message);
+49 -17
View File
@@ -2,32 +2,57 @@
describe('errors', function() {
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
var originalUrlErrorParamsEnabled = minErrConfig.urlErrorParamsEnabled;
afterEach(function() {
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
minErrConfig.urlErrorParamsEnabled = originalUrlErrorParamsEnabled;
});
describe('errorHandlingConfig', function() {
it('should get default objectMaxDepth', function() {
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
describe('objectMaxDepth',function() {
it('should get default objectMaxDepth', function() {
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
});
it('should set objectMaxDepth', function() {
errorHandlingConfig({objectMaxDepth: 3});
expect(errorHandlingConfig().objectMaxDepth).toBe(3);
});
it('should not change objectMaxDepth when undefined is supplied', function() {
errorHandlingConfig({objectMaxDepth: undefined});
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
});
they('should set objectMaxDepth to NaN when $prop is supplied',
[NaN, null, true, false, -1, 0], function(maxDepth) {
errorHandlingConfig({objectMaxDepth: maxDepth});
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
}
);
});
it('should set objectMaxDepth', function() {
errorHandlingConfig({objectMaxDepth: 3});
expect(errorHandlingConfig().objectMaxDepth).toBe(3);
describe('urlErrorParamsEnabled',function() {
it('should get default urlErrorParamsEnabled', function() {
expect(errorHandlingConfig().urlErrorParamsEnabled).toBe(true);
});
it('should set urlErrorParamsEnabled', function() {
errorHandlingConfig({urlErrorParamsEnabled: false});
expect(errorHandlingConfig().urlErrorParamsEnabled).toBe(false);
errorHandlingConfig({urlErrorParamsEnabled: true});
expect(errorHandlingConfig().urlErrorParamsEnabled).toBe(true);
});
it('should not change its value when non-boolean is supplied', function() {
errorHandlingConfig({urlErrorParamsEnabled: 123});
expect(errorHandlingConfig().urlErrorParamsEnabled).toBe(originalUrlErrorParamsEnabled);
});
});
it('should not change objectMaxDepth when undefined is supplied', function() {
errorHandlingConfig({objectMaxDepth: undefined});
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
});
they('should set objectMaxDepth to NaN when $prop is supplied',
[NaN, null, true, false, -1, 0], function(maxDepth) {
errorHandlingConfig({objectMaxDepth: maxDepth});
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
}
);
});
describe('minErr', function() {
@@ -165,7 +190,6 @@ describe('errors', function() {
.toMatch(/^[\s\S]*\?p0=a&p1=b&p2=value%20with%20space$/);
});
it('should strip error reference urls from the error message parameters', function() {
var firstError = testError('firstcode', 'longer string and so on');
@@ -177,5 +201,13 @@ describe('errors', function() {
'%3A%2F%2Ferrors.angularjs.org%2F%22NG_VERSION_FULL%22%2Ftest%2Ffirstcode');
});
it('should not generate URL query parameters when urlErrorParamsEnabled is false', function() {
errorHandlingConfig({urlErrorParamsEnabled: false});
expect(testError('acode', 'aproblem', 'a', 'b', 'c').message).toBe('[test:acode] aproblem\n' +
'https://errors.angularjs.org/"NG_VERSION_FULL"/test/acode');
});
});
});