Skip to content
This repository was archived by the owner on Dec 18, 2024. It is now read-only.

Commit 6547eb2

Browse files
committed
ci: set up lighthouse
- define CircleCI version - update NodeJS docker image - use the branch as part of the cache_key - add the circleci/build-tools orb - store the build output for reuse in other jobs - add new job to run the lighthouse a11y audits on various pages - add new job to run all of the lighthouse audits on the home page - add typing and comments to the original scripts - update to the latest firebase-tools
1 parent 4c012b3 commit 6547eb2

File tree

6 files changed

+1521
-262
lines changed

6 files changed

+1521
-262
lines changed

.circleci/config.yml

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1+
# Note: YAML anchors allow an object to be re-used, reducing duplication.
2+
# The ampersand declares an alias for an object, then later the `<<: *name`
3+
# syntax dereferences it.
4+
# See https://blog.daemonl.com/2016/02/yaml.html
5+
# To validate changes, use an online parser, eg.
6+
# https://yaml-online-parser.appspot.com/
7+
8+
# CircleCI configuration version
9+
# Version 2.1 allows for extra config reuse features
10+
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
11+
version: 2.1
12+
113
# Cache key for CircleCI. We want to invalidate the cache whenever the Yarn lock file changes.
2-
var_1: &cache_key material-angular-io-{{ checksum "yarn.lock" }}
3-
var_2: &default_docker_image circleci/node:12.9.1-browsers
14+
var_1: &cache_key material-angular-io-{{ .Branch }}-{{ checksum "yarn.lock" }}
15+
var_2: &default_docker_image circleci/node:12.14.1-browsers
416

517
# Settings common to each job
618
var_3: &job_defaults
@@ -19,19 +31,41 @@ var_5: &yarn_install
1931
name: "Installing project dependencies"
2032
command: yarn install --frozen-lockfile --non-interactive
2133

