Skip to content

Commit a37f89f

Browse files
committed
chore(docs-app): add dynamic 404 behavior
Adapted from angular/angular@88045a5, angular/angular@c3fb820, and angular/angular@5a624fa.
1 parent 02fb980 commit a37f89f

File tree

6 files changed

+136
-10
lines changed

6 files changed

+136
-10
lines changed

docs/app/e2e/.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010

1111
"globals": {
12+
"angular": false,
1213
/* testabilityPatch / matchers */
1314
"inject": false,
1415
"module": false,

docs/app/e2e/app.scenario.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ describe('docs.angularjs.org', function() {
2121
console.log('browser console errors: ' + require('util').inspect(filteredLog));
2222
}
2323
});
24+
25+
browser.ignoreSynchronization = false;
26+
browser.clearMockModules();
2427
});
2528

2629

@@ -102,6 +105,66 @@ describe('docs.angularjs.org', function() {
102105
expect(mainHeader.getText()).toEqual('Oops!');
103106
});
104107

108+
it('should set "noindex" if the page does not exist', function() {
109+
browser.get('build/docs/index-production.html#!/api/does/not/exist');
110+
var robots = element(by.css('meta[name="robots"][content="noindex"]'));
111+
var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]'));
112+
expect(robots.isPresent()).toBe(true);
113+
expect(googleBot.isPresent()).toBe(true);
114+
});
115+
116+
it('should remove "noindex" if the page exists', function() {
117+
browser.get('build/docs/index-production.html#!/api');
118+
var robots = element(by.css('meta[name="robots"][content="noindex"]'));
119+
var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]'));
120+
expect(robots.isPresent()).toBe(false);
121+
expect(googleBot.isPresent()).toBe(false);
122+
});
123+
124+
describe('template request error', function() {
125+
beforeEach(function() {
126+
browser.addMockModule('httpMocker', function() {
127+
angular.module('httpMocker', ['ngMock'])
128+
.run(['$httpBackend', function($httpBackend) {
129+
$httpBackend.whenGET('localhost:8000/build/docs/partials/api.html').respond(500, '');
130+
}]);
131+
});
132+
});
133+
134+
it('should set "noindex" for robots if the request fails', function() {
135+
// index-test includes ngMock
136+
browser.get('build/docs/index-test.html#!/api');
137+
var robots = element(by.css('meta[name="robots"][content="noindex"]'));
138+
var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]'));
139+
expect(robots.isPresent()).toBe(true);
140+
expect(googleBot.isPresent()).toBe(true);
141+
});
142+
});
143+
144+
145+
describe('page bootstrap error', function() {
146+
beforeEach(function() {
147+
browser.addMockModule('httpMocker', function() {
148+
// Require a module that does not exist to break the bootstrapping
149+
angular.module('httpMocker', ['doesNotExist']);
150+
});
151+
});
152+
153+
it('should have "noindex" for robots if bootstrapping fails', function() {
154+
browser.get('build/docs/index.html#!/api').catch(function() {
155+
// get() will fail on AngularJS bootstrap, but if we continue here, protractor
156+
// will assume the app is ready
157+
browser.ignoreSynchronization = true;
158+
var robots = element(by.css('meta[name="robots"][content="noindex"]'));
159+
var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]'));
160+
expect(robots.isPresent()).toBe(true);
161+
expect(googleBot.isPresent()).toBe(true);
162+
});
163+
});
164+
165+
166+
});
167+
105168
});
106169

107170
});

