#
Utilisation avec Lighthouse CI
#
Objectifs
Utiliser le plugin lighthouse-ecoindex avec Lighthouse CI dans vos outils de CI/CD ou vos repositories CI/Cd.
Vous pourrez ainsi :
- Publier les résultats des audits Lighthouse et EcoIndex® dans votre CI/CD ;
- Publier les résultats des audits Lighthouse et EcoIndex® dans un serveur Lighthouse.
#
Installation
Vous devez utiliser les fichiers de configuration de Lighthouse (exemple ci-dessous) et Puppeteer (présent dans le plugin) pour pouvoir utiliser le plugin lighthouse-ecoindex.
# Ajout à un projet existant
npm install lighthouse lighthouse-plugin-ecoindex-core puppeteer --save-dev
#
Utilisation
Vous devez utiliser le fichiers configuration de Lighthouse pour pouvoir utiliser le plugin lighthouse-ecoindex.
const path = require('path')
/**
* Get the path to the custom Lighthouse config file.
* @returns {string} The path to the custom Lighthouse config file.
*/
const getLighthouseConfig = () => {
return path.join(
require.resolve('lighthouse-plugin-ecoindex-core'),
'../helpers/lhci/custom-config.js',
)
}
module.exports = {
ci: {
collect: {
url: [
'https://www.ecoindex.fr/',
'https://novagaia.fr/',
// 'https://www.relocalisons.bzh/',
// 'https://www.neuro-mav-france.org/',
// 'https://www.alodokter.com/',
],
numberOfRuns: 1,
settings: {
configPath: getLighthouseConfig(),
},
puppeteerLaunchOptions: {
headless: 'new',
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-sandbox',
],
},
puppeteerScript: './.puppeteerrc.cjs',
},
assert: {
preset: 'lighthouse:default',
},
},
}
Tip
Placer le fichier .lighthouserc.js
à la racine de votre projet.
Le fichier de configuration Puppeteer est indiqué dans puppeteerScript
du fichier. Si besoin, adapter le chemin qui doit être en absolu, obligatoire avec lhci
.
Ne modifier pas le fichier Puppeteer sauf si vous devez ajouter des actions spécifiques (ex. Fermeture de popin pour validation de cookies). Conserver le process en place pour avoir des mesures normalisée.
Voir les explications
// https://pptr.dev/guides/configuration
// https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#puppeteerscript
/**
* @param {puppeteer.Browser} browser
* @param {{url: string, options: LHCI.CollectCommand.Options}} context
*/
module.exports = async (browser, context) => {
// launch browser for LHCI
var page = await browser.newPage(context.options)
// To be set by env vars
const authenticate = {
loginPage: `https://greenit.eco/wp-login.php/`,
loginField: '#user_login',
loginValue: process.env.LOGIN_VALUE || '********',
passField: '#user_pass',
passValue: process.env.PASS_VALUE || '********',
}
// Test if current page is the login URL page
if (context.url === authenticate.loginPage) {
console.log(`Authenticate on`, authenticate.loginPage)
connect(page, browser, authenticate)
} else {
const session = await page.target().createCDPSession()
try {
await page.goto(context.url, {
waitUntil: 'networkidle0',
timeout: 10000, // change timeout to 10s for crashing tests faster.
})
} catch (error) {
console.error('Error getting page:', error.message)
console.error('Retry...')
await page.goto(context.url)
}
await startEcoindexPageMesure(page, session)
await endEcoindexPageMesure()
// close session for next run
await page.close()
}
}
async function connect(page, browser, authenticate) {
page = await browser.newPage()
await page.goto(authenticate.loginPage)
await page.type(authenticate.loginField, authenticate.loginValue)
await page.type(authenticate.passField, authenticate.passValue)
await page.click('[type="submit"]')
try {
await page.waitForNavigation()
// close session for next run
await page.close()
console.log(`Authenticated!`)
} catch (error) {
throw new Error(`Connection failed!`)
}
}
async function startEcoindexPageMesure(page, session) {
page.setViewport({
width: 1920,
height: 1080,
})
await new Promise(r => setTimeout(r, 3 * 1000))
const dimensions = await page.evaluate(() => {
var body = document.body,
html = document.documentElement
var height = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
)
return {
width: document.documentElement.clientWidth,
height: height,
deviceScaleFactor: window.devicePixelRatio,
}
})
// console.log('dimensions', dimensions)
// We need the ability to scroll like a user. There's not a direct puppeteer function for this, but we can use the DevTools Protocol and issue a Input.synthesizeScrollGesture event, which has convenient parameters like repetitions and delay to somewhat simulate a more natural scrolling gesture.
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-synthesizeScrollGesture
await session.send('Input.synthesizeScrollGesture', {
x: 100,
y: 600,
yDistance: -dimensions.height,
speed: 1000,
})
}
/**
* End Ecoindex flow. Wait 3s.
*/
async function endEcoindexPageMesure() {
await new Promise(r => setTimeout(r, 3 * 1000))
}
#
Exemple
#
Exemples à adapter suivant votre CI/CD
.github/workflows/ci.yml
name: CI
on: [push]
jobs:
lighthouseci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm install && npm install -g @lhci/cli@0.12.x
- run: npm run build
- run: lhci autorun
.gitlab-ci.yml
default:
cache: # Cache modules using lock file
key:
files:
- package-lock.json
paths:
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline --loglevel=error
lighthouse:
image: registry.gitlab.com/gitlab-ci-utils/lighthouse:latest
stage: test
when: manual
script:
- npm run lhci:collect || echo "LHCI:Collect failed!"
- npm run lhci:upload || echo "LHCI:Upload failed!"
artifacts:
# Always save artifacts. This is needed if lighthouse is run configured
# to fail on certain criteria, and will ensure the report is saved.
when: always
# Save the lighthouse report, which by default is named for the site
# analyzed and the current time.
paths:
- .lighthouseci/*
package.json
{
"name": "poc-mesure-automatisee-ecoindex-lighthouse",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"lhci": "npm run lhci:collect && npm run lhci:upload",
"lhci:collect": "lhci collect",
"lhci:upload": "lhci upload --upload.basicAuth.username=******** --upload.basicAuth.********",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@lhci/cli": "^0.14.0",
"lighthouse-plugin-ecoindex-core": "^5.0.0",
"puppeteer": "^23.3.0",
"puppeteer-core": "^23.3.0"
}
}
.lighthouserc.js
const path = require('path')
/**
* Get the path to the custom Lighthouse config file.
* @returns {string} The path to the custom Lighthouse config file.
*/
const getLighthouseConfig = () => {
return path.join(
require.resolve('lighthouse-plugin-ecoindex-core'),
'../helpers/lhci/custom-config.js',
)
}
module.exports = {
ci: {
collect: {
url: [
'https://www.ecoindex.fr/',
'https://novagaia.fr/',
// 'https://www.relocalisons.bzh/',
// 'https://www.neuro-mav-france.org/',
// 'https://www.alodokter.com/',
],
numberOfRuns: 1,
settings: {
configPath: getLighthouseConfig(),
},
puppeteerLaunchOptions: {
headless: 'new',
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-sandbox',
],
},
puppeteerScript: './.puppeteerrc.cjs',
},
assert: {
preset: 'lighthouse:default',
},
},
}
.puppeteerrc.cjs
// https://pptr.dev/guides/configuration
// https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#puppeteerscript
/**
* @param {puppeteer.Browser} browser
* @param {{url: string, options: LHCI.CollectCommand.Options}} context
*/
module.exports = async (browser, context) => {
// launch browser for LHCI
var page = await browser.newPage(context.options)
// To be set by env vars
const authenticate = {
loginPage: `https://greenit.eco/wp-login.php/`,
loginField: '#user_login',
loginValue: process.env.LOGIN_VALUE || '********',
passField: '#user_pass',
passValue: process.env.PASS_VALUE || '********',
}
// Test if current page is the login URL page
if (context.url === authenticate.loginPage) {
console.log(`Authenticate on`, authenticate.loginPage)
connect(page, browser, authenticate)
} else {
const session = await page.target().createCDPSession()
try {
await page.goto(context.url, {
waitUntil: 'networkidle0',
timeout: 10000, // change timeout to 10s for crashing tests faster.
})
} catch (error) {
console.error('Error getting page:', error.message)
console.error('Retry...')
await page.goto(context.url)
}
await startEcoindexPageMesure(page, session)
await endEcoindexPageMesure()
// close session for next run
await page.close()
}
}
async function connect(page, browser, authenticate) {
page = await browser.newPage()
await page.goto(authenticate.loginPage)
await page.type(authenticate.loginField, authenticate.loginValue)
await page.type(authenticate.passField, authenticate.passValue)
await page.click('[type="submit"]')
try {
await page.waitForNavigation()
// close session for next run
await page.close()
console.log(`Authenticated!`)
} catch (error) {
throw new Error(`Connection failed!`)
}
}
async function startEcoindexPageMesure(page, session) {
page.setViewport({
width: 1920,
height: 1080,
})
await new Promise(r => setTimeout(r, 3 * 1000))
const dimensions = await page.evaluate(() => {
var body = document.body,
html = document.documentElement
var height = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
)
return {
width: document.documentElement.clientWidth,
height: height,
deviceScaleFactor: window.devicePixelRatio,
}
})
// console.log('dimensions', dimensions)
// We need the ability to scroll like a user. There's not a direct puppeteer function for this, but we can use the DevTools Protocol and issue a Input.synthesizeScrollGesture event, which has convenient parameters like repetitions and delay to somewhat simulate a more natural scrolling gesture.
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-synthesizeScrollGesture
await session.send('Input.synthesizeScrollGesture', {
x: 100,
y: 600,
yDistance: -dimensions.height,
speed: 1000,
})
}
/**
* End Ecoindex flow. Wait 3s.
*/
async function endEcoindexPageMesure() {
await new Promise(r => setTimeout(r, 3 * 1000))
}
lighthouse-metrics.js
const fs = require('fs')
const scoreScalingFactor = 100
const categories = ['accessibility', 'lighthouse-plugin-ecoindex-core']
// get all the report files
const reportFileNames = fs.readdirSync('./').filter(fn => fn.endsWith('.json'))
// read the report files
reportFileNames.forEach(report => {
// read the report file
const results = JSON.parse(fs.readFileSync(report, 'utf8'))
// get the URL
const url = results.finalUrl
console.log(`Metrics for ${url}`)
// must slugify the URL to avoid special characters in the file name
const slug = url.replace(/[^a-z0-9]/gi, '-').toLowerCase()
// generate the file name
const metricsFileName = `lighthouse-metrics-${slug}.txt`
// generate the metrics
const metrics = categories
.map(category => {
if (category === 'lighthouse-plugin-ecoindex-core') {
return `lighthouse{category="ecoindex"} ${
results?.categories['lighthouse-plugin-ecoindex-core']?.score *
scoreScalingFactor
}`
} else {
return `lighthouse{category="${category}"} ${
results?.categories[category]?.score * scoreScalingFactor
}`
}
})
.join('\n')
// write the metrics to the file
fs.writeFileSync(metricsFileName, metrics)
})
.circleci/config.yml
version: 2.1
orbs:
browser-tools: circleci/browser-tools@1.2.3
jobs:
build:
docker:
- image: cimg/node:16.13-browsers
working_directory: ~/your-project
steps:
- checkout
- browser-tools/install-chrome
- run: npm install
- run: npm run build
- run: sudo npm install -g @lhci/cli@0.12.x
- run: lhci autorun
machine-setup.sh
#!/bin/bash
set -euxo pipefail
# Add Chrome's apt-key
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/google.list
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
# Add Node's apt-key
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
# Install NodeJS and Google Chrome
sudo apt-get update
sudo apt-get install -y nodejs google-chrome-stable
job.sh
#!/bin/bash
set -euxo pipefail
npm install
npm run build
export CHROME_PATH=$(which google-chrome-stable)
export LHCI_BUILD_CONTEXT__EXTERNAL_BUILD_URL="$BUILD_URL"
npm install -g @lhci/cli@0.12.x
lhci autorun
#
Documentation externe des dépendances