22-
# Job step for checking out the source code from GitHub. This also ensures that the source code
23-
# is rebased on top of master.
24-
var_6: &checkout_code
25-
checkout:
26-
# After checkout, rebase on top of master. By default, PRs are not rebased on top of master,
27-
# which we want. See https://discuss.circleci.com/t/1662
28-
post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge"
34+
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
35+
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
36+
var_6: &workspace_location ~/
37+
38+
orbs:
39+
build-tools: circleci/[email protected]
40+
41+
commands:
42+
store_build_output:
43+
description: 'Stores build artifacts'
44+
steps:
45+
- run:
46+
name: Move compiled apps to workspace
47+
command: |
48+
set -exu
49+
mkdir -p ~/dist
50+
mv dist/* ~/dist/
51+
- persist_to_workspace:
52+
root: *workspace_location
53+
paths:
54+
- dist
55+
# Command for checking out the source code from GitHub. This also ensures that the source code
56+
# can be merged to the master branch without conflicts.
57+
checkout_and_rebase:
58+
description: Checkout and verify clean merge with master
59+
steps:
60+
- checkout
61+
- build-tools/merge-with-parent:
62+
parent: master
2963

3064
jobs:
3165
lint:
3266
<<: *job_defaults
3367
steps:
34-
- *checkout_code
68+
- checkout_and_rebase
3569
- restore_cache:
3670
key: *cache_key
3771
- *yarn_install
@@ -45,26 +79,55 @@ jobs:
4579
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
4680
resource_class: large
4781
steps:
48-
- *checkout_code
82+
- checkout_and_rebase
4983
- restore_cache:
5084
key: *cache_key
5185
- *yarn_install
5286
- run: yarn prod-build
5387
- *save_cache
88+
- store_build_output
5489

5590
test:
5691
<<: *job_defaults
5792
steps:
58-
- *checkout_code
93+
- checkout_and_rebase
5994
- restore_cache:
6095
key: *cache_key
6196
- *yarn_install
6297
- run: yarn test --watch false --progress=false
6398

99+
audit_pages_for_a11y:
100+
<<: *job_defaults
101+
steps:
102+
- attach_workspace:
103+
at: *workspace_location
104+
- checkout_and_rebase
105+
- restore_cache:
106+
key: *cache_key
107+
- *yarn_install
108+
- run: yarn test:audit:a11y:ci
109+
110+
lighthouse_audits:
111+
<<: *job_defaults
112+
steps:
113+
- attach_workspace:
114+
at: *workspace_location
115+
- checkout_and_rebase
116+
- restore_cache:
117+
key: *cache_key
118+
- *yarn_install
119+
- run: yarn test:audit:ci
120+
64121
workflows:
65122
version: 2
66123
default_workflow:
67124
jobs:
68125
- lint
69126
- build
70127
- test
128+
- audit_pages_for_a11y:
129+
requires:
130+
- build
131+
- lighthouse_audits:
132+
requires:
133+
- build

package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"start:jit": "ng serve --aot=false",
1010
"start:prod": "ng serve --prod",
1111
"start:scenes": "ng serve scenes",
12+
"start:static": "firebase serve --only hosting --port 4200",
1213
"lint": "ng lint",
1314
"test": "ng test",
1415
"pree2e": "webdriver-manager update",
@@ -22,7 +23,14 @@
2223
"postinstall": "webdriver-manager update --gecko false && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
2324
"publish-prod": "bash ./tools/deploy.sh stable prod",
2425
"publish-dev": "bash ./tools/deploy.sh",
25-
"publish-beta": "bash ./tools/deploy.sh stable beta"
26+
"publish-beta": "bash ./tools/deploy.sh stable beta",
27+
"test:audit:a11y": "node tools/audit-docs-a11y",
28+
"test:audit:a11y:localhost": "run-p --race \"~~light-server -s dist/material-angular-io -p 4200 --quiet\" \"test:audit:a11y http://localhost:4200\" --",
29+
"test:audit:a11y:ci": "run-p --race \"~~light-server -s ../dist/material-angular-io -p 4200 --quiet\" \"test:audit:a11y http://localhost:4200\" --",
30+
"test:audit": "node tools/audit-docs",
31+
"test:audit:localhost": "run-p --race \"start:static\" \"test:audit http://localhost:4200 2000\" --",
32+
"test:audit:ci": "run-p --race \"~~light-server -s ../dist/material-angular-io -p 4200 --quiet\" \"test:audit http://localhost:4200\" --",
33+
"~~light-server": "light-server --bind=localhost --historyindex=/index.html --no-reload"
2634
},
2735
"private": true,
2836
"dependencies": {
@@ -55,8 +63,9 @@
5563
"@angular/localize": "^11.0.0",
5664
"@types/jasmine": "^3.6.0",
5765
"@types/node": "^12.12.70",
66+
"@types/shelljs": "~0.8.8",
5867
"codelyzer": "^6.0.1",
59-
"firebase-tools": "^8.16.0",
68+
"firebase-tools": "^9.2.1",
6069
"jasmine-core": "^3.6.0",
6170
"jasmine-spec-reporter": "^5.0.2",
6271
"karma": "~5.1.1",
@@ -65,7 +74,13 @@
6574
"karma-firefox-launcher": "^2.1.0",
6675
"karma-jasmine": "^4.0.1",
6776
"karma-jasmine-html-reporter": "^1.5.4",
77+
"light-server": "^2.6.2",
78+
"lighthouse": "~7.0.0",
79+
"lighthouse-logger": "~1.2.0",
80+
"npm-run-all": "^4.1.5",
6881
"protractor": "^7.0.0",
82+
"puppeteer": "5.4.1",
83+
"shelljs": "^0.8.4",
6984
"ts-node": "^8.10.1",
7085
"tslint": "^6.1.2",
7186
"typescript": "4.0.2"

tools/audit-docs-a11y.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/env node
2+
'use strict';
3+
4+
/**
5+
* Originally from https://github.com/angular/angular/blob/b48eabddb83900496e5e00fb98e192c6c49dff26/aio/scripts/test-aio-a11y.js
6+
*
7+
* Usage:
8+
* ```sh
9+
* node tools/audit-docs-a11y <origin>
10+
* ```
11+
*
12+
* Runs accessibility audits on several (pre-defined) pages on the specified
13+
* origin. It fails, if the score for any page is below the minimum (see
14+
* `MIN_SCORES_PER_PAGE` below).
15+
*
16+
* `<origin>` is the origin (scheme + hostname + port) of an material.angular.io
17+
* deployment. It can be remote (e.g. `https://next.material.angular.io`) or local (e.g.
18+
* `http://localhost:4200`).
19+
*/
20+
21+
// Imports
22+
const sh = require('shelljs');
23+
sh.set('-e');
24+
25+
// Constants
26+
const MIN_SCORES_PER_PAGE = {
27+
'' : 100,
28+
'components/categories' : 91,
29+
'cdk/categories' : 91,
30+
'guides' : 100,
31+
'guide/creating-a-custom-form-field-control' : 99,
32+
'guide/getting-started' : 98,
33+
'cdk/a11y/overview' : 85,
34+
'cdk/a11y/api' : 89,
35+
'cdk/a11y/examples' : 85,
36+
'components/button/overview' : 92,
37+
'components/button/api' : 89,
38+
'components/button/examples' : 90,
39+
};
40+
41+
// Run the a11y audit against the above pages
42+
const lighthouseAuditCmd = `"${process.execPath}" "${__dirname}/lighthouse-audit"`;
43+
const origin = process.argv[2];
44+
for (const [page, minScore] of Object.entries(MIN_SCORES_PER_PAGE)) {
45+
sh.exec(`${lighthouseAuditCmd} ${origin}/${page} accessibility:${minScore}`);
46+
}

