fix(aio): show aio-themed 404 page for unknown resources (#23188)
Fixes #23179 PR Close #23188
This commit is contained in:
parent
668bfcec9d
commit
3d41739021
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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>
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue