Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

feat(minerr): log minerr doc url in development #3566

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/minErr.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@

function minErr(module) {
return function () {
var prefix = '[' + (module ? module + ':' : '') + arguments[0] + '] ',
var code = arguments[0],
prefix = '[' + (module ? module + ':' : '') + code + '] ',
template = arguments[1],
templateArgs = arguments,
message;
stringify = function (obj) {
if (isFunction(obj)) {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (isUndefined(obj)) {
return 'undefined';
} else if (!isString(obj)) {
return JSON.stringify(obj);
}
return obj;
},
message, i;

message = prefix + template.replace(/\{\d+\}/g, function (match) {
var index = +match.slice(1, -1), arg;
Expand All @@ -52,6 +63,13 @@ function minErr(module) {
return match;
});

message = message + '\nhttp://errors.angularjs.org/' + version.full + '/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
encodeURIComponent(stringify(arguments[i]));
}

return new Error(message);
};
}
10 changes: 5 additions & 5 deletions test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,20 @@ describe('angular', function() {

it('should throw an exception if a Scope is being copied', inject(function($rootScope) {
expect(function() { copy($rootScope.$new()); }).
toThrow("[ng:cpws] Can't copy! Making copies of Window or Scope instances is not supported.");
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
}));

it('should throw an exception if a Window is being copied', function() {
expect(function() { copy(window); }).
toThrow("[ng:cpws] Can't copy! Making copies of Window or Scope instances is not supported.");
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
});

it('should throw an exception when source and destination are equivalent', function() {
var src, dst;
src = dst = {key: 'value'};
expect(function() { copy(src, dst); }).toThrow("[ng:cpi] Can't copy! Source and destination are identical.");
expect(function() { copy(src, dst); }).toThrowMinErr("ng", "cpi", "Can't copy! Source and destination are identical.");
src = dst = [2, 4];
expect(function() { copy(src, dst); }).toThrow("[ng:cpi] Can't copy! Source and destination are identical.");
expect(function() { copy(src, dst); }).toThrowMinErr("ng", "cpi", "Can't copy! Source and destination are identical.");
});

it('should not copy the private $$hashKey', function() {
Expand Down Expand Up @@ -901,7 +901,7 @@ describe('angular', function() {

expect(function() {
element.injector().get('foo');
}).toThrow('[$injector:unpr] Unknown provider: fooProvider <- foo');
}).toThrowMinErr('$injector', 'unpr', 'Unknown provider: fooProvider <- foo');

expect(element.injector().get('$http')).toBeDefined();
});
Expand Down
2 changes: 1 addition & 1 deletion test/BinderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ describe('Binder', function() {
$rootScope.error['throw'] = function() {throw 'MyError';};
errorLogs.length = 0;
$rootScope.$apply();
expect(errorLogs.shift().message).toBe("[$interpolate:interr] Can't interpolate: {{error.throw()}}\nMyError");
expect(errorLogs.shift().message).toMatch(/^\[\$interpolate:interr\] Can't interpolate: \{\{error.throw\(\)\}\}\nMyError/);

$rootScope.error['throw'] = function() {return 'ok';};
$rootScope.$apply();
Expand Down
39 changes: 19 additions & 20 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('injector', function() {
it('should provide useful message if no provider', function() {
expect(function() {
injector.get('idontexist');
}).toThrow("[$injector:unpr] Unknown provider: idontexistProvider <- idontexist");
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
});


Expand All @@ -79,7 +79,7 @@ describe('injector', function() {
providers('b', function(a) {return 2;});
expect(function() {
injector.get('b');
}).toThrow("[$injector:unpr] Unknown provider: idontexistProvider <- idontexist <- a <- b");
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- a <- b");
});


Expand Down Expand Up @@ -127,10 +127,10 @@ describe('injector', function() {
it('should fail with errors if not function or array', function() {
expect(function() {
injector.invoke({});
}).toThrow("[ng:areq] Argument 'fn' is not a function, got Object");
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got Object");
expect(function() {
injector.invoke(['a', 123], {});
}).toThrow("[ng:areq] Argument 'fn' is not a function, got number");
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got number");
});
});

Expand Down Expand Up @@ -268,9 +268,8 @@ describe('injector', function() {
it('should error on invalid module name', function() {
expect(function() {
createInjector(['IDontExist'], {});
}).toThrowMatching(
/\[\$injector:modulerr\].+\n.*\[\$injector:nomod] Module 'IDontExist' is not available! You either misspelled the module name or forgot to load it/
);
}).toThrowMinErr('$injector', 'modulerr',
/\[\$injector:nomod\] Module 'IDontExist' is not available! You either misspelled the module name or forgot to load it/);
});


Expand Down Expand Up @@ -553,7 +552,7 @@ describe('injector', function() {
createInjector([
{}
], {});
}).toThrowMatching(/\[\$injector:modulerr\] Failed to instantiate module {} due to:\n.*\[ng\:areq] Argument 'module' is not a function, got Object/);
}).toThrowMinErr('$injector', 'modulerr', /Failed to instantiate module \{\} due to:\n.*\[ng:areq\] Argument 'module' is not a function, got Object/);
});


Expand All @@ -562,16 +561,16 @@ describe('injector', function() {
createInjector([function() {
throw 'MyError';
}], {});
}).toThrowMatching(/\[\$injector:modulerr\] Failed to instantiate module .+ due to:\n.*MyError/);
}).toThrowMinErr('$injector', 'modulerr', /Failed to instantiate module .+ due to:\n.*MyError/);
});


