Playwright teljesítménytesztek 2026: Core Web Vitals mérése CI-ben

Így mérj Core Web Vitals metrikákat Playwrighttel: web-vitals.js integráció, CDP throttling, INP mérés, GitHub Actions és regressziós költségvetések 2026-ban.

Playwright Web Vitals CI 2026

Frissítve: 2026. július 10.

A Playwright teljesítménytesztek azt jelentik, hogy a Playwright böngészőautomatizációs keretrendszerével valódi Chromium példányban futtatunk mérési szkripteket, amelyek a web-vitals.js könyvtárat beinjektálva rögzítik a Core Web Vitals (LCP, INP, CLS) értékeit, majd a CI pipeline egy előre definiált regressziós költségvetéshez hasonlítja azokat. 2026-ban a Playwright 1.50+ verziója már natívan támogatja a Chrome DevTools Protocol throttling API-t, így a Lighthouse-tól független, gyorsabb és stabilabb szintetikus mérőrendszert lehet építeni néhány óra alatt.

  • A Playwright 1.50+ és a web-vitals.js v5 kombinációja lehetővé teszi az összes Core Web Vitals metrika (LCP, INP, CLS, TTFB, FCP) mérését egyetlen tesztfájlban, körülbelül 40 sor kóddal.
  • A Chrome DevTools Protocol (CDP) session throttling stabilabb és 3-4× gyorsabb, mint a Lighthouse CI teljes auditja. Regressziós tesztekhez ez az iparági alapértelmezés 2026-ban.
  • Az INP mérése szintetikusan nehéz, mert valódi felhasználói interakció kell hozzá. Playwrightban a page.click() és page.dispatchEvent() hívásokkal lehet szimulálni, majd a metrikát a onINP callbacken keresztül kiolvasni.
  • A GitHub Actions matrix-tal érdemes párhuzamosan három futtatást indítani ugyanarra az URL-re, és a medián értéket használni, mert egyetlen szintetikus futás varianciája elérheti a 15-25%-ot.
  • A regressziós költségvetést p75-alapú küszöbként definiáld (LCP < 2500 ms, INP < 200 ms, CLS < 0.1), és a PR-ekben csak akkor buktasd a buildet, ha a mérés ezen kívülre esik. Ne pedig fix delta alapján.
  • A Playwright nem helyettesíti a Real User Monitoringot. A szintetikus adatok kiegészítik a valós felhasználói mérésekből érkező p75 percentiliseket.

Miért Playwright a teljesítményteszthez 2026-ban?

A csapatok többsége évek óta Lighthouse CI-t használ regressziós teljesítménytesztekhez, de 2026-ra egyre világosabb, hogy a Lighthouse két területen gyengén teljesít: a mérés lassú (egy audit átlagosan 45-90 másodperc), és az INP metrikát nem tudja megbízhatóan szimulálni. A Playwright ezzel szemben másodpercek alatt végigfut egy oldalon, valódi interakciókat képes elvégezni, és Chromium-alapon a web-vitals.js hivatalos könyvtárához fér hozzá, amely pontosan ugyanazt az API-t használja, mint amit a Chrome UX Report ad vissza.

Az én tapasztalatom szerint (hat évet töltöttem egy nagy zeneszolgáltató Web Player csapatában, ahol a TTI-regressziós költségvetést birtokoltam) a Lighthouse CI kifejezetten problémás monorepókban, ahol egy PR több száz oldalt érint. Amikor 20-30 URL-re kell futtatni auditot, a build könnyen 40 percre nyúlik, és a fejlesztők egyszerűen kikapcsolják. Őszintén szólva, ezt már láttam háromszor. A Playwright ezt a nagyságrendet 8-12 percre csökkenti, mert nem hordozza magával a teljes Lighthouse audit súlyát, csak azt méri, amit valóban kérünk.

