Skip to content

Commit 6ab42bb

Browse files
committed
chore(docs-app): add dynamic 404 behavior
1 parent dfaea38 commit 6ab42bb

File tree

5 files changed

+124
-8
lines changed

5 files changed

+124
-8
lines changed

docs/app/e2e/app.scenario.js

+63
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(404, '');
130+
}]);
131+
});
132+
});
133+
134+
it('should set "noindex" for googlebot 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(false);
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 googlebot 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(false);
161+
expect(googleBot.isPresent()).toBe(true);
162+
});
163+
});
164+
165+
166+
});
167+
105168
});
106169

107170
});

docs/app/src/docs.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -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,23 @@ 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;
71+
var errorPartialPath = 'Error404.html';
6572

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

docs/config/index.js

+4-1
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

+40
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

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
Declarative templates with data-binding, MVC, dependency injection and great
1010
testability story all implemented with pure client-side JavaScript!">
1111
<meta name="fragment" content="!">
12+
<meta name="robots" ng-attr-content="{{hasError() ? 'noindex' : ''}}" ng-if="hasError()">
13+
<meta name="googlebot" content="noindex" ng-if="hasError()">
1214
<title ng-bind-template="AngularJS: {{ currentArea.name }}: {{ currentPage.name || 'Error: Page not found'}}">AngularJS</title>
1315

1416
<script type="text/javascript">
@@ -127,7 +129,7 @@ <h1 class="brand"><a href="http://angularjs.org"><img width="117" height="30" sr
127129
</ul>
128130

129131
</div>
130-
<div class="search-results-container" ng-show="hasResults">
132+
<div class="search-results-container" ng-show="hasResults" ng-cloak>
131133
<div class="container">
132134
<div class="search-results-frame">
133135
<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 +159,7 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
157159
</div>
158160
</div>
159161
</nav>
160-
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element>
162+
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element ng-cloak>
161163
<div class="container main-grid main-header-grid">
162164
<div class="grid-left">
163165
<version-picker></version-picker>
@@ -174,7 +176,7 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
174176
</nav>
175177
</header>
176178

177-
<section role="main" class="container main-body">
179+
<section role="main" class="container main-body" ng-cloak>
178180
<div class="main-grid main-body-grid">
179181
<div class="grid-left">
180182
<a class="btn toc-toggle visible-xs" ng-click="toc=!toc">Show / Hide Table of Contents</a>
@@ -198,7 +200,8 @@ <h4 class="search-results-group-heading">{{ key }}</h4>
198200
</div>
199201
</div>
200202
<div class="grid-right">
201-
<div id="loading" ng-show="loading">Loading...</div>
203+
<div ng-show="loading">Loading &hellip;</div>
204+
<div ng-show="loadingError">There was an error loading this resource. Please try again later.</div>
202205
<div ng-hide="loading" ng-include="partialPath" toc-collector autoscroll></div>
203206
</div>
204207
</div>

0 commit comments

Comments
 (0)