it('should decorate the missing service error with module name', function() {
angular.module('TestModule', [], function(xyzzy) {});
expect(function() {
createInjector(['TestModule' ]);
}).toThrowMatching(
/\[\$injector:modulerr\] Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
}).toThrowMinErr(
'$injector', 'modulerr', /Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
);
});

Expand All @@ -580,8 +579,8 @@ describe('injector', function() {
function myModule(xyzzy){}
expect(function() {
createInjector([myModule]);
}).toThrowMatching(
/\[\$injector:modulerr\] Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
}).toThrowMinErr(
'$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
);
});

Expand All @@ -590,8 +589,8 @@ describe('injector', function() {
function myModule(xyzzy){}
expect(function() {
createInjector([['xyzzy', myModule]]);
}).toThrowMatching(
/\[\$injector:modulerr\] Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
}).toThrowMinErr(
'$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
);
});

Expand All @@ -602,7 +601,7 @@ describe('injector', function() {
$provide.factory('service', function(service){});
return function(service) {}
}])
}).toThrow("[$injector:cdep] Circular dependency found: service");
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service');
});


Expand All @@ -613,7 +612,7 @@ describe('injector', function() {
$provide.factory('b', function(a){});
return function(a) {}
}])
}).toThrow('[$injector:cdep] Circular dependency found: b <- a');
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: b <- a');
});
});
});
Expand Down Expand Up @@ -703,7 +702,7 @@ describe('injector', function() {
it('should throw usefull error on wrong argument type]', function() {
expect(function() {
$injector.invoke({});
}).toThrow("[ng:areq] Argument 'fn' is not a function, got Object");
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got Object");
});
});

Expand Down Expand Up @@ -790,15 +789,15 @@ describe('injector', function() {
}]);
expect(function() {
$injector.get('nameProvider');
}).toThrow("[$injector:unpr] Unknown provider: nameProviderProvider <- nameProvider");
}).toThrowMinErr("$injector", "unpr", "Unknown provider: nameProviderProvider <- nameProvider");
});


it('should prevent provider configuration in app', function() {
var $injector = createInjector([]);
expect(function() {
$injector.get('$provide').value('a', 'b');
}).toThrow("[$injector:unpr] Unknown provider: $provideProvider <- $provide");
}).toThrowMinErr("$injector", "unpr", "Unknown provider: $provideProvider <- $provide");
});


Expand Down
6 changes: 3 additions & 3 deletions test/jqLiteSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -900,15 +900,15 @@ describe('jqLite', function() {

expect(function() {
elm.on('click', anObj, callback);
}).toThrowMatching(/\[jqLite\:onargs\]/);
}).toThrowMinErr('jqLite', 'onargs');

expect(function() {
elm.on('click', null, aString, callback);
}).toThrowMatching(/\[jqLite\:onargs\]/);
}).toThrowMinErr('jqLite', 'onargs');