A második ok, ami miatt Playwright-re váltottam: a mérési szkript ugyanaz a nyelv és tooling, mint amit a csapat funkcionális E2E tesztekhez már használ. Nincs külön config, külön retry-logika, külön riporter. A perf teszt egy másik *.spec.ts fájl a tests/ mappában, a szokásos Playwright reporterrel.

Alapkörnyezet: Playwright 1.50 és web-vitals.js v5 telepítése

A 2026-os stabil verziók: Playwright 1.50+ (a CDP session API változtatásokkal), web-vitals.js v5.0+ (az új INP algoritmussal, amely a P98 leghosszabb interakciót veszi). A Node 20 LTS az elfogadott baseline. Kezdjük egy tiszta projekttel.

npm init -y
npm install --save-dev @playwright/test@^1.50 web-vitals@^5
npx playwright install chromium --with-deps

A playwright.config.ts minimális perf-oriented beállítása így néz ki:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './perf-tests',
  timeout: 60_000,
  fullyParallel: false, // szeriálisan futtatunk, hogy a CPU tiszta legyen
  workers: 1,
  retries: 0, // NE retryzz teljesítménytesztet, inkább mediánt vegyél
  reporter: [['json', { outputFile: 'perf-results.json' }], ['list']],
  use: {
    ...devices['Desktop Chrome'],
    headless: true,
    viewport: { width: 1366, height: 768 },
  },
});

Két dolgot érdemes kiemelni. Az retries: 0 szándékos: teljesítménytesztet nem retryzünk automatikusan, mert az elrejti a valódi regressziókat. A workers: 1 pedig biztosítja, hogy két teszt nem osztozik CPU-n. Ha párhuzamosan futtatnád őket, a variancia a duplájára nőhet.

Hogyan mérjünk Core Web Vitals metrikákat Playwright-tel?

A központi ötlet: a Playwright egy Node-oldali kontextusban fut, a web-vitals.js viszont a böngészőben kell hogy éljen. Az addInitScript pontosan erre való, beinjektálja a könyvtárat még a page load előtt, és a window-ra tesz egy globális gyűjtőt.

import { test, expect, Page } from '@playwright/test';
import * as fs from 'fs';

const webVitalsScript = fs.readFileSync(
  require.resolve('web-vitals/dist/web-vitals.iife.js'),
  'utf-8'
);

async function measureCWV(page: Page, url: string) {
  await page.addInitScript(webVitalsScript);
  await page.addInitScript(() => {
    (window as any).__cwv = {};
    // @ts-ignore, web-vitals.js beinjektálva
    webVitals.onLCP((m) => ((window as any).__cwv.LCP = m.value));
    webVitals.onCLS((m) => ((window as any).__cwv.CLS = m.value));
    webVitals.onINP((m) => ((window as any).__cwv.INP = m.value));
    webVitals.onFCP((m) => ((window as any).__cwv.FCP = m.value));
    webVitals.onTTFB((m) => ((window as any).__cwv.TTFB = m.value));
  });

  await page.goto(url, { waitUntil: 'networkidle' });
  // Kényszerítsük ki a CLS és INP flush-ot: bezárjuk a láthatóságot
  await page.evaluate(() => document.dispatchEvent(
    new Event('visibilitychange')
  ));
  await page.waitForTimeout(500);

  return page.evaluate(() => (window as any).__cwv);
}

test('LCP a főoldalon 2500 ms alatt', async ({ page }) => {
  const metrics = await measureCWV(page, 'https://example.com/');
  console.log('CWV:', metrics);
  expect(metrics.LCP).toBeLessThan(2500);
  expect(metrics.CLS).toBeLessThan(0.1);
});

Egy fontos részlet, amit gyakran elfelejtenek: a visibilitychange esemény manuális kiváltása. A web-vitals.js csak akkor "flushöli" a végleges CLS és INP értéket, amikor a lap háttérbe kerül vagy bezáródik. Playwrightban ez sosem történik meg automatikusan, ezért a mérés előtt manuálisan kell triggerelni. Ha ezt kihagyod, hiányos metrikákat fogsz kapni, és órákig fogod keresni, hogy miért NaN a CLS. (Én kb. két és fél órát pazaroltam el pont ezen, még 2024-ben.)

