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

Commit

Permalink
feat(minerr): log minerr doc url in development
Browse files Browse the repository at this point in the history
Closes #3566
  • Loading branch information
ksheedlo committed Aug 15, 2013
1 parent fe267e3 commit 37123cd
Show file tree
Hide file tree
Showing 24 changed files with 286 additions and 217 deletions.
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

0 comments on commit 37123cd

Please sign in to comment.