expect(function() {
elm.on('click', aValue, callback);
}).toThrowMatching(/\[jqLite\:onargs\]/);
}).toThrowMinErr('jqLite', 'onargs');

});
}
Expand Down
2 changes: 1 addition & 1 deletion test/loaderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('module loader', function() {
it('should complain of no module', function() {
expect(function() {
window.angular.module('dontExist');
}).toThrow("[$injector:nomod] Module 'dontExist' is not available! You either misspelled the module name " +
}).toThrowMinErr("$injector", "nomod", "Module 'dontExist' is not available! You either misspelled the module name " +
"or forgot to load it. If registering a module ensure that you specify the dependencies as the second " +
"argument.");
});
Expand Down
47 changes: 47 additions & 0 deletions test/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,53 @@ beforeEach(function() {

toThrowMatching: function(expected) {
return jasmine.Matchers.prototype.toThrow.call(this, expected);
},

toThrowMinErr: function(namespace, code, content) {
var result,
exception,
exceptionMessage = '',
escapeRegexp = function (str) {
// This function escapes all special regex characters.
// We use it to create matching regex from arbitrary strings.
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
not = this.isNot ? "not " : "",
regex = jasmine.isA_("RegExp", content) ? content :
isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;

if(!isFunction(this.actual)) {
throw new Error('Actual is not a function');
}

try {
this.actual();
} catch (e) {
exception = e;
}

if (exception) {
exceptionMessage = exception.message || exception;
}

this.message = function () {
return "Expected function " + not + "to throw " +
namespace + "MinErr('" + code + "')" +
(regex ? " matching " + regex.toString() : "") +
(exception ? ", but it threw " + exceptionMessage : ".");
};

result = codeRegex.test(exceptionMessage);
if (!result) {
return result;
}

if (isDefined(regex)) {
return regex.test(exceptionMessage);
}
return result;
}
});
});
Expand Down
16 changes: 8 additions & 8 deletions test/minErrSpec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

describe('minErr', function () {

var supportStackTraces = function() {
var e = new Error();
return isDefined(e.stack);
};
var emptyTestError = minErr(),
var emptyTestError = minErr(),
testError = minErr('test');

it('should return an Error factory', function() {
Expand Down Expand Up @@ -34,7 +34,7 @@ describe('minErr', function () {

it('should interpolate string arguments without quotes', function() {
var myError = testError('1', 'This {0} is "{1}"', 'foo', 'bar');
expect(myError.message).toBe('[test:1] This foo is "bar"');
expect(myError.message).toMatch(/^\[test:1\] This foo is "bar"/);
});

it('should interpolate non-string arguments', function() {
Expand All @@ -57,7 +57,7 @@ describe('minErr', function () {
var myError = testError('26', 'false: {0}; zero: {1}; null: {2}; undefined: {3}; emptyStr: {4}',
false, 0, null, undefined, '');
expect(myError.message).
toBe('[test:26] false: false; zero: 0; null: null; undefined: undefined; emptyStr: ');
toMatch(/^\[test:26\] false: false; zero: 0; null: null; undefined: undefined; emptyStr: /);
});


Expand All @@ -67,19 +67,19 @@ describe('minErr', function () {
var foo = 'Fooooo',
myError = testError('26', 'This {0} is {1} on {2}', foo);

expect(myError.message).toBe('[test:26] This Fooooo is {1} on {2}');
expect(myError.message).toMatch(/^\[test:26\] This Fooooo is \{1\} on \{2\}/);
});


it('should pass through the message if no interpolation is needed', function() {
var myError = testError('26', 'Something horrible happened!');
expect(myError.message).toBe('[test:26] Something horrible happened!');
expect(myError.message).toMatch(/^\[test:26\] Something horrible happened!/);
});

it('should include a namespace in the message only if it is namespaced', function () {
var myError = emptyTestError('26', 'This is a {0}', 'Foo');
var myNamespacedError = testError('26', 'That is a {0}', 'Bar');
expect(myError.message).toBe('[26] This is a Foo');
expect(myNamespacedError.message).toBe('[test:26] That is a Bar');
expect(myError.message).toMatch(/^\[26\] This is a Foo/);
expect(myNamespacedError.message).toMatch(/^\[test:26\] That is a Bar/);
});
});
2 changes: 1 addition & 1 deletion test/ng/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("$animate", function() {
module(function($animateProvider) {
expect(function() {
$animateProvider.register('abc', null);
}).toThrow("[$animate:notcsel] Expecting class selector starting with '.' got 'abc'.");
}).toThrowMinErr("$animate", "notcsel", "Expecting class selector starting with '.' got 'abc'.");
});
inject();
});
Expand Down
2 changes: 1 addition & 1 deletion test/ng/cacheFactorySpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('$cacheFactory', function() {
it('should complain if the cache id is being reused', inject(function($cacheFactory) {
$cacheFactory('cache1');
expect(function() { $cacheFactory('cache1'); }).
toThrow("[$cacheFactory:iid] CacheId 'cache1' is already taken!");
toThrowMinErr("$cacheFactory", "iid", "CacheId 'cache1' is already taken!");
}));


Expand Down
Loading