13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/2-scraping/7-wiki-philosophy/clean.js:
--------------------------------------------------------------------------------
1 | function clean(string) {
2 | let d = 0;
3 | let k = 0;
4 | let c;
5 | let out = '';
6 | for (const element of string) {
7 | c = element;
8 |
9 | if (d < 1) {
10 | if (c === '>') {
11 | k -= 1;
12 | }
13 |
14 | if (c === '<') {
15 | k += 1;
16 | }
17 | }
18 |
19 | if (k < 1) {
20 | if (c === '(') {
21 | d += 1;
22 | }
23 |
24 | if (d > 0) {
25 | out += ' ';
26 | } else {
27 | out += c;
28 | }
29 |
30 | if (c === ')') {
31 | d -= 1;
32 | }
33 | } else {
34 | out += c;
35 | }
36 | }
37 |
38 | return out.replace(/'/g, '').trim();
39 | }
40 |
41 | export default clean;
42 |
--------------------------------------------------------------------------------
/2-scraping/7-wiki-philosophy/index.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer';
2 | import clean from './clean.js';
3 |
4 | const browser = await puppeteer.launch({
5 | headless: false
6 | });
7 |
8 | const page = await browser.newPage();
9 |
10 | await page.setViewport({ width: 1280, height: 720 });
11 |
12 | await page.goto('http://en.wikipedia.org/wiki/Special:Random');
13 | // await page.goto('http://en.wikipedia.org/wiki/Aircraft');
14 |
15 | await page.exposeFunction('clean', clean);
16 |
17 | async function handleLink() {
18 | const firstLinkSelector = '.mw-parser-output > p > a[title]';
19 | const h1 = await page.$eval('h1', el => el.innerText);
20 |
21 | console.log('> ', h1);
22 |
23 | if (h1 === 'Philosophy') {
24 | return;
25 | }
26 |
27 | await page.evaluate(async () => {
28 | const para = document.querySelector('.mw-parser-output > p:not(.mw-empty-elt)');
29 | para.innerHTML = await clean(para.innerHTML);
30 | });
31 |
32 | await Promise.all([
33 | page.waitForNavigation({
34 | waitUntil: ['domcontentloaded']
35 | }),
36 | page.click(firstLinkSelector)
37 | ]);
38 |
39 | return handleLink();
40 | }
41 |
42 | await handleLink();
43 | await browser.close();
44 |
--------------------------------------------------------------------------------
/2-scraping/7-wiki-philosophy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code",
3 | "type": "module",
4 | "scripts": {
5 | "start": "node --experimental-top-level-await index"
6 | }
7 | }
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/2-scraping/7-wiki-philosophy/readme.md:
--------------------------------------------------------------------------------
1 |
2 | ## To run
3 |
4 | ```sh
5 | npm start
6 |
7 | # or
8 |
9 | yarn start
10 | ```
--------------------------------------------------------------------------------
/2-scraping/8-product-price-checker/.env.sample:
--------------------------------------------------------------------------------
1 | AIRTABLE_API_KEY=
2 | AIRTABLE_BASE=
--------------------------------------------------------------------------------
/2-scraping/8-product-price-checker/index.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer';
2 | import notifier from 'node-notifier';
3 | import Airtable from 'airtable';
4 | import dotenv from 'dotenv';
5 |
6 | dotenv.config();
7 |
8 | let base;
9 |
10 | const AIRTABLE_API_KEY = process.env.AIRTABLE_API_KEY;
11 | const AIRTABLE_BASE = process.env.AIRTABLE_BASE;
12 |
13 | const shouldSaveToCloud = AIRTABLE_API_KEY && AIRTABLE_BASE;
14 |
15 | const airtableConfig = {
16 | apiKey: AIRTABLE_API_KEY
17 | };
18 |
19 | if (shouldSaveToCloud) {
20 | base = new Airtable(airtableConfig).base(AIRTABLE_BASE);
21 | }
22 |
23 | const browser = await puppeteer.launch({headless: true});
24 | const page = await browser.newPage();
25 |
26 | function sleep(ms = 1000) {return new Promise((resolve) => setTimeout(resolve, ms))};
27 |
28 | let price = 0;
29 | let maxChecks = 5;
30 | let checksMade = 0;
31 |
32 | async function getLatestPrice() {
33 | await page.goto('https://automatebrowsers.com/amazon/cat-mug/');
34 | await page.waitForSelector('#price_inside_buybox');
35 | const latestPrice = await page.$eval('#price_inside_buybox', el => {
36 | const priceAsFloat = parseFloat(el.textContent.substr(1));
37 | return Math.round(priceAsFloat);
38 | });
39 |
40 | return latestPrice;
41 | }
42 |
43 | async function logPriceChange(latestPrice) {
44 | if (price === latestPrice) {
45 | return;
46 | }
47 |
48 | const priceDifference = latestPrice - price;
49 |
50 | if (priceDifference > 0) {
51 | console.log(`Price increase $${latestPrice} (+${priceDifference})`);
52 | } else {
53 | const message = `Price decrease $${latestPrice} (${priceDifference})`;
54 | console.log(message);
55 | notifier.notify(message);
56 | }
57 |
58 | if (shouldSaveToCloud) {
59 | await base('Amazon Price Updates').create([{
60 | "fields": {
61 | "Price": latestPrice,
62 | "Time": new Date().toString()
63 | }
64 | }]);
65 | }
66 | }
67 |
68 | async function check() {
69 | if (checksMade <= maxChecks ) {
70 | checksMade++;
71 | const latestPrice = await getLatestPrice();
72 | if (checksMade > 1) await logPriceChange(latestPrice);
73 | price = latestPrice;
74 | await sleep(2000);
75 | return check();
76 | } else {
77 | console.log('Closing browser');
78 | await browser.close();
79 | }
80 | }
81 |
82 | await check();
83 |
--------------------------------------------------------------------------------
/2-scraping/8-product-price-checker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code",
3 | "type": "module",
4 | "scripts": {
5 | "start": "node --experimental-top-level-await index"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/2-scraping/8-product-price-checker/readme.md:
--------------------------------------------------------------------------------
1 |
2 | ## Amazon Price Checker
3 |
4 | ### Goals
5 |
6 | We want to build a price checker for an Amazon product. The Amazon product is hosted on this standalone page, which is suitable for frequent scraping: [Amazon: Cat mug](https://automatebrowsers.com/amazon/cat-mug/).
7 |
8 | 1. Write a node.js script which automates a browser to scrape the Amazon price of the cat mug
9 | 2. The script should not close, instead, it can check for price changes at a specified interval (the price changes on page reload for testing purposes)
10 | 3. The script could optionally use something like `node-notifier` to notify the user of changes to the price
11 | 4. The script could optionally save prices to the cloud, using a service like [Airtable](https://airtable.com) - they have a generous free plan
12 |
13 | ### Instructions
14 |
15 | ```sh
16 | npm start # or yarn start
17 | ```
18 |
19 | ### Want to save results to the cloud?
20 |
21 | [Airtable Table Example](https://airtable.com/shrHejfReBwZPavxA)
22 |
23 | 1. Make a free account at airtable.com
24 | 2. Copy `.env.sample` to `.env`
25 | 3. Fill it with values from: https://airtable.com/api (you'll need your 'base' and api key)
26 |
27 |
--------------------------------------------------------------------------------
/2-scraping/9-offline-websites/index.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer';
2 |
3 | const browser = await puppeteer.connect({
4 | browserURL: 'http://localhost:9222/',
5 | headless: false
6 | });
7 |
8 | const page = await browser.newPage();
9 | await page.setCacheEnabled(false);
10 |
11 | await page.goto('https://umaar.com/dev-tips/');
12 |
13 | /* Interact with the offline page below */
--------------------------------------------------------------------------------
/2-scraping/9-offline-websites/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code",
3 | "type": "module",
4 | "scripts": {
5 | "start": "node ../../node_modules/archivist1/index.js",
6 | "interact": "node --experimental-top-level-await index"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/2-scraping/9-offline-websites/readme.md:
--------------------------------------------------------------------------------
1 |
2 | ## To run
3 |
4 | 1. Start 22120
5 |
6 | ```sh
7 | npm start
8 |
9 | # or
10 |
11 | yarn start
12 | ```
13 |
14 | 2. Open [http://localhost:22120/](http://localhost:22120/) and ensure it's in __Save mode__
15 |
16 | 3. Navigate to any page(s) you wish to scrape/interact with through code. Visiting such websites automatically saves them
17 |
18 | 4. Set the mode to __Serve mode__
19 |
20 | 5. In a new terminal tab, within the `2-scraping/9-offline-websites` folder, run:
21 |
22 | ```sh
23 | npm run interact
24 |
25 | # or
26 |
27 | yarn interact
28 | ```
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/README.md:
--------------------------------------------------------------------------------
1 | - [Notes](#notes)
2 | - [1. Upload lighthouse results](#1-upload-lighthouse-results)
3 | - [2. Upload lighthouse results locally](#2-upload-lighthouse-results-locally)
4 | - [2.1. Start lighthouse CI](#21-start-lighthouse-ci)
5 | - [2.2. Configure lighthouse CI](#22-configure-lighthouse-ci)
6 | - [2.3. Upload lighthouse results locally](#23-upload-lighthouse-results-locally)
7 | - [Windows alternative](#windows-alternative)
8 | - [3. Run lighthouse CI in the cloud](#3-run-lighthouse-ci-in-the-cloud)
9 | - [3.1. Setup heroku](#31-setup-heroku)
10 | - [3.2. Deploy lighthouse CI to heroku (free)](#32-deploy-lighthouse-ci-to-heroku-free)
11 | - [3.3. Run the lighthouse wizard](#33-run-the-lighthouse-wizard)
12 | - [3.4. Upload lighthouse results to heroku](#34-upload-lighthouse-results-to-heroku)
13 | - [Windows alternative](#windows-alternative-1)
14 | - [4. Connect lighthouse with github](#4-connect-lighthouse-with-github)
15 | - [4.1. Add an action file](#41-add-an-action-file)
16 | - [4.2. Enable the status check](#42-enable-the-status-check)
17 | - [4.3. Make lighthouse mandatory](#43-make-lighthouse-mandatory)
18 | - [5. Finishing up](#5-finishing-up)
19 | - [Example dashboard](#example-dashboard)
20 |
21 |
22 | # Lighthouse CI
23 |
24 | 🔥️ This uses completely free services.
25 |
26 | The definitive guide to automated performance testing using Lighthouse, GitHub Actions and Heroku.
27 |
28 | |  |
29 | |:--:|
30 | | *Preview of what we'll achieve* |
31 |
32 | ## Notes
33 |
34 | - Please take time to understand how the commands work, inspect the `package.json` file, and approach with a willingness to debug. This is not just copy-and-paste everything and it magically works.
35 | - You will need to read external documentation, such as that for lighthouse, to get this working fully.
36 | - You'll need to adapt some of the commands/instructions to run this in your own fresh repository. It will help to read through this whole guide, taking notes of the commands might need changing.
37 | - Windows users should ensure they have git installed.
38 |
39 |
40 | __The end goal__: At the end of this, you'll be able to run standard Lighthouse audits, but also __custom audits created by you__.
41 |
42 | You can run performance checks, accessibility checks, security checks, on each pull request to your GitHub repo.
43 |
44 | ### Which website should you test?
45 |
46 | _Some_ of the commands can be run within this folder (`3-auditing/1-lighthouse`) - there's a small `express` web server which has some random HTML and images, and is currently what Lighthouse is auditing.
47 |
48 | It would be helpful to audit __your own website__. Consider these options:
49 |
50 | - Use your own website, whether it's dynamic or a set of static pages. Ideally, you want to be able to add lots of large images to your page, and then immediately see how the Lighthouse scores are affected. Pay attention to the `--collect.startServerCommand` flag throughout these instructions.
51 | - If you don't have your own website to test, you can just use a boilerplate, e.g. here's [express + nunjucks](https://github.com/iamstuartwilson/express-nunjucks-boilerplate), but using any sort of boilerplate is fine, ideally something simple which can be started via `npm start`.
52 | - If you do not have your own website, and you don't wan't to copy from a boilerplate, you could in theory test a publicly facing URL. Just pay attention to the `--collect.url` flag you'll see throughout these instructions.
53 |
54 | For now, you can run commands in this `3-auditing/1-lighthouse` folder, it will become clear when you need to add in your own repo.
55 |
56 | ---
57 |
58 | ## 1. Upload lighthouse results
59 |
60 | |  |
61 | |:--:|
62 | | *Preview of a single Lighthouse report* |
63 |
64 | Run lighthouse and upload results to their public server.
65 |
66 | While the Lighthouse CI tool does support extra config, let's just run this as one command:
67 |
68 | ```sh
69 | # from 3-auditing/1-lighthouse
70 | npm run lighthouse # or yarn lighthouse
71 | ```
72 |
73 | 💡️ Go and check what the `lighthouse` script does, in the `package.json`, it's important to understand the following:
74 |
75 | - [startServerCommand](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#startservercommand)
76 | - [target](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#target)
77 | - [url](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#url)
78 | - [numberOfRuns](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#numberofruns)
79 |
80 | Before you continue, please understand each part of the `npm run lighthouse` command.
81 |
82 | ---
83 |
84 | ## 2. Upload lighthouse results locally
85 |
86 | Upload results to your local lighthouse server.
87 |
88 | ### 2.1. Start lighthouse CI
89 |
90 | |  |
91 | |:--:|
92 | | *Lighthouse CI can show how metrics are improving or worsening over time* |
93 |
94 | Start the lighthouse CI server on your local machine.
95 |
96 | ```sh
97 | # from 3-auditing/1-lighthouse
98 | npm run lighthouse-local-server # or yarn lighthouse-local-server
99 | ```
100 |
101 | Now: check the app is running @ http://localhost:9001
102 |
103 | 💡️ As usual, go and check what the `lighthouse-local-server` command does (in `package.json`).
104 |
105 | ### 2.2. Configure lighthouse CI
106 |
107 | Configure the lighthouse CI server. The lighthouse wizard tool can configure your lighthouse CI instance, both locally and remotely.
108 |
109 | In a new terminal tab:
110 |
111 | ```sh
112 | # from 3-auditing/1-lighthouse
113 | npm run lighthouse-wizard # or yarn lighthouse-wizard
114 | ```
115 |
116 | I used the following answers, you can substitute the appropriate values for your own:
117 |
118 |
119 | ```
120 | ? Which wizard do you want to run? new-project
121 | ? What is the URL of your LHCI server? http://localhost:9001
122 | ? What would you like to name the project? learn-browser-testing
123 | ? Where is the project's code hosted? https://github.com/umaar/learn-browser-testing
124 | ? What branch is considered the repo's trunk or main branch? master
125 | ```
126 |
127 | 💡️ You might want to use your own repository when answering those questions.
128 |
129 | After executing that, take note of the `build token`.
130 |
131 | ### 2.3. Upload lighthouse results locally
132 |
133 | |  |
134 | |:--:|
135 | | *The Lighthouse CI dashboard gives a handy overview of your page scores* |
136 |
137 | Run lighthouse and upload the results to your __local__ lighthouse CI server:
138 |
139 | Run the following command, and be sure to substitute `[YOUR_TOKEN]` for your actual `build token`.
140 |
141 | 💡️ __Important__ - Take time to understand what this command is doing, and how it works:
142 |
143 | - [serverBaseUrl](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#serverbaseurl)
144 | - [token](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#token)
145 | - What is the difference between `--upload.target=lhci` and `--upload.target=temporary-public-storage`?
146 |
147 | ```sh
148 | # from 3-auditing/1-lighthouse
149 | ../../node_modules/.bin/lhci autorun \
150 | --collect.numberOfRuns=1 \
151 | --collect.startServerCommand="npm start" \
152 | --collect.url="http://localhost:3000" \
153 | --upload.target=lhci \
154 | --upload.serverBaseUrl="http://127.0.0.1:9001" \
155 | --upload.token="[YOUR_TOKEN]"
156 | ```
157 |
158 | #### Windows alternative
159 |
160 | ```sh
161 | ..\\..\\node_modules\\.bin\\lhci autorun ^
162 | --collect.numberOfRuns=1 ^
163 | --collect.startServerCommand="npm start" ^
164 | --collect.url="http://localhost:3000" ^
165 | --upload.target=lhci ^
166 | --upload.serverBaseUrl="http://127.0.0.1:9001" ^
167 | --upload.token="[YOUR_TOKEN]"
168 | ```
169 |
170 | Now, you can verify the results on your local lighthouse CI server, e.g. at http://localhost:9001
171 |
172 | ---
173 |
174 | ## 3. Run lighthouse CI in the cloud
175 |
176 | This runs the tool which powers the lighthouse dashboard, to the cloud. Earlier, we ran `npm run lighthouse-local-server` - we're going to run that server online through a hosting platform.
177 |
178 | On this occasion, we'll use [heroku](https://www.heroku.com/) since it has a free tier.
179 |
180 | ### 3.1. Setup heroku
181 |
182 | - Make a [heroku.com](heroku.com) account
183 | - Install their [CLI tool](https://devcenter.heroku.com/articles/heroku-cli)
184 |
185 | ### 3.2. Deploy lighthouse CI to heroku (free)
186 |
187 | The lighthouse CI dashboard is completely independent of this `learn-browser-testing` repo, therefore, clone the lhci-heroku starter kit __outside__ of this current project. Instructions below:
188 |
189 | ```sh
190 | # For example, in ~/code or wherever your code projects live
191 | git clone https://github.com/umaar/lhci-heroku.git
192 | cd lhci-heroku
193 |
194 | # run this command just once
195 | heroku login
196 |
197 | # Create your new project in heroku
198 | heroku create
199 |
200 | # Create a new database (https://devcenter.heroku.com/articles/heroku-postgresql#provisioning-heroku-postgres)
201 | heroku addons:create heroku-postgresql:hobby-dev
202 |
203 | # The "git remote" named "heroku" is automatically configured, you just need to run `git push heroku master` to push to the heroku servers
204 | git push heroku master
205 |
206 | # Finally, start the app
207 | heroku ps:scale web=1
208 | ```
209 |
210 | ### 3.3. Run the lighthouse wizard
211 |
212 | Previously, we ran the lighthouse wizard to configure a __local instance__ of the lighthouse CI tool. Now, we'll use that exact same wizard to configure the __remote heroku instance__ of the lighthouse CI tool.
213 |
214 | ```sh
215 | # While in the `lhci-heroku` folder, run:
216 | npm install
217 | npx lhci wizard
218 | ```
219 |
220 | I gave these answers. In the answers below, configure the URL so it points to the platform you've deployed to Heroku.
221 |
222 | ```
223 | ? Which wizard do you want to run? new-project
224 | ? What is the URL of your LHCI server? https://salty-headland-92476.herokuapp.com/
225 | ? What would you like to name the project? lhci-heroku
226 | ? Where is the project's code hosted? https://github.com/umaar/lhci-heroku
227 | ? What branch is considered the repo's trunk or main branch? master
228 | ```
229 |
230 | Take note of the tokens which are presented to you.
231 |
232 | ### 3.4. Upload lighthouse results to heroku
233 |
234 | At this point, you have:
235 |
236 | - Run lighthouse and uploaded results to their (the lighthouse team) public temporary storage
237 | - Run lighthouse and uploaded results to your local instance of lighthouse ci
238 |
239 | Now, you will run lighthouse and upload the results to your __heroku lighthouse CI server__:
240 |
241 | - Substitute `[YOUR_TOKEN]` for your actual `build token`.
242 | - Substitute `[YOUR BASE URL]` for your heroku URL.
243 |
244 | ```sh
245 | # Back in 3-auditing/1-lighthouse
246 | ../../node_modules/.bin/lhci autorun \
247 | --collect.numberOfRuns=1 \
248 | --collect.startServerCommand="npm start" \
249 | --collect.url="http://localhost:3000" \
250 | --upload.target=lhci \
251 | --upload.serverBaseUrl="[YOUR BASE URL]" \
252 | --upload.token="[YOUR_TOKEN]"
253 | ```
254 |
255 | #### Windows alternative
256 |
257 | ```sh
258 | # Back in 3-auditing/1-lighthouse
259 | ..\\..\\node_modules\\.bin\\lhci autorun ^
260 | --collect.numberOfRuns=1 ^
261 | --collect.startServerCommand="npm start" ^
262 | --collect.url="http://localhost:3000" ^
263 | --upload.target=lhci ^
264 | --upload.serverBaseUrl="[YOUR BASE URL]" ^
265 | --upload.token="[YOUR_TOKEN]"
266 | ```
267 |
268 | Be sure to verify the results on your heroku lighthouse CI server.
269 |
270 | ---
271 |
272 | ## 4. Connect lighthouse with github
273 |
274 | |  |
275 | |:--:|
276 | | *We'll configure GitHub to require successful checks for merging* |
277 |
278 | You can do this for your own repository, or just follow along by observing.
279 |
280 | Starting with GitHub actions, you need to add an actions file.
281 |
282 | ### 4.1. Add an action file
283 |
284 | This [action file](https://github.com/umaar/learn-browser-testing/blob/master/.github/workflows/lighthouse-ci.yaml) is a sensible starting point. Just add it in your repo, under `.github/workflows/lighthouse-ci.yaml`.
285 |
286 | 💡️ If you've never used GitHub actions before, spend a bit of time [reading about them](https://docs.github.com/en/actions/reference).
287 |
288 | Note the final run command which I've used:
289 |
290 | ```sh
291 | npm run --prefix 3-auditing/1-lighthouse lighthouse-private-with-error
292 | ```
293 |
294 | I've resorted to this command because of the directory structure of this particular repository, and because that command above is run from the `root` of the repo.
295 |
296 | If you're doing this in your own repo, you'll want to simplify that command to something like `npm test` or `npm run lighthouse`, and make sure the relevant script definition is in your `package.json`.
297 |
298 | ### 4.2. Enable the status check
299 |
300 | |  |
301 | |:--:|
302 | | *We'll configure GitHub to require that all checks were successful before merging* |
303 |
304 | Now, configure a Lighthouse 'status' message to appear under pull requests. This can inform you whether or not the pull request passes the lighthouse audit.
305 |
306 | 1. Open https://github.com/apps/lighthouse-ci
307 | 2. Click `Configure`
308 | 3. Enable for the repo you are interested in
309 | 4. Click authorise
310 |
311 | Observe the message like:
312 |
313 | ```
314 | Authorized
315 | Save the token below in a safe place.
316 | This is the only time it will be visible to you!
317 | Store the token as LHCI_GITHUB_APP_TOKEN in your build environment.
318 |
319 | abc:123
320 | ```
321 |
322 | 5. Add the token as a GitHub secret, e.g. https://github.com/umaar/learn-browser-testing/settings/secrets/new
323 |
324 | + Token name = `LHCI_GITHUB_APP_TOKEN`
325 | + Value = `[value from the message you saw earlier]`
326 |
327 |
328 | 💡️ Understand why we are adding this as a secret. Hint, we added `LHCI_GITHUB_APP_TOKEN` in our actions file which exposes this as an [environment variable](https://docs.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables).
329 |
330 | ### 4.3. Make lighthouse mandatory
331 |
332 | Make the status check mandatory for merging a PR
333 |
334 | If the status check reports a failure, by default, this will not block pull requests from being merged. We can change this behaviour:
335 |
336 | 1. Add a new [protection rule](https://github.com/umaar/learn-browser-testing/settings/branch_protection_rules/new).
337 | + GitHub Repo > Settings > Branches > Branch protection rules > Add rule
338 | 2. Enter the following:
339 | + Branch name pattern = *
340 | + Require status checks to pass before merging = enabled
341 | + Require branches to be up to date before merging = enabled
342 | + Enable the status checks = Lighthouse CI and lhci/url/
343 |
344 | ---
345 |
346 | ## 5. Finishing up
347 |
348 | |  |
349 | |:--:|
350 | | *GitHub provides full console output logs so you can debug* |
351 |
352 | That was quite a few steps, but it should all be working now.
353 |
354 | Test this by making a PR to your repo, do you see the Lighthouse status checks? You can use their assertions feature (e.g. fail when this performance metric is too low) to block pull requests from merging.
355 |
356 | Here's a good way to check, by failing on the `heading-order` error. It's simple enough that you can add in some HTML like this to make the build pass/fail:
357 |
358 | ```html
359 |
heading 2
360 |
heading 1
361 |
heading 3
362 | ```
363 |
364 | Here's the command to run:
365 |
366 | ```sh
367 | # from 3-auditing/1-lighthouse, or run this in your own repo
368 | # this is using my personal token and personal dashboard, feel free to swap with your own
369 | ../../node_modules/.bin/lhci autorun \
370 | --collect.numberOfRuns=1 \
371 | --collect.startServerCommand='npm start' \
372 | --collect.url='http://localhost:3000' \
373 | --upload.target=lhci \
374 | --upload.serverBaseUrl='[YOUR_HEROKU_URL]' \
375 | --upload.token='[YOUR_TOKEN]' \
376 | --assert.assertions.heading-order=error
377 | ```
378 |
379 | 💡️ Read up on the [`assertions`](https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#assertions) feature of the lighthouse ci tool. The `--assert.assertions` flag is what can instruct the PR to fail if the audit does not pass, so be sure to understand it.
380 |
381 | Finally, send me your repository! I can submit a PR and try to get things to fail/pass.
382 |
383 | ---
384 |
385 | ## Example dashboard
386 |
387 | Here's [my dashboard](https://salty-headland-92476.herokuapp.com/).
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import nunjucks from 'nunjucks';
3 |
4 | const app = express();
5 |
6 | // Setup nunjucks templating engine
7 | nunjucks.configure('views', {
8 | autoescape: true,
9 | express: app
10 | });
11 |
12 | app.set('port', process.env.PORT || 3000);
13 |
14 | // Home page
15 | app.get('/', function(req, res) {
16 | res.render('index.html', {
17 | page: 'home',
18 | port: app.get('port')
19 | });
20 | });
21 |
22 | // Other example
23 | app.get('/example', function(req, res) {
24 | res.render('example.html', {
25 | page: 'example',
26 | port: app.get('port')
27 | });
28 | });
29 |
30 | // Kick start our server
31 | app.listen(app.get('port'), function() {
32 | console.log('Server ready on port', app.get('port'));
33 | });
34 |
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code",
3 | "type": "module",
4 | "scripts": {
5 | "start": "node --experimental-top-level-await index",
6 | "lighthouse": "../../node_modules/.bin/lhci autorun --collect.numberOfRuns=1 --collect.startServerCommand=\"npm start\" --collect.url=\"http://localhost:3000\" --upload.target=temporary-public-storage",
7 | "lighthouse-with-error": "../../node_modules/.bin/lhci autorun --collect.numberOfRuns=1 --collect.startServerCommand=\"npm start\" --collect.url=\"http://localhost:3000\" --upload.target=temporary-public-storage --assert.assertions.heading-order=error",
8 | "lighthouse-private": "../../node_modules/.bin/lhci autorun --collect.numberOfRuns=1 --collect.startServerCommand=\"npm start\" --collect.url=\"http://localhost:3000\" --upload.target=lhci --upload.serverBaseUrl=\"https://salty-headland-92476.herokuapp.com\" --upload.token=\"02fd25fc-e007-4ef9-9d88-eec9fa59f966\"",
9 | "lighthouse-private-with-error": "../../node_modules/.bin/lhci autorun --collect.numberOfRuns=1 --collect.startServerCommand=\"npm start\" --collect.url=\"http://localhost:3000\" --upload.target=lhci --upload.serverBaseUrl=\"https://salty-headland-92476.herokuapp.com\" --upload.token=\"02fd25fc-e007-4ef9-9d88-eec9fa59f966\" --assert.assertions.heading-order=error",
10 | "lighthouse-local": "../../node_modules/.bin/lhci autorun --collect.numberOfRuns=1 --collect.startServerCommand=\"npm start\" --collect.url=\"http://localhost:3000\" --upload.target=lhci --upload.serverBaseUrl=\"http://127.0.0.1:9001\" --upload.token=\"5ae28497-95ed-4aa1-93c4-525a1893b643\"",
11 | "lighthouse-local-server": "node ../../node_modules/@lhci/cli/src/cli.js server --storage.storageMethod=sql --storage.sqlDialect=sqlite --storage.sqlDatabasePath=./db.sql",
12 | "lighthouse-wizard": "node ../../node_modules/@lhci/cli/src/cli.js wizard"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/all-checks-passing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/all-checks-passing.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/ga-fail-log.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/ga-fail-log.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/incomplete-checks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/incomplete-checks.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/lh-dashboard-third-party-increase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/lh-dashboard-third-party-increase.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/lh-single-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/lh-single-report.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/lhci-dashboard-changes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/lhci-dashboard-changes.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/lhci-dashboard-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/lhci-dashboard-overview.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/pics/some-checks-not-successful.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umaar/learn-browser-testing/4d67c76a80d9bcb5fb13e0b60db581ed25822403/3-auditing/1-lighthouse/pics/some-checks-not-successful.png
--------------------------------------------------------------------------------
/3-auditing/1-lighthouse/views/example.html:
--------------------------------------------------------------------------------
1 | {% extends 'index.html' %}
2 |
3 | {% block content %}
4 | {{ super() }}
5 |