tools/audit-docs.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/env node
2+
'use strict';
3+
4+
/**
5+
* Usage:
6+
* ```sh
7+
* node tools/audit-docs <origin> <delay>
8+
* ```
9+
*
10+
* Runs the configured audits on several (pre-defined) pages on the specified
11+
* origin. It fails if any score for any page is below the minimum (see
12+
* `MIN_SCORES_PER_PAGE` below).
13+
*
14+
* `<origin>` is the origin (scheme + hostname + port) of an material.angular.io
15+
* deployment. It can be remote (e.g. `https://next.material.angular.io`) or local (e.g.
16+
* `http://localhost:4200`).
17+
*
18+
* `<delay>` is a millisecond value used with `setTimeout()` to allow a configurable delay
19+
* for the HTTP server to start up. This is needed when used with `firebase serve`.
20+
*/
21+
22+
// Imports
23+
const sh = require('shelljs');
24+
sh.set('-e');
25+
26+
// Constants
27+
/**
28+
* @type {{minScores: {performance: number, accessibility: number, 'best-practices': number, pwa: number, seo: number}, url: string}[]}
29+
*/
30+
const MIN_SCORES_PER_PAGE = [
31+
{
32+
url: '',
33+
minScores: {
34+
'pwa': 70,
35+
'performance': 21,
36+
'seo': 98,
37+
'best-practices': 100,
38+
'accessibility': 100
39+
}
40+
}
41+
];
42+
43+
/**
44+
* @param {{performance?: number, accessibility?: number, 'best-practices'?: number, pwa?: number, seo?: number}} scores
45+
* @returns string scores formatted as described in the docs of lighthouse-audit.js's _main()
46+
*/
47+
function formatScores(scores) {
48+
let formattedScores = '';
49+
Object.keys(scores).map((key, index) => {
50+
if (index > 0) {
51+
formattedScores += ',';
52+
}
53+
formattedScores += `${key}:${scores[key]}`;
54+
});
55+
return formattedScores;
56+
}
57+
58+
// Run the a11y audit against the above pages
59+
const lighthouseAuditCmd = `"${process.execPath}" "${__dirname}/lighthouse-audit"`;
60+
const origin = process.argv[2];
61+
const delay = process.argv[3];
62+
if (delay) {
63+
setTimeout(_main, delay);
64+
} else {
65+
_main();
66+
}
67+
68+
function _main() {
69+
MIN_SCORES_PER_PAGE.map((urlsAndScores) => {
70+
sh.exec(`${lighthouseAuditCmd} ${origin}/${urlsAndScores.url} ${formatScores(urlsAndScores.minScores)}`);
71+
});
72+
}

0 commit comments

Comments
 (0)