docs/app/src/docs.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ angular.module('DocsController', ['currentVersionData'])
88
function($scope, $rootScope, $location, $window, $cookies,
99
NG_PAGES, NG_NAVIGATION, CURRENT_NG_VERSION) {
1010

11+
var errorPartialPath = 'Error404.html';
12+
1113
$scope.navClass = function(navItem) {
1214
return {
1315
active: navItem.href && this.currentPage && this.currentPage.path,
@@ -16,8 +18,6 @@ angular.module('DocsController', ['currentVersionData'])
1618
};
1719
};
1820

19-
20-
2121
$scope.$on('$includeContentLoaded', function() {
2222
var pagePath = $scope.currentPage ? $scope.currentPage.path : $location.path();
2323
$window._gaq.push(['_trackPageview', pagePath]);
@@ -26,6 +26,7 @@ angular.module('DocsController', ['currentVersionData'])
2626

2727
$scope.$on('$includeContentError', function() {
2828
$scope.loading = false;
29+
$scope.loadingError = true;
2930
});
3031

3132
$scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) {
@@ -35,6 +36,7 @@ angular.module('DocsController', ['currentVersionData'])
3536
var currentPage = $scope.currentPage = NG_PAGES[path];
3637

3738
$scope.loading = true;
39+
$scope.loadingError = false;
3840

3941
if (currentPage) {
4042
$scope.partialPath = 'partials/' + path + '.html';
@@ -50,18 +52,22 @@ angular.module('DocsController', ['currentVersionData'])
5052
} else {
5153
$scope.currentArea = NG_NAVIGATION['api'];
5254
$scope.breadcrumb = [];
53-
$scope.partialPath = 'Error404.html';
55+
$scope.partialPath = errorPartialPath;
5456
}
5557
});
5658

59+
$scope.hasError = function() {
60+
return $scope.partialPath === errorPartialPath || $scope.loadingError;
61+
};
62+
5763
/**********************************
5864
Initialize
5965
***********************************/
6066

6167
$scope.versionNumber = CURRENT_NG_VERSION.full;
6268
$scope.version = CURRENT_NG_VERSION.full + ' ' + CURRENT_NG_VERSION.codeName;
63-
$scope.loading = 0;
64-
69+
$scope.loading = false;
70+
$scope.loadingError = false;
6571

6672
var INDEX_PATH = /^(\/|\/index[^.]*.html)$/;
6773
if (!$location.path() || INDEX_PATH.test($location.path())) {

docs/config/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module.exports = new Package('angularjs', [
2222
.factory(require('./services/deployments/debug'))
2323
.factory(require('./services/deployments/default'))
2424
.factory(require('./services/deployments/jquery'))
25+
.factory(require('./services/deployments/test'))
2526
.factory(require('./services/deployments/production'))
2627

2728
.factory(require('./inline-tag-defs/type'))
@@ -157,12 +158,14 @@ module.exports = new Package('angularjs', [
157158
generateProtractorTestsProcessor,
158159
generateExamplesProcessor,
159160
debugDeployment, defaultDeployment,
160-
jqueryDeployment, productionDeployment) {
161+
jqueryDeployment, testDeployment,
162+
productionDeployment) {
161163

162164
generateIndexPagesProcessor.deployments = [
163165
debugDeployment,
164166
defaultDeployment,
165167
jqueryDeployment,
168+
testDeployment,
166169
productionDeployment
167170
];
168171

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
module.exports = function testDeployment(getVersion) {
4+
return {
5+
name: 'test',
6+
examples: {
7+
commonFiles: {
8+
scripts: ['../../../angular.js']
9+
},
10+
dependencyPath: '../../../'
11+
},
12+
scripts: [
13+
'../angular.js',
14+
'../angular-resource.js',
15+
'../angular-route.js',
16+
'../angular-cookies.js',
17+
'../angular-mocks.js',
18+
'../angular-sanitize.js',
19+
'../angular-touch.js',
20+
'../angular-animate.js',
21+
'components/marked-' + getVersion('marked') + '/lib/marked.js',
22+
'js/angular-bootstrap/dropdown-toggle.js',
23+
'components/lunr-' + getVersion('lunr') + '/lunr.js',
24+
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
25+
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
26+
'js/current-version-data.js',
27+
'js/all-versions-data.js',
28+
'js/pages-data.js',
29+
'js/nav-data.js',
30+
'js/docs.js'
31+
],
32+
stylesheets: [
33+
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
34+
'css/prettify-theme.css',
35+
'css/angular-topnav.css',
36+
'css/docs.css',
37+
'css/animations.css'
38+
]
39+
};
40+
};

docs/config/templates/app/indexPage.template.html

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
<meta name="fragment" content="!">
1212
<title ng-bind-template="AngularJS: {{ currentArea.name }}: {{ currentPage.name || 'Error: Page not found'}}">AngularJS</title>
1313

14+
<script type="text/javascript">
15+
(function() {
16+
// Dynamically, pre-emptively, add `noindex`, which will be removed when the doc is ready and valid
17+
['googlebot', 'robots'].forEach(function(bot) {
18+
var tag = document.createElement('meta');
19+
tag.name = bot;
20+
tag.content = 'noindex';
21+
tag.setAttribute('ng-if', 'hasError()');
22+
document.head.appendChild(tag);
23+
});
24+
})();
25+
</script>
1426
<script type="text/javascript">
1527
// dynamically add base tag as well as css and javascript files.
1628
// we can't add css/js the usual way, because some browsers (FF) eagerly prefetch resources
@@ -127,7 +139,7 @@ <h1 class="brand"><a href="http://angularjs.org"><img width="117" height="30" sr
127139
</ul>
128140

129141
</div>
130-
<div class="search-results-container" ng-show="hasResults">
142+
<div class="search-results-container" ng-show="hasResults" ng-cloak>
131143
<div class="container">
132144
<div class="search-results-frame">
133145
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
@@ -157,7 +169,7 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
157169
</div>
158170
</div>
159171
</nav>
160-
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element>
172+
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element ng-cloak>
161173
<div class="container main-grid main-header-grid">
162174
<div class="grid-left">
163175
<version-picker></version-picker>
@@ -174,7 +186,7 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
174186
</nav>
175187
</header>
176188

177-
<section role="main" class="container main-body">
189+
<section role="main" class="container main-body" ng-cloak>
178190
<div class="main-grid main-body-grid">
179191
<div class="grid-left">
180192
<a class="btn toc-toggle visible-xs" ng-click="toc=!toc">Show / Hide Table of Contents</a>
@@ -198,7 +210,8 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
198210
</div>
199211
</div>
200212
<div class="grid-right">
201-
<div id="loading" ng-show="loading">Loading...</div>
213+
<div ng-show="loading">Loading &hellip;</div>
214+
<div ng-show="loadingError">There was an error loading this resource. Please try again later.</div>
202215
<div ng-hide="loading" ng-include="partialPath" toc-collector autoscroll></div>
203216
</div>
204217
</div>

0 commit comments

Comments
 (0)