MCPcopy
hub / github.com/addyosmani/puppeteer-webperf

github.com/addyosmani/puppeteer-webperf @main sqlite

repository ↗ · DeepWiki ↗
12 symbols 18 edges 23 files 6 documented · 50%
README

Puppeteer WebPerf logo

Automating Web Perf measurement 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.

Table Of Contents

Get a DevTools performance trace for a page load

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();
})();

Source

Screenshot of a DevTools performance profile from loading and rendering a page

Get a DevTools trace with screenshots

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();
})();

Source

DevTools screenshots in the performance panel

Get a DevTools trace and extract filmstrip screenshots

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();
})();

Source

Get a DevTools trace for a user interaction

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();
})();

Source

Get Runtime performance metrics

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();
})();

Source

Runtime performance metrics shown in the terminal

Generate a Lighthouse report

💡🏠 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);

Source

Lighthouse report generation from Puppeteer

Extract Lighthouse performance metrics

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);

Source

Emulate a slow network

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.

Emulating a Slow 3G network is demonstrated below.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const client = await page.target().createCDPSession();
  await client.send('Network.enable');
  // Simulated network throttling (Slow 3G)
  await page.emulateNetworkConditions(puppeteer.networkConditions['Slow 3G']);
  await browser.close();
})();

Source

You can find details on the presets DevTools supports for Slow and Fast 3G in the official source. If you are looking for the older presets around Regular 4G, WiFi etc, they are captured in network throttling in Puppeteer.

Emulate a slow network and CPU

CPU throttling allows you to simulate how a page performs on slower mobile devices. This can be done using Puppeteer’s page.emulateNetworkConditions API.

🚨 Real device CPU performance is impacted by many factors that are not trivial to emulate via the Chrome DevTools Protocol / Puppeteer. e.g core count, L1/L2 cache, thermal throttling impacting performance, architecture etc. Simulating CPU performance can be a good guideline, but ideally also verify any numbers you see on a real mobile device.

Building on top of Slow 3G network throttling, slow CPU throttling (4x slowdown - close to a median-quality device like the Moto G4), is shown below.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const client = await page.target().createCDPSession();
  await client.send('Network.enable');
  // Simulated network throttling (Slow 3G)
  await page.emulateNetworkConditions(puppeteer.networkConditions['Slow 3G']);
  await page.emulateCPUThrottling(4);
  await browser.close();
})();

Source

Test your site renders with JavaScript disabled

Situations with intermittant connectivity may mean JS is effectively disabled until it can be loaded. Testing a page with JS disabled allows you to simulate a 'worst case' for this.

Puppeteer API: setRequestInterception()

```js const puppeteer = require('puppeteer');

(async () => { const browser = await puppeteer.

Core symbols most depended-on inside this repo

countObjects
called by 2
measure-memory-leaks.js
calculateUsedBytes
called by 2
code-coverage.js
lighthouseFromPuppeteer
called by 1
lighthouse-report.js
getLCP
called by 1
largest-contentful-paint.js
lighthouseFromPuppeteer
called by 1
lighthouse-metrics.js
getCLS
called by 1
cumulative-layout-shift.js
constructor
called by 0
measure-memory-leaks.js
onMessage
called by 0
measure-memory-leaks.js

Shape

Function 9
Class 2
Method 1

Languages

TypeScript100%

Modules by API surface

measure-memory-leaks.js5 symbols
largest-contentful-paint.js2 symbols
cumulative-layout-shift.js2 symbols
lighthouse-report.js1 symbols
lighthouse-metrics.js1 symbols
code-coverage.js1 symbols

Dependencies from manifests, versioned

eslint6.8.0 · 1×
eslint-config-google0.14.0 · 1×
lighthouse6.0.0 · 1×
puppeteer3.0.1 · 1×
puppeteer-har1.1.2 · 1×

For agents

$ claude mcp add puppeteer-webperf \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact