Automating Web Performance testing with Puppeteer ๐ช
๐น Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. This repository has recipes for automating Web Performance measurement with Puppeteer.
Puppeteer API: tracing.start()
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Drag and drop this JSON file to the DevTools Performance panel!
await page.tracing.start({path: 'profile.json'});
await page.goto('https://pptr.dev');
await page.tracing.stop();
await browser.close();
})();
Puppeteer API: tracing.start()
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Drag and drop this JSON file to the DevTools Performance panel!
await page.tracing.start({ path: 'profile.json', screenshots: true });
await page.goto('https://pptr.dev');
await page.tracing.stop();
await browser.close();
})();
If you would like to record a performance trace and extract filmstrip screenshots from that trace to a local directory, the below snippet should do the trick. It works by filtering trace events for screenshot entries.
const puppeteer = require('puppeteer');
const fs = require('fs');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.tracing.start({ screenshots: true, path: 'trace.json' });
await page.goto('https://netflix.com', { timeout: 60000 });
await page.tracing.stop();
// Extract data from the trace
const tracing = JSON.parse(fs.readFileSync('./trace.json', 'utf8'));
const traceScreenshots = tracing.traceEvents.filter(x => (
x.cat === 'disabled-by-default-devtools.screenshot' &&
x.name === 'Screenshot' &&
typeof x.args !== 'undefined' &&
typeof x.args.snapshot !== 'undefined'
));
traceScreenshots.forEach(function(snap, index) {
fs.writeFile(`trace-screenshot-${index}.png`, snap.args.snapshot, 'base64', function(err) {
if (err) {
console.log('writeFile error', err);
}
});
});
await browser.close();
})();
Puppeteer API: page.click()
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const navigationPromise = page.waitForNavigation();
await page.goto('https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=outline');
await page.setViewport({ width: 1440, height: 714 });
await navigationPromise;
const selector = 'body > sidebar-component > sidebar-item:nth-child(3) > .pptr-sidebar-item';
await page.waitForSelector(selector);
await page.tracing.start({path: 'trace.json', screenshots: true});
await page.click(selector);
await page.tracing.stop();
await browser.close();
})();
The page.metrics()
returns runtime metrics from the Chrome DevTools Protocol Performance.getMetrics() method, such as layout duration, recalc-style durations and JS event listeners.
Puppeteer API: metrics()
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://pptr.dev');
const metrics = await page.metrics();
console.info(metrics);
await browser.close();
})();
๐ก๐ Lighthouse is an engine for analyzing web apps and web pages, collecting modern performance metrics and insights on developer best practices. It's available in the Chrome DevTools, PageSpeed Insights, a CLI and as a consumable module.
Generate a Lighthouse report for a URL and output it to a local HTML file. For more details, see the official guide to using Puppeteer with Lighthouse.
Puppeteer API: connect()
const fs = require('fs');
const lighthouse = require('lighthouse');
const puppeteer = require('puppeteer');
const chromeLauncher = require('chrome-launcher');
const reportGenerator = require('lighthouse/lighthouse-core/report/report-generator');
const request = require('request');
const util = require('util');
const options = {
logLevel: 'info',
disableDeviceEmulation: true,
chromeFlags: ['--disable-mobile-emulation']
};
async function lighthouseFromPuppeteer(url, options, config = null) {
// Launch chrome using chrome-launcher
const chrome = await chromeLauncher.launch(options);
options.port = chrome.port;
// Connect chrome-launcher to puppeteer
const resp = await util.promisify(request)(`http://localhost:${options.port}/json/version`);
const { webSocketDebuggerUrl } = JSON.parse(resp.body);
const browser = await puppeteer.connect({ browserWSEndpoint: webSocketDebuggerUrl });
// Run Lighthouse
const { lhr } = await lighthouse(url, options, config);
await browser.disconnect();
await chrome.kill();
const html = reportGenerator.generateReport(lhr, 'html');
fs.writeFile('report.html', html, function (err) {
if (err) throw err;
});
}
lighthouseFromPuppeteer("https://pptr.dev", options);
Lighthouse exposes a number of user-centric performance metrics. It's possible to pluck these metrics values out from the JSON response, as demonstrated below.
const fs = require('fs');
const lighthouse = require('lighthouse');
const puppeteer = require('puppeteer');
const chromeLauncher = require('chrome-launcher');
const reportGenerator = require('lighthouse/lighthouse-core/report/report-generator');
const request = require('request');
const util = require('util');
const options = {
logLevel: 'info',
disableDeviceEmulation: true,
chromeFlags: ['--disable-mobile-emulation']
};
async function lighthouseFromPuppeteer(url, options, config = null) {
// Launch chrome using chrome-launcher
const chrome = await chromeLauncher.launch(options);
options.port = chrome.port;
// Connect chrome-launcher to puppeteer
const resp = await util.promisify(request)(`http://localhost:${options.port}/json/version`);
const { webSocketDebuggerUrl } = JSON.parse(resp.body);
const browser = await puppeteer.connect({ browserWSEndpoint: webSocketDebuggerUrl });
// Run Lighthouse
const { lhr } = await lighthouse(url, options, config);
await browser.disconnect();
await chrome.kill();
const json = reportGenerator.generateReport(lhr, 'json');
const audits = JSON.parse(json).audits; // Lighthouse audits
const first_contentful_paint = audits['first-contentful-paint'].displayValue;
const total_blocking_time = audits['total-blocking-time'].displayValue;
const time_to_interactive = audits['interactive'].displayValue;
console.log(`\n
Lighthouse metrics:
๐จ First Contentful Paint: ${first_contentful_paint},
โ๏ธ Total Blocking Time: ${total_blocking_time},
๐ Time To Interactive: ${time_to_interactive}`);
}
lighthouseFromPuppeteer("https://bbc.com", options);
If you need to throttle the network connection, use Puppeteerโs page.emulateNetworkConditions
API.
๐จ Real network performance can be impacted by latency to towers, traffic patterns and the current radio activity. The Lighthouse guide to network throttling covers in more detail what the differences are between simulated, request-level and packet-level throttling.
You can use Lighthouse with the comcast module for packet-level throttling.