Chrome DevTools Protocol throttling: hálózat és CPU

Egy dev laptop 32 GB RAM-mal és gigabites vezetékes hálózattal nem reprezentatív egyetlen felhasználó sem. A CI mérésnek szimulálnia kell egy mediánközeli mobil felhasználót. A Playwright a CDP session API-n keresztül férhet hozzá a Chromium alacsonyszintű throttling parancsaihoz, amelyeket a Lighthouse is használ.

test.beforeEach(async ({ page }) => {
  const client = await page.context().newCDPSession(page);

  // "Slow 4G" preset, Lighthouse Mobile baseline
  await client.send('Network.emulateNetworkConditions', {
    offline: false,
    downloadThroughput: (1.6 * 1024 * 1024) / 8, // 1.6 Mbps
    uploadThroughput: (750 * 1024) / 8,           // 750 kbps
    latency: 150,                                  // 150 ms RTT
  });

  // 4x CPU slowdown, kozepes Android telefonhoz kozeli
  await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });
});

A Lighthouse alapértelmezetten "Slow 4G" plusz 4× CPU slowdownt használ mobil auditokhoz. Ha a mérésed a CI-ben ugyanezekkel a paraméterekkel megy, az eredmények közvetlenül összehasonlíthatók egy Lighthouse futással. Ez fontos, mert így nem kell két baseline-t vezetned.

Hogyan mérjük az INP-t Playwrightban?

Az INP (Interaction to Next Paint) mérése szintetikusan azért nehéz, mert kell hozzá valódi felhasználói interakció: kattintás, gombnyomás vagy tap. A web-vitals.js v5 az interakciók P98 leghosszabbját veszi INP értéknek, tehát legalább 5-6 interakciót érdemes végrehajtani, hogy stabil értéket kapj.

test('INP a keresőn 200 ms alatt', async ({ page }) => {
  const metrics = await measureCWV(page, 'https://example.com/search');

  const searchBox = page.locator('input[name="q"]');
  await searchBox.click();

  // Több interakció szükséges a P98 kalkulációhoz
  for (const query of ['ap', 'app', 'appl', 'apple', 'apples']) {
    await searchBox.fill(query);
    await page.waitForTimeout(120);
  }

  await page.evaluate(() => document.dispatchEvent(
    new Event('visibilitychange')
  ));
  await page.waitForTimeout(300);

  const inp = await page.evaluate(() => (window as any).__cwv.INP);
  console.log('INP:', inp);
  expect(inp).toBeLessThan(200);
});

Egy gyakori hibám az volt kezdetben, hogy egyetlen kattintással próbáltam INP-t mérni, és mindig NaN vagy nagyon zajos érték jött ki. Az INP legalább egyetlen befejezett interakciót igényel (kattintás, billentyű vagy pointerdown/pointerup pár). Egyetlen page.click() hívás technikailag elég, de a P98 statisztikailag zajos lesz egyetlen mintából.

Playwright vagy Lighthouse CI: melyik jobb teljesítményteszthez?

Mindkét eszköz jó, de eltérő célra. A Lighthouse CI a "audit-jellegű" checklist-alapú vizsgálatokban erős: SEO score, accessibility, best practices, PWA. A Playwright a metrikaközpontú regressziós tesztelésben jobb: gyors, testreszabható, valódi interakciókat tud.

SzempontPlaywright 1.50+Lighthouse CI 12+
Egy oldal mérési ideje8-15 másodperc45-90 másodperc
INP mérés valódi interakcióvalIgen, page.click()Korlátozott, csak szimulált
TestreszabhatóságTeljes JS-hozzáférésConfig JSON, plugin API
Accessibility / SEO auditCsak külön plugin-nalBeépített
Tanulási görbeMeredek, ha nincs Playwright tapasztalatAlacsony, CLI-first
Variancia egy futásra10-15%15-25%
CI matrix skálázhatóságKiváló (workerek)Nehézkes 10+ URL fölött
RiportformátumokJSON, HTML, JUnitJSON, HTML, GitHub Comment

