Performance Testing Authenticated Pages with Lighthouse

Claire Tran
4 min readDec 13, 2020

--

This is a continuation of the performance testing series that I have been adding to.

This will be a quick post covering how to run automated

  • Lighthouse tests on the command line
  • Lighthouse tests against a performance budget
  • Running Lighthouse tests for authenticated pages

1) Running Lighthouse Tests via the CLI

Setup

For this example, you will need

  • Yarn
  • Node 13.12.0

First create a project

mkdir <project-name>
cd <project-name>

If you are using asdf

asdf install nodejs 13.12.0echo v13.12.0 > .tool-versions

Then add lighthouse as a dependency

yarn global add lighthouse

Next, test this out against a URL

lighthouse <url>

There should be a HTML file listed e.g. www.theguardian.com.au_2020-12-13_17-32-36.report.html

If you open the file in your browser, you’ll see something like this

I won’t go into detail about each metric listed. Refer to

JSON results

You also have the option of outputting the test results into a JSON file and in headless mode. Next run

lighthouse <url> --quiet --chrome-flags="--headless" --output json --output html

You’ll see the results

The JSON file will contain the same information, for example speed index

"speed-index": {
"id": "speed-index",
"title": "Speed Index",
"description": "Speed Index shows how quickly the contents of a page are visibly populated. [Learn more](https://web.dev/speed-index/).",
"score": 0.03,
"scoreDisplayMode": "numeric",
"numericValue": 12599.883495028384,
"numericUnit": "millisecond",
"displayValue": "12.6 s"
}

2) Running against a performance budget

Next define a budget

touch budget.json

Contents:

[
{
"path": "/*",
"timings": [
{
"metric": "interactive",
"budget": 3000
},
{
"metric": "first-meaningful-paint",
"budget": 1000
}
]
}
]

Then run on the command line with an additional option

--budget-path=./budget.json

Example:

lighthouse <url> --quiet --chrome-flags="--headless" --output json --output html --budget-path=./budget.json

You will see a new HTML and JSON result file, with budget results

Example:

"timing-budget": {
"id": "timing-budget",
"title": "Timing budget",
"description": "Set a timing budget to help you keep an eye on the performance of your site. Performant sites load fast and respond to user input events quickly. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
"score": null,
"scoreDisplayMode": "informative",
"details": {
"type": "table",
"headings": [
{
"key": "label",
"itemType": "text",
"text": "Metric"
},
{
"key": "measurement",
"itemType": "ms",
"text": "Measurement"
},
{
"key": "overBudget",
"itemType": "ms",
"text": "Over Budget"
}
],
"items": [
{
"metric": "interactive",
"label": "Time to Interactive",
"measurement": 18110,
"overBudget": 15110
},
{
"metric": "first-meaningful-paint",
"label": "First Meaningful Paint",
"measurement": 5030,
"overBudget": 4030
}
]
}
},

The above scenarios work against public pages, however there will be times when you want to test against authenticated pages.

3) Authenticated Pages

There are a few mechanisms to test against authenticated pages (see: https://github.com/GoogleChrome/lighthouse/blob/master/docs/authenticated-pages.md).

The below example will use Puppeteer in conjunction with Lighthouse to test authenticated pages.

First, add Puppeteer to your dependencies

yarn add puppeteer

Then create a new file that will run tests e.g. auth.js

const fs = require('fs');
const puppeteer = require('puppeteer');
const lighthouse = require('lighthouse');
const PORT = 8041;//to be filled in or as an environment variable
const EMAIL = ''
const PASSWORD = ''
const LOGINURL = ''
const TARGETURL = ''

Then add a function to login

async function login(browser, origin) {
const page = await browser.newPage();
await page.goto(origin);
await page.waitForSelector('input[type="email"]', {visible: true});
const emailInput = await page.$('input[type="email"]');
await emailInput.type(EMAIL);

const passwordInput = await page.$('input[type="password"]');
await passwordInput.type(PASSWORD);
await Promise.all([
page.click('#submit'),
page.waitForNavigation(),
]);
await page.close();
}

Then create a function that will run puppeteer and lighthouse

async function main() {

const browser = await puppeteer.launch({
args: [`--remote-debugging-port=${PORT}`],
headless: false, //can be set to true
slowMo: 50,
});
await login(browser, LOGINURL);
const url = TARGETURL;

const options = {port: PORT, disableStorageReset: true, output: 'html'}
const result = await lighthouse(url, options);
await browser.close();
const reportHtml = result.report;
fs.writeFileSync('report.html', reportHtml);
}

Then after that invoke the main function and export other functions

if (require.main === module) {
try{
main();
} catch(e) {
console.log(e);
}
} else {
module.exports = {
login,
logout,
};
}

Next run this file on the command line

node auth.js

You should see the output

What happens next?

Here are a few options, for what you can do

  • CI: You can setup automated tests to run in your build pipeline or as part of a nightly performance test.
  • Reporting: The result files can be parsed into reports which can be used to track trends in performance
  • Dashboards: The results can be pushed to a dashboard e.g. Grafana backed by a DB

I’m sure there are other uses which could fit your use case :)

--

--

Claire Tran
Claire Tran

Written by Claire Tran

Engineering Leader | Writer | Speaker | Traveller. Passionate about growing opportunities for people in Tech.