Skip to content

Commit 96690ed

Browse files
gkalpakAndrewKushnir
authored andcommitted
feat(docs-infra): add shortcuts for the angular.io PWA (angular#40393)
This commits adds some shortcut definitions for the angular.io PWA. The user agent can use them to assemble a context menu to be displayed by the operating system when a user engages with the app's icon. (In addition, shortcuts provide an easy way for users to add links to specific pages on their home screen.) See [here][1] for more details on the `shortcuts` property of the PWA manifest. The choice of pages to create shortcuts to was influenced by the following facts/criteria: - It seems that only the first 4 shortcuts are displayed by Chrome (at least on my Android phone). - Since the PWA is mostly used on mobile, I omitted pages that are less likely to be useful for mobile users (such as pages related to CLI). [1]: https://developer.mozilla.org/en-US/docs/Web/Manifest/shortcuts PR Close angular#40393
1 parent 4065c98 commit 96690ed

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

aio/src/pwa-manifest.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,31 @@
2222
"purpose": "maskable"
2323
}
2424
],
25-
"start_url": "/?utm_source=homescreen"
25+
"start_url": "/?utm_source=homescreen",
26+
"shortcuts": [
27+
{
28+
"name": "Go to API Reference",
29+
"short_name": "API",
30+
"description": "Go to the Angular API reference page.",
31+
"url": "/api?utm_source=homescreen"
32+
},
33+
{
34+
"name": "Go to Glossary",
35+
"short_name": "Glossary",
36+
"description": "Go to the glossary page: A list of common Angular terms and their explanation.",
37+
"url": "/guide/glossary?utm_source=homescreen"
38+
},
39+
{
40+
"name": "Go to Resources",
41+
"short_name": "Resources",
42+
"description": "Go to the resources page: A list of Angular resouces, such as development tooling, UI libraries, books, courses, community publications, podcasts, etc.",
43+
"url": "/resources?utm_source=homescreen"
44+
},
45+
{
46+
"name": "Go to Tutorial: Tour of Heroes",
47+
"short_name": "Tutorial",
48+
"description": "Go to the \"Tour of Heroes\" tutorial page: Learn how to create your first Angular application.",
49+
"url": "/tutorial?utm_source=homescreen"
50+
}
51+
]
2652
}

aio/tests/e2e/src/app.po.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class SitePage {
4141

4242
async navigateTo(pageUrl: string) {
4343
// Navigate to the page, disable animations, and wait for Angular.
44-
await browser.get(`/${pageUrl}`);
44+
await browser.get(`/${pageUrl.replace(/^\//, '')}`);
4545
await browser.executeScript('document.body.classList.add(\'no-animations\')');
4646
await browser.waitForAngular();
4747
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { PwaManifestPage, PwaShortcutItem } from './pwa-manifest.po';
2+
3+
4+
describe('PWA manifest', () => {
5+
const page = new PwaManifestPage();
6+
7+
describe('shortcuts', () => {
8+
let shortcuts: PwaShortcutItem[];
9+
10+
// Helpers
11+
const pageExists = async (url: string) => {
12+
await page.navigateTo(url);
13+
const content = await page.getDocViewerText();
14+
return !/page not found/i.test(content);
15+
};
16+
17+
beforeEach(async () => {
18+
shortcuts = await page.getPwaShortcuts();
19+
});
20+
21+
it('should exist', async () => {
22+
for (const {short_name, url} of shortcuts) {
23+
expect(await pageExists(url)).toBe(
24+
true,
25+
`Page for shortcut '${short_name}' (from '${page.pwaManifestUrl}') does not exist. (URL: ${url})`);
26+
}
27+
});
28+
});
29+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { get as httpGet } from 'http';
2+
import { get as httpsGet } from 'https';
3+
import { browser } from 'protractor';
4+
import { SitePage } from './app.po';
5+
6+
7+
export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
8+
9+
/**
10+
* The shape of a PWA manifest.
11+
* For simplicity, we only define types for the properties we care about in tests.
12+
* @see https://developer.mozilla.org/en-US/docs/Web/Manifest
13+
*/
14+
export type PwaManifest = Json & {
15+
shortcuts?: PwaShortcutItem[],
16+
};
17+
18+
/**
19+
* The shape of an item in a PWA manifest's `shortcuts` list.
20+
* @see https://developer.mozilla.org/en-US/docs/Web/Manifest/shortcuts
21+
*/
22+
export type PwaShortcutItem = Json & {
23+
url: string,
24+
name: string,
25+
short_name?: string,
26+
description?: string,
27+
icons?: PwaImageResource[],
28+
};
29+
30+
/**
31+
* The shape of an item in a PWA manifest's icons list (such as the value of the top-level `icons` property or that of
32+
* the `icons` property of a shortcut item).
33+
* @see https://w3c.github.io/manifest/#manifestimageresource-and-its-members
34+
*/
35+
export type PwaImageResource = Json & {
36+
src: string,
37+
sizes?: string,
38+
type?: string,
39+
purpose?: string,
40+
};
41+
42+
43+
export class PwaManifestPage extends SitePage {
44+
/** The base URL with the trailing `/` stripped off (if any). */
45+
baseUrl = browser.baseUrl.replace(/\/$/, '');
46+
47+
/** The URL to the app's PWA manifest. */
48+
pwaManifestUrl = `${this.baseUrl}/pwa-manifest.json`;
49+
50+
private pwaManifestText: string | null = null;
51+
52+
/** Get the app's PWA manifest as an object. */
53+
async getPwaManifest(): Promise<PwaManifest> {
54+
if (this.pwaManifestText === null) {
55+
const get = /^https:/.test(this.pwaManifestUrl) ? httpsGet : httpGet;
56+
57+
this.pwaManifestText = await new Promise<string>((resolve, reject) => {
58+
let responseText = '';
59+
get(this.pwaManifestUrl, res => res
60+
.on('data', chunk => responseText += chunk)
61+
.on('end', () => resolve(responseText))
62+
.on('error', reject));
63+
});
64+
}
65+
66+
return JSON.parse(this.pwaManifestText);
67+
}
68+
69+
/** Get a list of PWA shortcuts as extracted from the app's PWA manifest. */
70+
async getPwaShortcuts(): Promise<PwaShortcutItem[]> {
71+
const {shortcuts = []} = await this.getPwaManifest();
72+
return shortcuts;
73+
}
74+
}

0 commit comments

Comments
 (0)