Az én ajánlásom 2026-ra: használd mindkettőt, de más gyakorisággal. Playwrightot minden PR-en futtasd (gyors, olcsó); Lighthouse CI-t hetente egyszer teljes audithoz. Ez a hibrid megközelítés adja a legjobb signal-to-noise arányt.

Beállítás CI-ben: GitHub Actions példa

Az alábbi workflow párhuzamosan futtat három mérést ugyanarra az URL-re, és a mediánt küldi tovább egy összesítő scriptnek. Ez a "3× median" pattern jelentősen csökkenti a hamis pozitív buildbukásokat.

name: Perf Regression

on: pull_request

jobs:
  measure:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        run: [1, 2, 3]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx playwright install chromium --with-deps
      - run: npx playwright test perf-tests/ --reporter=json > run-${{ matrix.run }}.json
      - uses: actions/upload-artifact@v4
        with:
          name: perf-run-${{ matrix.run }}
          path: run-${{ matrix.run }}.json

  aggregate:
    needs: measure
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
      - run: node scripts/median-and-budget.js perf-run-*

Az összesítő script logikája egyszerű: minden metrikára veszi a 3 futás mediánját, összehasonlítja a perf-budget.json-ban tárolt küszöbökkel, és 1-es exit code-dal kilép, ha bármelyik metrika átlépi a limitet. A regressziós költségvetés fájlt commiteld a repóba, hogy a változásai is code review-n menjenek át.

Regressziós költségvetések és p75 küszöbök

A Largest Contentful Paint, INP és CLS Google-féle "jó" küszöbei a p75-alapú értékek: LCP < 2500 ms, INP < 200 ms, CLS < 0.1. Ezeket használd a CI-ben abszolút küszöbként, ne pedig relatív delta alapján. A relatív delta ("15%-nál rosszabb, mint az előző build") gyakran zajos, mert a szintetikus mérés varianciája elfedi a valódi regressziót. A pontos p75 határértékeket a web.dev Core Web Vitals dokumentációja tartalmazza.

Egy jó perf-budget.json így néz ki:

{
  "budgets": [
    {
      "path": "/",
      "LCP": 2500,
      "INP": 200,
      "CLS": 0.1,
      "TTFB": 800
    },
    {
      "path": "/search",
      "LCP": 3000,
      "INP": 200,
      "CLS": 0.15
    }
  ]
}

Miért engedékenyebb a /search route küszöbe? Mert a keresőoldal dinamikus, interaktív, és a valós felhasználók is elfogadóbbak a lassulás iránt, ha egyértelműen munkát végez az oldal. A budget értékeket a CrUX-ból származó p75 adatok alapján kalibráld route-onként. Ne találomra vedd őket a levegőből.

Gyakori hibák és megoldások

Öt év perf-mérés után az alábbi öt hiba az, amit szinte minden csapatnál látok. A sorrend nem véletlen: a legelső egyben a leggyakoribb is.

1. Egyetlen futás, nem medián

Egy szintetikus futás varianciája 15-25%. Ha egyetlen mérés dönt a build sikeréről, a fejlesztők 3-4 hét múlva kikapcsolják a perf tesztet, mert "random buktatja a PR-eket". Legalább 3 futás mediánját használd.

2. A cache pollution

A második teszt gyakran gyorsabb, mert a Chromium in-memory cache-e még él. Playwrightban a context.clearCookies() és context.clearPermissions() nem törli a HTTP cache-t. Használj minden teszt előtt új browser.newContext()-et, vagy CDP-n keresztül: client.send('Network.clearBrowserCache').

3. Auth bypass hiánya

