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

Commit 37123cd

Browse files
committed
feat(minerr): log minerr doc url in development
Closes #3566
1 parent fe267e3 commit 37123cd

24 files changed

+286
-217
lines changed

src/minErr.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,21 @@
3030

3131
function minErr(module) {
3232
return function () {
33-
var prefix = '[' + (module ? module + ':' : '') + arguments[0] + '] ',
33+
var code = arguments[0],
34+
prefix = '[' + (module ? module + ':' : '') + code + '] ',
3435
template = arguments[1],
3536
templateArgs = arguments,
36-
message;
37+
stringify = function (obj) {
38+
if (isFunction(obj)) {
39+
return obj.toString().replace(/ \{[\s\S]*$/, '');
40+
} else if (isUndefined(obj)) {
41+
return 'undefined';
42+
} else if (!isString(obj)) {
43+
return JSON.stringify(obj);
44+
}
45+
return obj;
46+
},
47+
message, i;
3748

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

66+
message = message + '\nhttp://errors.angularjs.org/' + version.full + '/' +
67+
(module ? module + '/' : '') + code;
68+
for (i = 2; i < arguments.length; i++) {
69+
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
70+
encodeURIComponent(stringify(arguments[i]));
71+
}
72+
5573
return new Error(message);
5674
};
5775
}

test/AngularSpec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,20 @@ describe('angular', function() {
108108

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

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

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

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

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

906906
expect(element.injector().get('$http')).toBeDefined();
907907
});

test/BinderSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ describe('Binder', function() {
175175
$rootScope.error['throw'] = function() {throw 'MyError';};
176176
errorLogs.length = 0;
177177
$rootScope.$apply();
178-
expect(errorLogs.shift().message).toBe("[$interpolate:interr] Can't interpolate: {{error.throw()}}\nMyError");
178+
expect(errorLogs.shift().message).toMatch(/^\[\$interpolate:interr\] Can't interpolate: \{\{error.throw\(\)\}\}\nMyError/);
179179

180180
$rootScope.error['throw'] = function() {return 'ok';};
181181
$rootScope.$apply();

test/auto/injectorSpec.js

+19-20
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('injector', function() {
7070
it('should provide useful message if no provider', function() {
7171
expect(function() {
7272
injector.get('idontexist');
73-
}).toThrow("[$injector:unpr] Unknown provider: idontexistProvider <- idontexist");
73+
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
7474
});
7575

7676

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

8585

@@ -127,10 +127,10 @@ describe('injector', function() {
127127
it('should fail with errors if not function or array', function() {
128128
expect(function() {
129129
injector.invoke({});
130-
}).toThrow("[ng:areq] Argument 'fn' is not a function, got Object");
130+
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got Object");
131131
expect(function() {
132132
injector.invoke(['a', 123], {});
133-
}).toThrow("[ng:areq] Argument 'fn' is not a function, got number");
133+
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got number");
134134
});
135135
});
136136

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

276275

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

559558

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

568567

569568
it('should decorate the missing service error with module name', function() {
570569
angular.module('TestModule', [], function(xyzzy) {});
571570
expect(function() {
572571
createInjector(['TestModule' ]);
573-
}).toThrowMatching(
574-
/\[\$injector:modulerr\] Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
572+
}).toThrowMinErr(
573+
'$injector', 'modulerr', /Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
575574
);
576575
});
577576

@@ -580,8 +579,8 @@ describe('injector', function() {
580579
function myModule(xyzzy){}
581580
expect(function() {
582581
createInjector([myModule]);
583-
}).toThrowMatching(
584-
/\[\$injector:modulerr\] Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
582+
}).toThrowMinErr(
583+
'$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
585584
);
586585
});
587586

@@ -590,8 +589,8 @@ describe('injector', function() {
590589
function myModule(xyzzy){}
591590
expect(function() {
592591
createInjector([['xyzzy', myModule]]);
593-
}).toThrowMatching(
594-
/\[\$injector:modulerr\] Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
592+
}).toThrowMinErr(
593+
'$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
595594
);
596595
});
597596

@@ -602,7 +601,7 @@ describe('injector', function() {
602601
$provide.factory('service', function(service){});
603602
return function(service) {}
604603
}])
605-
}).toThrow("[$injector:cdep] Circular dependency found: service");
604+
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service');
606605
});
607606

608607

@@ -613,7 +612,7 @@ describe('injector', function() {
613612
$provide.factory('b', function(a){});
614613
return function(a) {}
615614
}])
616-
}).toThrow('[$injector:cdep] Circular dependency found: b <- a');
615+
}).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: b <- a');
617616
});
618617
});
619618
});
@@ -703,7 +702,7 @@ describe('injector', function() {
703702
it('should throw usefull error on wrong argument type]', function() {
704703
expect(function() {
705704
$injector.invoke({});
706-
}).toThrow("[ng:areq] Argument 'fn' is not a function, got Object");
705+
}).toThrowMinErr("ng", "areq", "Argument 'fn' is not a function, got Object");
707706
});
708707
});
709708

