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

Commit 6aaae06

Browse files
ksheedlobtford
authored andcommitted
feat(docs): linkify error messages on minErr docs pages
1 parent 6a8edc1 commit 6aaae06

File tree

2 files changed

+90
-3
lines changed

2 files changed

+90
-3
lines changed
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
describe("errorLinkFilter", function () {
2+
3+
var errorLinkFilter;
4+
5+
beforeEach(module('docsApp'));
6+
7+
beforeEach(inject(function ($filter) {
8+
errorLinkFilter = $filter('errorLink');
9+
}));
10+
11+
it('should not change text that does not contain links', function () {
12+
expect(errorLinkFilter('This is a test')).toBe('This is a test');
13+
});
14+
15+
it('should find links in text and linkify them', function () {
16+
expect(errorLinkFilter("http://ab/ (http://a/) http://1.2/v:~-123. c")).
17+
toBe('<a href="http://ab/">http://ab/</a> ' +
18+
'(<a href="http://a/">http://a/</a>) ' +
19+
'<a href="http://1.2/v:~-123">http://1.2/v:~-123</a>. c');
20+
expect(errorLinkFilter(undefined)).not.toBeDefined();
21+
});
22+
23+
it('should handle mailto', function () {
24+
expect(errorLinkFilter("mailto:me@example.com")).
25+
toBe('<a href="mailto:me@example.com">me@example.com</a>');
26+
expect(errorLinkFilter("me@example.com")).
27+
toBe('<a href="mailto:me@example.com">me@example.com</a>');
28+
expect(errorLinkFilter("send email to me@example.com, but")).
29+
toBe('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
30+
});
31+
32+
it('should handle target', function () {
33+
expect(errorLinkFilter("http://example.com", "_blank")).
34+
toBe('<a target="_blank" href="http://example.com">http://example.com</a>')
35+
expect(errorLinkFilter("http://example.com", "someNamedIFrame")).
36+
toBe('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
37+
});
38+
39+
it('should not linkify stack trace URLs', function () {
40+
expect(errorLinkFilter("http://example.com/angular.min.js:42:1337")).
41+
toBe("http://example.com/angular.min.js:42:1337");
42+
});
43+
44+
it('should truncate linked URLs at 60 characters', function () {
45+
expect(errorLinkFilter("http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp")).
46+
toBe('<a href="http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp">' +
47+
'http://errors.angularjs.org/very-long-version-string/$inj...</a>');
48+
});
49+
});

docs/src/templates/js/docs.js

+41-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var docsApp = {
22
controller: {},
33
directive: {},
4-
serviceFactory: {}
4+
serviceFactory: {},
5+
filter: {}
56
};
67

78
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
@@ -255,7 +256,40 @@ docsApp.directive.docTutorialReset = function() {
255256
};
256257

257258

258-
docsApp.directive.errorDisplay = ['$location', function ($location) {
259+
docsApp.filter.errorLink = ['$sanitize', function ($sanitize) {
260+
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/g,
261+
MAILTO_REGEXP = /^mailto:/,
262+
STACK_TRACE_REGEXP = /:\d+:\d+$/;
263+
264+
var truncate = function (text, nchars) {
265+
if (text.length > nchars) {
266+
return text.substr(0, nchars - 3) + '...';
267+
}
268+
return text;
269+
};
270+
271+
return function (text, target) {
272+
var targetHtml = target ? ' target="' + target + '"' : '';
273+
274+
if (!text) return text;
275+
276+
return $sanitize(text.replace(LINKY_URL_REGEXP, function (url) {
277+
if (STACK_TRACE_REGEXP.test(url)) {
278+
return url;
279+
}
280+
281+
// if we did not match ftp/http/mailto then assume mailto
282+
if (!/^((ftp|https?):\/\/|mailto:)/.test(url)) url = 'mailto:' + url;
283+
284+
return '<a' + targetHtml + ' href="' + url +'">' +
285+
truncate(url.replace(MAILTO_REGEXP, ''), 60) +
286+
'</a>';
287+
}));
288+
};
289+
}];
290+
291+
292+
docsApp.directive.errorDisplay = ['$location', 'errorLinkFilter', function ($location, errorLinkFilter) {
259293
var interpolate = function (formatString) {
260294
var formatArgs = arguments;
261295
return formatString.replace(/\{\d+\}/g, function (match) {
@@ -278,7 +312,7 @@ docsApp.directive.errorDisplay = ['$location', function ($location) {
278312
for (i = 0; angular.isDefined(search['p'+i]); i++) {
279313
formatArgs.push(search['p'+i]);
280314
}
281-
element.text(interpolate.apply(null, formatArgs));
315+
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
282316
}
283317
};
284318
}];
@@ -873,3 +907,7 @@ angular.module('docsApp', ['ngResource', 'ngRoute', 'ngCookies', 'ngSanitize', '
873907
factory(docsApp.serviceFactory).
874908
directive(docsApp.directive).
875909
controller(docsApp.controller);
910+
911+
angular.forEach(docsApp.filter, function (docsAppFilter, filterName) {
912+
angular.module('docsApp').filter(filterName, docsAppFilter);
913+
});

0 commit comments

Comments
 (0)