Ha az oldal login mögött van, a Playwright a login page-t méri, nem a valódi oldalt. Használj storageState-et (Playwright natívan támogatja), amit egy setup teszt tölt fel, és a perf tesztek onnan olvasnak.

4. Third-party szkriptek varianciája

A Google Analytics, hotjar, chat widgetek nem determinisztikusak, hol 200 ms-ig futnak, hol 1500 ms-ig. Blokkold őket a mérés idejére page.route()-tal, vagy futtass két külön mérési csomagot: "with 3rd party" és "without". A regressziót az utóbbin nézd.

5. Fejlesztői build a production helyett

Dev buildben React devtools, source map, unminified JS: mind lassít. A CI-nek mindig npm run build && npm run start-tal indított production build ellen kell futnia. Ha a Vite HMR-t méred, semmi valós dolgot nem mérsz.

Gyakran ismételt kérdések

Ki tudja váltani a Playwright a Real User Monitoringot?

Nem. A Playwright szintetikus mérést végez egy előre definiált környezetben, míg a RUM valós felhasználók valós eszközein és hálózatán rögzít adatokat. A kettő egymást egészíti ki: a Playwright PR-időben elkapja a regressziókat, a RUM pedig kimutatja, hogy a valós p75 hogyan alakul földrajzi és eszközkategóriák szerint.

Mi a különbség a szintetikus és a valós felhasználói mérés között?

A szintetikus mérés (Playwright, Lighthouse CI) egy kontrollált laboratóriumi környezetben szimulál egy felhasználót: throttled hálózat, tiszta cache, előre meghatározott user flow. A valós felhasználói mérés (RUM, web-vitals.js) a valódi látogatók böngészőjében fut, változó hálózaton és eszközökön. A szintetikus adatok gyorsabbak és stabilabbak; a RUM adatok reprezentatívabbak, de nagyobb varianciájúak.

Miért kapok NaN értéket a Playwright web-vitals mérésnél?

A leggyakoribb ok, hogy a metrikát a lap bezárása vagy háttérbe kerülése előtt olvasod ki. A web-vitals.js a végleges CLS és INP értéket csak a visibilitychange eseményre flushöli. Küldj el egy manuális eseményt (document.dispatchEvent(new Event('visibilitychange'))), várj 300-500 ms-ot, aztán olvasd ki az értéket.

Mennyibe kerül a Playwright perf teszteket futtatni GitHub Actions-ben?

Egy 3× matrix futtatás átlagos oldalra 3-5 GitHub Actions percet fogyaszt. Nyilvános repóknál ingyenes; privát repókban a Team csomag havi 3000 percet ad, ami körülbelül 600-1000 PR mérésére elég. Ha ennél több kell, érdemes self-hosted runner-t húzni, ami stabilabb mérést is ad, mert a hardver konzisztens.

Használhatom a Playwright-et Firefox vagy Safari teljesítménymérésre?

Csak korlátozottan. A web-vitals.js Chromium-specifikus API-kra épül (LargestContentfulPaint API, Event Timing API), amelyek Firefoxban részben, Safariban pedig alig támogatottak 2026-ban. Cross-browser perf-teszthez WebPageTest vagy SpeedCurve a megfelelő eszköz. A Playwrightot Chromium/Chrome mérésre használd.

A Szerzőről Daniel Okafor

Daniel started in performance work on the SRE side. He spent six years at Spotify on the Web Player team, where he owned the TTI regression budget for the desktop web app and built the internal dashboard that flagged perf regressions per PR before merge. He left in 2023 to join a small consultancy doing performance audits for fintech and travel companies, mostly in the UK and Nigeria. His subspecialty is server-side rendering tradeoffs: when streaming SSR actually helps, when it makes things worse on flaky 4G, and the real numbers behind React Server Components for content-heavy sites. He's a heavy Playwright user for perf testing, mistrusts most npm dependencies on principle, and is currently writing a small Rust tool to diff WebPageTest waterfalls across deploys. Outside of work he coaches a junior dev meetup in Manchester.