Cypress Lighthouse Example

Getting the Lighthouse performance metrics from a Cypress test.

You can find the Cypress tests shown in this blog post in the repo bahmutov/cypress-lighthouse-example.

Let's see how fast my blog https://glebbahmutov.com/blog/ loads in the browser. I will use Cypress to visit the site and use a plugin @cypress-audit/lighthouse to report the performance metrics.

In the tests repository, install Cypress and the plugin

1
2
3
$ npm i -D cypress @cypress-audit/lighthouse
+ [email protected]
+ @cypress-audit/[email protected]

Following the plugin's README, I have added its initialization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// cypress/support/index.js
import '@cypress-audit/lighthouse/commands'
// cypress/plugins/index.js
const { lighthouse, prepareAudit } = require('@cypress-audit/lighthouse')

module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, launchOptions) => {
prepareAudit(launchOptions)
})

on('task', {
lighthouse: lighthouse(), // calling the function is important
})
}

The test

The moment of truth. My test should visit the site and run the Lighthouse.

cypress/integration/spec.js
1
2
3
4
5
6
/// <reference types="@cypress-audit/lighthouse" />

it('loads fast enough', () => {
cy.visit('/')
cy.lighthouse()
})

Tip: I am using the special "reference types" comment to load the TypeScript definitions that come with the Lighthouse plugin to load the definition for its custom command cy.lighthouse

Custom command cy.lighthouse IntelliSense

Open Cypress Test Runner with npx cypress open and pick the Chrome browser to run the tests.

Lighthouse audit requires Chrome browser to run

Launch the spec file. Ughh, we definitely did not hit 100 across each category.

Lighthouse audit with default parameters

By default, Lighthouse audits in the mobile mode and requires each score to equal 100. Let's change the thresholds to match our situation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <reference types="@cypress-audit/lighthouse" />

it('loads fast enough', () => {
cy.visit('/')
cy.lighthouse(
{
performance: 60,
accessibility: 90,
'best-practices': 80,
seo: 80,
},
{
formFactor: 'desktop',
screenEmulation: {
mobile: false,
disable: false,
width: Cypress.config('viewportWidth'),
height: Cypress.config('viewportHeight'),
deviceScaleRatio: 1,
},
},
)
})

Now it is passing!

Running audit in Desktop mode

A word of caution: Cypress by intercepting and forwarding all network requests and observing the page affects the performance measurements.

Customize the report

You can insert your own summary to the Lighthouse report before returning from the plugins file to the spec. Here is the custom code

cypress/plugins/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const { lighthouse, prepareAudit } = require('@cypress-audit/lighthouse')

module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, launchOptions) => {
prepareAudit(launchOptions)
})

on('task', {
async lighthouse(allOptions) {
let txt
// calling the function is important
const lighthouseTask = lighthouse((lighthouseReport) => {
let lighthouseScoreText = ''
let lighthouseResult = lighthouseReport?.lhr?.categories
let lighthousePerformance =
'Performance: ' + lighthouseResult?.performance?.score + '\n'
let lighthouseAccessibility =
'Accessibility: ' + lighthouseResult?.accessibility?.score + '\n'
let lighthouseBestPractices =
'Best Practices: ' +
lighthouseResult?.['best-practices']?.score +
'\n'
let lighthouseSEO = 'SEO: ' + lighthouseResult?.seo?.score + '\n'
lighthouseScoreText =
lighthousePerformance +
lighthouseAccessibility +
lighthouseBestPractices +
lighthouseSEO

console.log(lighthouseScoreText)
txt = lighthouseScoreText
})

const report = await lighthouseTask(allOptions)
// insert the text into the report returned the test
report.txt = txt
return report
},
})
}

In the spec file we want to log the txt property to the Command Log. Unfortunately, the default cy.lighthouse() command provided by the plugin ignores all properties returned by the cy.task('lighthouse') command, except for the errors. Thus we can call that task ourselves, rather than going through the plugin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
it.only('shows the text report returned by from the plugins task', () => {
cy.visit('/')

const thresholds = {
performance: 50,
accessibility: 90,
'best-practices': 80,
seo: 80,
}
const opts = {
formFactor: 'desktop',
screenEmulation: {
mobile: false,
disable: false,
width: Cypress.config('viewportWidth'),
height: Cypress.config('viewportHeight'),
deviceScaleRatio: 1,
},
}
cy.url()
.then((url) => {
cy.task('lighthouse', {
url,
thresholds,
opts,
})
})
.then((report) => {
const { errors, results, txt } = report
// our custom code in the plugins file has summarized the report
cy.log(report.txt)
})
})

We now see the performance summary in the terminal

1
2
3
4
Performance: 0.62
Accessibility: 0.92
Best Practices: 0.87
SEO: 0.8

We can see the same message text in the Cypress Command Log

Logged text from the Lighthouse report

See also