diff --git a/src/minErr.js b/src/minErr.js
index 89be9b1bffea..4ebcf7221226 100644
--- a/src/minErr.js
+++ b/src/minErr.js
@@ -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;
@@ -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);
};
}
diff --git a/test/AngularSpec.js b/test/AngularSpec.js
index f049c2fd0c05..415ff25dc718 100644
--- a/test/AngularSpec.js
+++ b/test/AngularSpec.js
@@ -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() {
@@ -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();
});
diff --git a/test/BinderSpec.js b/test/BinderSpec.js
index c03b8ace10c3..3c204b64c48b 100644
--- a/test/BinderSpec.js
+++ b/test/BinderSpec.js
@@ -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();
diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js
index 2c4856550bae..efed2aa40f9c 100644
--- a/test/auto/injectorSpec.js
+++ b/test/auto/injectorSpec.js
@@ -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");
});
@@ -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");
});
@@ -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");
});
});
@@ -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/);
});
@@ -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/);
});
@@ -562,7 +561,7 @@ 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/);
});
@@ -570,8 +569,8 @@ describe('injector', 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/
);
});
@@ -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/
);
});
@@ -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/
);
});
@@ -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');
});
@@ -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');
});
});
});
@@ -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");
});
});
@@ -790,7 +789,7 @@ describe('injector', function() {
}]);
expect(function() {
$injector.get('nameProvider');
- }).toThrow("[$injector:unpr] Unknown provider: nameProviderProvider <- nameProvider");
+ }).toThrowMinErr("$injector", "unpr", "Unknown provider: nameProviderProvider <- nameProvider");
});
@@ -798,7 +797,7 @@ describe('injector', 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");
});
diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js
index 913e61924726..79c0d0c6a433 100644
--- a/test/jqLiteSpec.js
+++ b/test/jqLiteSpec.js
@@ -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');
});
}
diff --git a/test/loaderSpec.js b/test/loaderSpec.js
index 73e942151696..302852cb6db2 100644
--- a/test/loaderSpec.js
+++ b/test/loaderSpec.js
@@ -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.");
});
diff --git a/test/matchers.js b/test/matchers.js
index b7d336b7ee5b..57bf35c7cd9e 100644
--- a/test/matchers.js
+++ b/test/matchers.js
@@ -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;
}
});
});
diff --git a/test/minErrSpec.js b/test/minErrSpec.js
index 4ee569e0ebe5..6b7d93b894e4 100644
--- a/test/minErrSpec.js
+++ b/test/minErrSpec.js
@@ -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() {
@@ -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() {
@@ -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: /);
});
@@ -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/);
});
});
diff --git a/test/ng/animateSpec.js b/test/ng/animateSpec.js
index 2e9034e4b9b2..79115c1e8e4c 100644
--- a/test/ng/animateSpec.js
+++ b/test/ng/animateSpec.js
@@ -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();
});
diff --git a/test/ng/cacheFactorySpec.js b/test/ng/cacheFactorySpec.js
index c398a55eadbc..b1a018dab879 100644
--- a/test/ng/cacheFactorySpec.js
+++ b/test/ng/cacheFactorySpec.js
@@ -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!");
}));
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index ba94c48bbfcf..28dc57fef0f8 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -632,11 +632,11 @@ describe('$compile', function() {
inject(function($compile) {
expect(function() {
$compile('
');
- }).toThrow("[$compile:tplrt] Template for directive 'noRootElem' must have exactly one root element. ");
+ }).toThrowMinErr("$compile", "tplrt", "Template for directive 'noRootElem' must have exactly one root element. ");
expect(function() {
$compile('');
- }).toThrow("[$compile:tplrt] Template for directive 'multiRootElem' must have exactly one root element. ");
+ }).toThrowMinErr("$compile", "tplrt", "Template for directive 'multiRootElem' must have exactly one root element. ");
// ws is ok
expect(function() {
@@ -748,7 +748,7 @@ describe('$compile', function() {
expect(function() {
$templateCache.put('http://example.com/should-not-load.html', 'Should not load even if in cache.');
$compile('')($rootScope);
- }).toThrow('[$sce:insecurl] Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://example.com/should-not-load.html');
+ }).toThrowMinErr('$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://example.com/should-not-load.html');
}));
it('should load cross domain templates when trusted', inject(
@@ -1011,7 +1011,7 @@ describe('$compile', function() {
expect(function() {
$httpBackend.flush();
- }).toThrow('[$compile:tpload] Failed to load template: hello.html');
+ }).toThrowMinErr('$compile', 'tpload', 'Failed to load template: hello.html');
expect(sortedHtml(element)).toBe('