DEV Community

James
James

Posted on

2 2

Programmatically audit with Lighthouse and performance budgets

Lighthouse is a fantastic tool for auditing websites. Not only can it be run in Chrome Devtools and on the CLI, but also programmatically! This is especially useful in CI pipelines where we can use Lighthouse to check metrics such as Core Web Vitals on local or ephemeral environments, in order to warn the engineer(s) that quality attributes have degraded before going to production.

const PORT = 8041;

function createAuditor(budget) {
  const flags = {
    port: PORT,
    disableStorageReset: true,
  };
  const optionsArray = [];
  const mobileOptions = {
    extends: 'lighthouse:default',
    settings: {
      budgets: budget && [budget],
    },
  };
  optionsArray.push(mobileOptions);

  return async function audit(url) {
    const lighthouse = require('lighthouse');
    const puppeteer = require('puppeteer');
    const browser = await puppeteer.launch({
      args: [`--remote-debugging-port=${PORT}`],
      headless: true,
    });
    const results = [];

    for (const options of optionsArray) {
      const runnerResult = await lighthouse(url, flags, options);
      const budgetReport = getBudgetReport(runnerResult.report);
      const { categories, finalUrl, configSettings } = runnerResult.lhr;

      results.push({
        url: finalUrl,
        formFactor: configSettings.formFactor,
        accessibility: categories.accessibility.score * 100,
        bestPractices: categories['best-practices'].score * 100,
        performance: categories.performance.score * 100,
        pwa: categories.pwa.score * 100,
        seo: categories.seo.score * 100,
        budgetReport,
      });
    }

    await browser.close();
    return results;
  };
}

function getBudgetReport(result) {
  const report = JSON.parse(result);
  const getOverBudget = (item) => item.countOverBudget || item.sizeOverBudget || item.overBudget;
  const perfBudget = report.audits['performance-budget'];
  const timingBudget = report.audits['timing-budget'];
  const budgetReport = [];

  if (perfBudget && perfBudget.details) {
    const perf = perfBudget.details.items.filter(getOverBudget);
    budgetReport.push(...perf);
  }

  if (timingBudget && timingBudget.details) {
    const timings = timingBudget.details.items.filter(getOverBudget);
    budgetReport.push(...timings);
  }

  return budgetReport;
}
Enter fullscreen mode Exit fullscreen mode

The example implementation uses Puppeteer, as this would allow us to interact and navigate before beginning the audit. The audit method returns the results including metrics that have not met the budget requirements, allowing us to fail a build and/or report metrics.

Below is an example of the Lighthouse performance budget.

{
    "resourceCounts": [
        {
            "resourceType": "script",
            "budget": 15
        }
    ],
    "resourceSizes": [
        {
            "resourceType": "script",
            "budget": 180
        }
    ],
    "timings": [
        {
            "metric": "interactive",
            "budget": 4500
        },
        {
            "metric": "first-contentful-paint",
            "budget": 1300
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

I've written about sending these metrics to Datadog in a following post.

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay