fix(aio): show aio-themed 404 page for unknown resources (#23188)

Fixes #23179

PR Close #23188
This commit is contained in:
George Kalpakas 2018-04-05 14:24:37 +03:00 committed by Matias Niemelä
parent 668bfcec9d
commit 3d41739021
4 changed files with 122 additions and 2 deletions

View File

@ -57,11 +57,12 @@
"generate-zips": "node ./tools/example-zipper/generateZips", "generate-zips": "node ./tools/example-zipper/generateZips",
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json", "sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/", "sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"build-404-page": "node scripts/build-404-page",
"build-ie-polyfills": "yarn webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production", "build-ie-polyfills": "yarn webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG", "update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG",
"~~check-env": "node scripts/check-environment", "~~check-env": "node scripts/check-environment",
"~~build": "ng build", "~~build": "ng build",
"post~~build": "yarn sw-manifest && yarn sw-copy" "post~~build": "yarn build-404-page && yarn sw-manifest && yarn sw-copy"
}, },
"engines": { "engines": {
"node": ">=8.9.1 <9.0.0", "node": ">=8.9.1 <9.0.0",

View File

@ -0,0 +1,31 @@
#!/usr/bin/env node
// Imports
const {readFileSync, writeFileSync} = require('fs');
const {join, resolve} = require('path');
// Constants
const SRC_DIR = resolve(__dirname, '../src');
const DIST_DIR = resolve(__dirname, '../dist');
// Run
_main(process.argv.slice(2));
// Functions - Definitions
function _main() {
const srcIndexPath = join(DIST_DIR, 'index.html');
const src404BodyPath = join(SRC_DIR, '404-body.html');
const dst404PagePath = join(DIST_DIR, '404.html');
const srcIndexContent = readFileSync(srcIndexPath, 'utf8');
const src404BodyContent = readFileSync(src404BodyPath, 'utf8');
const dst404PageContent = srcIndexContent.replace(/<body>[\s\S]+<\/body>/, src404BodyContent);
if (dst404PageContent === srcIndexContent) {
throw new Error(
'Failed to generate \'404.html\'. ' +
'The content of \'index.html\' does not match the expected pattern.');
}
writeFileSync(dst404PagePath, dst404PageContent);
}

52
aio/src/404-body.html Normal file
View File

@ -0,0 +1,52 @@
<!--
This content replaces the `<body>` content of `index.html` to generate our custom `404.html` page.
The content must visually and structurally resemble the resulting HTML of the main app for not
found pages (e.g. https://angular.io/not/existing).
-->
<style>
.mat-toolbar-row{display:flex;box-sizing:border-box;padding:0 16px;width:100%;flex-direction:row;align-items:center;white-space:nowrap}
.mat-toolbar-row{height:64px}
@media (max-width:600px){.mat-toolbar-row{height:56px}}
.nav-link.home{margin-left:24px}
</style>
<aio-shell class="mode-stable page-file-not-found folder-file-not-found view- aio-notification-hide">
<mat-toolbar class="app-toolbar no-print mat-toolbar mat-primary">
<mat-toolbar-row class="mat-toolbar-row">
<a class="nav-link home" href="/">
<img alt="Home" height="40" src="assets/images/logos/angular/logo-nav@2x.png" title="Home" width="150">
</a>
</mat-toolbar-row>
</mat-toolbar>
<mat-sidenav-container class="sidenav-container mat-drawer-container mat-sidenav-container" role="main">
<mat-sidenav-content class="mat-drawer-content mat-sidenav-content">
<section class="sidenav-content" role="content">
<aio-doc-viewer>
<div class="content">
<div class="nf-container l-flex-wrap flex-center">
<img src="assets/images/support/angular-404.svg" width="300" height="300" />
<div class="nf-response l-flex-wrap">
<h1 class="no-toc" id="page-not-found">Resource Not Found</h1>
<p>We're sorry. The resource you are looking for cannot be found.</p>
</div>
</div>
</div>
</aio-doc-viewer>
</section>
</mat-sidenav-content>
</mat-sidenav-container>
<footer class="no-print">
<aio-footer>
<p>
Powered by Google ©2010-2018.
Code licensed under an <a href="license" title="License text">MIT-style License</a>.
Documentation licensed under <a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
</p>
</aio-footer>
</footer>
</aio-shell>

View File

@ -1,4 +1,4 @@
import { browser } from 'protractor'; import { browser, by, element } from 'protractor';
import { SitePage } from './site.po'; import { SitePage } from './site.po';
describe(browser.baseUrl, () => { describe(browser.baseUrl, () => {
@ -34,4 +34,40 @@ describe(browser.baseUrl, () => {
}); });
}); });
}); });
describe('(with unknown URLs)', () => {
const unknownPageUrl = '/unknown/page';
const unknownResourceUrl = '/unknown/resource.ext';
it('should serve `index.html` for unknown pages', async () => {
const aioShell = element(by.css('aio-shell'));
const heading = aioShell.element(by.css('h1'));
await page.goTo(unknownPageUrl);
expect(aioShell.isPresent()).toBe(true);
expect(heading.getText()).toMatch(/page not found/i);
});
it('should serve a custom 404 page for unknown resources', async () => {
const aioShell = element(by.css('aio-shell'));
const heading = aioShell.element(by.css('h1'));
await page.goTo(unknownResourceUrl);
expect(aioShell.isPresent()).toBe(true);
expect(heading.getText()).toMatch(/resource not found/i);
});
it('should include a link to the home page in custom 404 page', async () => {
const homeNavLink = element(by.css('.nav-link.home'));
await page.goTo(unknownResourceUrl);
expect(homeNavLink.isPresent()).toBe(true);
await homeNavLink.click();
const expectedUrl = browser.baseUrl;
const actualUrl = await browser.getCurrentUrl();
expect(actualUrl).toBe(expectedUrl);
});
});
}); });