@@ -790,15 +789,15 @@ describe('injector', function() {
790789
}]);
791790
expect(function() {
792791
$injector.get('nameProvider');
793-
}).toThrow("[$injector:unpr] Unknown provider: nameProviderProvider <- nameProvider");
792+
}).toThrowMinErr("$injector", "unpr", "Unknown provider: nameProviderProvider <- nameProvider");
794793
});
795794

796795

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

804803

test/jqLiteSpec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -900,15 +900,15 @@ describe('jqLite', function() {
900900

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

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

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

913913
});
914914
}

test/loaderSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe('module loader', function() {
6868
it('should complain of no module', function() {
6969
expect(function() {
7070
window.angular.module('dontExist');
71-
}).toThrow("[$injector:nomod] Module 'dontExist' is not available! You either misspelled the module name " +
71+
}).toThrowMinErr("$injector", "nomod", "Module 'dontExist' is not available! You either misspelled the module name " +
7272
"or forgot to load it. If registering a module ensure that you specify the dependencies as the second " +
7373
"argument.");
7474
});

test/matchers.js

+47
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,53 @@ beforeEach(function() {
165165

166166
toThrowMatching: function(expected) {
167167
return jasmine.Matchers.prototype.toThrow.call(this, expected);
168+
},
169+
170+
toThrowMinErr: function(namespace, code, content) {
171+
var result,
172+
exception,
173+
exceptionMessage = '',
174+
escapeRegexp = function (str) {
175+
// This function escapes all special regex characters.
176+
// We use it to create matching regex from arbitrary strings.
177+
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
178+
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
179+
},
180+
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
181+
not = this.isNot ? "not " : "",
182+
regex = jasmine.isA_("RegExp", content) ? content :
183+
isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
184+
185+
if(!isFunction(this.actual)) {
186+
throw new Error('Actual is not a function');
187+
}
188+
189+
try {
190+
this.actual();
191+
} catch (e) {
192+
exception = e;
193+
}
194+
195+
if (exception) {
196+
exceptionMessage = exception.message || exception;
197+
}
198+
199+
this.message = function () {
200+
return "Expected function " + not + "to throw " +
201+
namespace + "MinErr('" + code + "')" +
202+
(regex ? " matching " + regex.toString() : "") +
203+
(exception ? ", but it threw " + exceptionMessage : ".");
204+
};
205+
206+
result = codeRegex.test(exceptionMessage);
207+
if (!result) {
208+
return result;
209+
}
210+
211+
if (isDefined(regex)) {
212+
return regex.test(exceptionMessage);
213+
}
214+
return result;
168215
}
169216
});
170217
});

test/minErrSpec.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
22

33
describe('minErr', function () {
4-
4+
55
var supportStackTraces = function() {
66
var e = new Error();
77
return isDefined(e.stack);
88
};
9-
var emptyTestError = minErr(),
9+
var emptyTestError = minErr(),
1010
testError = minErr('test');
1111

1212
it('should return an Error factory', function() {
@@ -34,7 +34,7 @@ describe('minErr', function () {
3434

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

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

6363

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

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

7373

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

7979
it('should include a namespace in the message only if it is namespaced', function () {
8080
var myError = emptyTestError('26', 'This is a {0}', 'Foo');
8181
var myNamespacedError = testError('26', 'That is a {0}', 'Bar');
82-
expect(myError.message).toBe('[26] This is a Foo');
83-
expect(myNamespacedError.message).toBe('[test:26] That is a Bar');
82+
expect(myError.message).toMatch(/^\[26\] This is a Foo/);
83+
expect(myNamespacedError.message).toMatch(/^\[test:26\] That is a Bar/);
8484
});
8585
});

test/ng/animateSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe("$animate", function() {
4444
module(function($animateProvider) {
4545
expect(function() {
4646
$animateProvider.register('abc', null);
47-
}).toThrow("[$animate:notcsel] Expecting class selector starting with '.' got 'abc'.");
47+
}).toThrowMinErr("$animate", "notcsel", "Expecting class selector starting with '.' got 'abc'.");
4848
});
4949
inject();
5050
});

test/ng/cacheFactorySpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('$cacheFactory', function() {
1515
it('should complain if the cache id is being reused', inject(function($cacheFactory) {
1616
$cacheFactory('cache1');
1717
expect(function() { $cacheFactory('cache1'); }).
18-
toThrow("[$cacheFactory:iid] CacheId 'cache1' is already taken!");
18+
toThrowMinErr("$cacheFactory", "iid", "CacheId 'cache1' is already taken!");
1919
}));
2020

2121

0 commit comments

Comments
 (0)