Chrome fik i september og oktober 2026 en ny funktion i DevTools, der ændrer måden frontend-teams tester performance på. Med rulningen af Chrome 153 og Chrome 154 til stable introducerede Chrome-teamet kalibrerede CPU performance tier overrides i Performance-panelet, sammen med fuld understøttelse af soft navigations i Insights-visningen. Det fremgår af det officielle opslag “New in DevTools — October 2026” fra Chrome for Developers, offentliggjort 22. september 2026.

Denne guide viser, hvordan du bruger funktionen i praksis: fra manuel profilering i DevTools til et automatiseret Node.js-script med Puppeteer, der kører de samme tests i CI/CD hver gang en pull request åbnes. Du får 14 konkrete trin, otte kodeblokke og et komplet projekt, du kan kopiere direkte ind i dit eget repository.

Målgruppen er frontend-udviklere, QA-ingeniører og DevOps-folk, der allerede har en formening om, hvad Core Web Vitals er, men som mangler en konkret, reproducerbar metode til at fange regressioner inden de lander i produktion. Du behøver ikke være ekspert i Chrome DevTools Protocol for at følge med, men grundlæggende kendskab til Node.js og terminalkommandoer gør det markant lettere.

Hvad er CPU Performance Tier i Chrome DevTools?

CPU performance tier er en ny måde at simulere svagere hardware i Chrome DevTools’ Performance-panel. I stedet for at sætte en vilkårlig CPU-throttling-faktor, kan du nu kalibrere en profil, der matcher et bestemt hardwareniveau, og bruge den samme profil igen og igen. Det officielle Chrome-opslag beskriver, at Performance-panelet nu tilbyder “end-to-end soft navigation measurement” og “hardware calibration overrides” som en del af samme opdatering.

Ifølge Chromes egen referencedokumentation for Performance-panelet finder du den kalibrerede CPU-kontrol under Capture settings → CPU, hvor en ny Calibrate-knap lader DevTools måle din faktiske maskines ydelse og regne baglæns til et niveau, der svarer til en billig mobiltelefon eller en ældre laptop. Det er en markant ændring fra den gamle model, hvor du selv skulle gætte på en multiplikator som 4x eller 6x uden at vide, om tallet faktisk svarede til en reel enhed.

Teknisk set er funktionen bygget på en ny metode i Chrome DevTools Protocol, Emulation.setCPUPerformanceOverride, som nu er dokumenteret i Emulation-domænet i CDP-referencen. Det betyder, at funktionen i princippet kan styres programmatisk, ikke kun manuelt fra DevTools-UI’et, selvom Chrome endnu ikke har offentliggjort det fulde parameterskema for metoden. Derfor bygger den automatiserede del af denne guide på den ældre og fuldt dokumenterede Emulation.setCPUThrottlingRate, som Puppeteer eksponerer via page.emulateCPUThrottling().

Funktionen dukker ikke op i et vakuum. Chrome-teamet har gennem flere år udvidet Performance-panelet med stadig mere avancerede analysetilstande, fra de første “4x CPU slowdown”-knapper til et fuldt trace-baseret Insights-panel. Kalibreret CPU-tier er det logiske næste skridt, fordi det adresserer den tilbagevendende klage fra udviklerteams: at throttling-tal fra en profilering ikke kan genbruges meningsfuldt af en kollega med andet udstyr. Samme logik ligger bag, hvorfor Lighthouse længe har brugt en fast, kalibreret multiplikator i stedet for en rå tilfældig faktor, men nu er princippet også bygget direkte ind i det interaktive Performance-panel, du bruger dagligt under udvikling.

ReleaseTidspunktRelevant ændring
Chrome 152August 2026Soft navigations understøttet i Live Metrics og trace-visninger
Chrome 153September 2026Første rulning mod CPU performance tier overrides
Chrome 154September-oktober 2026Fuld soft navigation-støtte i Insights-panelet, CPU-tier bredt tilgængelig

Hvorfor Almindelig CPU-Throttling Ikke Er Nøjagtig Nok

Hvis du har brugt DevTools’ CPU-throttling før, kender du dropdown-menuen med valg som “4x slowdown” eller “6x slowdown”. Problemet er, at en 4x-faktor på en Apple M3 Pro giver et helt andet resultat end 4x på en to år gammel Dell-laptop. Tallet er relativt til din egen maskine, ikke til en fast reference. Det gør det svært at sammenligne tal mellem kolleger, mellem CI-servere og mellem lokale udviklermaskiner.

Kalibrerede tiers løser netop det problem ved at tage udgangspunkt i, hvor hurtig din maskine faktisk er, og derefter skalere ned til et defineret niveau. To udviklere med forskellige bærbare computere kan nu køre “lav-tier mobil”-testen og få sammenlignelige tal, fordi DevTools regner relativt til en fælles baseline i stedet for en vilkårlig multiplikator. Chromes performance-reference nævner direkte, at det nye flow matcher low- og mid-tier mobile devices som en kalibreret tilstand, hvilket er en markant forbedring for teams, der arbejder distribueret med forskelligt udstyr.

Konsekvensen i praksis er færre falske positiver. Et team, der tester på en kraftig udviklermaskine, overser ofte, at en almindelig bruger med en budget-Android-telefon oplever markant langsommere interaktioner. Kalibrerede tiers giver et mere realistisk billede, uden at du behøver at eje en fysisk billig telefon til hvert testforløb.

Tænk på et konkret eksempel: en e-handelsside tilføjer en ny produktkarrusel med tunge JavaScript-animationer. På udviklerens M-series Mac føles siden lynhurtig, og koden bliver godkendt i code review uden indvendinger. Først når samme side køres under en kalibreret low-tier-profil, bliver det synligt, at karrusellen blokerer hovedtråden i over 300 millisekunder ved hver swipe, hvilket direkte skader INP-scoren for en stor andel af mobilbrugerne. Uden en reproducerbar test ville den slags regressioner typisk først blive fanget, når rigtige brugere klager, eller når Core Web Vitals-rapporten i Google Search Console falder måneder senere.

CPU Performance Tier Sammenlignet Med Lighthouse Og WebPageTest

Mange teams bruger allerede Lighthouse eller WebPageTest til performance-rapporter, og det er naturligt at spørge, hvorfor man skal bygge endnu et testlag. Forskellen ligger i, hvad de to typer værktøjer er gode til. Lighthouse og WebPageTest kører typisk som et separat, isoleret job, der genererer en rapport med en samlet score efter testen er færdig. De er fremragende til periodiske sundhedstjek og til at spore en overordnet trend over tid.

Den Puppeteer-baserede tilgang i denne guide er derimod tættere på din egen udviklingsproces. Du styrer præcis, hvilke interaktioner der testes, hvilke selektorer der klikkes på, og hvilke metrics der sammenlignes mod en baseline, direkte i samme kodebase som resten af jeres test-suite. Det gør det lettere at fejlfinde en konkret regression ned til en bestemt commit, fordi testen kører side om side med jeres eksisterende enheds- og integrationstests, i stedet for i en ekstern tjeneste med sin egen kø og rapporteringsformat. De to tilgange udelukker ikke hinanden. Mange teams kører Lighthouse som en ugentlig sundhedsrapport og den kalibrerede CPU-tier-test som en hård gate på hver pull request.

Der er også en praktisk omkostningsside. En ekstern tjeneste som WebPageTest tilbyder dybere analyse og rigtige fysiske enheder i nogle opsætninger, men typisk med en kø-tid og en separat pris- eller kvotemodel. Et selvbygget Puppeteer-script koster derimod kun den computekraft, jeres egen CI allerede betaler for, og kan køre så ofte, I vil, uden ekstra licensomkostninger. Prisen er, at I selv skal vedligeholde scriptet, holde Puppeteer-versionen opdateret, og selv definere, hvad en “god” baseline er, i stedet for at læne jer på en færdig, forudbestemt scoringsmodel.

Forudsætninger: Software Og Versioner

Før du går i gang, skal du sikre dig, at din opsætning matcher følgende versioner. Listen er testet med de versioner, der var aktuelle i starten af oktober 2026.

  • Google Chrome 154 eller nyere (Chrome 153 og 154 rullede til stable i løbet af september 2026 ifølge Chrome for Developers)
  • Node.js 24 LTS (v24.21.0) eller Node.js 26-linjen, som begge er aktive udgivelser på nodejs.org i oktober 2026
  • npm 10 eller nyere (følger med Node.js-installationen)
  • Puppeteer 25.13.0, den nyeste version ifølge Puppeteer-dokumentationen og projektets GitHub-releases, udgivet 8. oktober 2026
  • Et terminalprogram og et kodeeditor, for eksempel VS Code, hvis du vil følge med i scriptet live
  • Adgang til et GitHub-repository, hvis du vil følge trinnet om GitHub Actions

Du behøver ikke en fysisk billig telefon eller en langsom laptop. Hele pointen med kalibrerede CPU-tiers er, at du kan simulere svagere hardware fra en almindelig udviklermaskine.

Node.js-versionen er vigtig, fordi Puppeteer 25.x kræver et moderne JavaScript-runtime for at kunne køre sine egne interne byggescripts og downloade den rigtige Chromium-binær korrekt. Hvis du sidder på en ældre Node 18- eller Node 20-installation, risikerer du enten installationsfejl eller uventet langsomme downloads, fordi ældre Node-versioner mangler optimeringer i de indbyggede fetch- og stream-API’er, som nyere Puppeteer-releases bygger videre på. Brug nvm install 24 eller nvm install 26, hvis du bruger Node Version Manager til at skifte mellem projekter.

Trin 1-3: Opdater Chrome, Tjek Versioner Og Optag Din Første Trace

Start med at verificere, at alt er på plads, inden du rører ved selve performance-funktionerne. Åbn en terminal og kør følgende tre kommandoer for at tjekke Chrome, Node.js og npm.

google-chrome --version
node -v
npm -v

Du bør se noget i stil med “Google Chrome 154.0.x.x”, “v24.21.0” og “10.x.x”. Hvis Chrome viser en version under 153, skal du opdatere via chrome://settings/help, da CPU-tier-funktionerne ikke findes i ældre builds.

Åbn derefter en hjemmeside, du selv styrer eller har lov til at teste, og tryk F12 for at åbne DevTools. Gå til fanen Performance, og tryk på det runde optage-ikon i øverste venstre hjørne. Klik rundt på siden i cirka 5 sekunder, og stop optagelsen igen. Du har nu en baseline-trace uden throttling, som du skal bruge til at sammenligne med senere.

Gem denne første trace ved at højreklikke i trace-vinduet og vælge “Save profile”. Det giver dig en .json-fil, du kan importere igen senere, eller dele med en kollega, der skal reproducere samme resultat uden selv at skulle navigere til siden og klikke de rigtige steder. Det er en god vane at gemme mindst en baseline-trace per større release, så I altid har et konkret referencepunkt, hvis en bruger rapporterer, at siden “pludselig føles langsommere”.

Trin 4-5: Aktivér CPU-Throttling Og Kalibrering I Performance-Panelet

Åbn Capture settings i Performance-panelet. Det lille tandhjuls- eller skyder-ikon findes typisk lige under optage-knappen. Her finder du en CPU-sektion med en dropdown for throttling-niveau. Ifølge Chromes performance-reference indeholder dette felt nu også en Calibrate-funktion, som måler din maskines faktiske ydelse og udleder et kalibreret niveau derfra.

Vælg Calibrate, og lad processen køre et par sekunder, mens DevTools kører en intern benchmark. Når kalibreringen er færdig, får du et niveau, der er tættere på et reelt stykke hardware end den gamle “4x”-knap. Gentag din klik-sekvens fra trin 3 med det kalibrerede niveau aktivt, og sammenlign de to traces side om side.

Det er her, du typisk opdager de første overraskelser. Interaktioner, der føltes instant på din udviklermaskine, kan tage flere hundrede millisekunder ekstra under et kalibreret lav-tier-niveau. Det er netop det, funktionen er designet til at afsløre tidligt, før det rammer rigtige brugere.

NiveauBeskrivelseTypisk brug
Ingen throttlingFuld hastighed på din egen maskineBaseline-måling, hurtig udviklingsiteration
Kalibreret mid-tierMatcher almindelige mellemklasse-mobilerRealistisk test af de fleste trafikscenarier
Kalibreret low-tierMatcher budget-Android og ældre enhederWorst-case test af kritiske brugerflows
Manuel 4x/6x (ældre metode)Relativ multiplikator uden kalibreringKun til hurtige, ikke-sammenlignelige tjek

Trin 6-7: Automatisér Med Chrome DevTools Protocol

Manuel profilering er fin til ad hoc-undersøgelser, men den skalerer ikke til et team, der shipper flere gange om dagen. Næste skridt er at styre samme type CPU-simulering direkte fra kode via Chrome DevTools Protocol. CDP er det samme lag, som selve DevTools-UI’et bygger på, så alt du kan klikke dig til i browseren, kan i princippet også styres programmatisk. Puppeteer giver dig en CDP-session gennem createCDPSession(), som du kan sende rå kommandoer igennem, uden at skulle gå via Puppeteers højniveau-API for hver enkelt funktion.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
const client = await page.target().createCDPSession();

// Fuldt dokumenteret CDP-metode til CPU-throttling via en ren multiplikator
await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });

await page.goto('https://example.com', { waitUntil: 'networkidle0' });

Bemærk, at vi her bruger den ældre, fuldt dokumenterede metode setCPUThrottlingRate frem for den helt nye setCPUPerformanceOverride. Den nye metode findes i CDP’s Emulation-domæne, men Chrome har ved offentliggørelsen i oktober 2026 ikke frigivet et fuldt parameterskema til offentligheden. Når det sker, kan du bytte en enkelt linje ud i scriptet nedenfor. Indtil da giver setCPUThrottlingRate samme type reproducerbare simulering, bare uden den automatiske hardware-kalibrering fra UI’et.

Husk at lukke CDP-sessionen og browserinstansen ordentligt efter hver testkørsel. En CDP-session, der ikke bliver frigivet, kan efterlade en hængende Chrome-proces i baggrunden, hvilket på en CI-runner med begrænset hukommelse hurtigt fører til, at efterfølgende jobs bliver langsommere eller fejler med uklare timeout-fejl. Kald altid await browser.close() i en finally-blok, så oprydningen sker, selv hvis selve testen kaster en fejl undervejs.

Trin 8-9: Byg Et Node.js-Testscript Med Puppeteer

Nu bygger vi det script, der bliver kernen i det komplette projekt senere i artiklen. Opret en mappe, initialisér et Node-projekt, og installér Puppeteer.

mkdir chrome-perf-test && cd chrome-perf-test
npm init -y
npm install [email protected]
npm pkg set type="module"

Opret filen run-performance-test.js, og brug Puppeteers indbyggede, dokumenterede metode page.emulateCPUThrottling() i stedet for den rå CDP-kommando. Det er samme underliggende mekanisme, men API’et er stabilt og testet af Puppeteer-teamet selv, hvilket er en vigtig forskel, når scriptet skal køre ubevogtet i CI.

import puppeteer from 'puppeteer';

const TIERS = [
  { name: 'desktop', rate: 1 },
  { name: 'mid-tier-mobil', rate: 4 },
  { name: 'low-tier-mobil', rate: 6 },
];

const TARGET_URL = process.env.TEST_URL || 'https://example.com';

async function measureTier(browser, tier) {
  const page = await browser.newPage();
  await page.emulateCPUThrottling(tier.rate);
  await page.setCacheEnabled(false);

  const start = Date.now();
  await page.goto(TARGET_URL, { waitUntil: 'networkidle0' });

  const metrics = await page.evaluate(() => {
    const [nav] = performance.getEntriesByType('navigation');
    const lcpEntries = performance.getEntriesByType('largest-contentful-paint');
    const lcp = lcpEntries.length ? lcpEntries[lcpEntries.length - 1].startTime : null;
    return {
      domContentLoaded: nav ? nav.domContentLoadedEventEnd : null,
      loadComplete: nav ? nav.loadEventEnd : null,
      lcp,
    };
  });

  await page.close();
  return { tier: tier.name, rate: tier.rate, wallClockMs: Date.now() - start, ...metrics };
}

const browser = await puppeteer.launch({ headless: 'new' });
const results = [];
for (const tier of TIERS) {
  results.push(await measureTier(browser, tier));
}
await browser.close();

console.log(JSON.stringify(results, null, 2));

Scriptet åbner tre separate faner, en per CPU-tier, kører den samme navigation i hver, og læser Largest Contentful Paint samt navigation-timing direkte fra browserens Performance API. Du kan køre det med node run-performance-test.js, eller sætte en anden adresse via miljøvariablen TEST_URL. Når scriptet kører korrekt, ser outputtet i terminalen ud på denne måde:

[
  { "tier": "desktop", "rate": 1, "wallClockMs": 612, "domContentLoaded": 340, "loadComplete": 480, "lcp": 410 },
  { "tier": "mid-tier-mobil", "rate": 4, "wallClockMs": 1180, "domContentLoaded": 890, "loadComplete": 1040, "lcp": 960 },
  { "tier": "low-tier-mobil", "rate": 6, "wallClockMs": 2140, "domContentLoaded": 1380, "loadComplete": 1920, "lcp": 1760 }
]

Mønsteret er typisk: desktop-tieret ligger komfortabelt inden for den gode LCP-grænse på 2,5 sekunder, mens low-tier-mobil-tieret nærmer sig grænsen for “skal forbedres”. Det er netop den slags gradvise forringelse, som er svær at opdage, hvis du kun tester på din egen hurtige maskine, men som bliver tydelig, så snart du kører de samme tre niveauer side om side.

Trin 10-11: Mål LCP, INP Og Long Tasks På Tværs Af Niveauer

LCP er let at hente direkte fra Performance API’et, men Interaction to Next Paint (INP) kræver, at en bruger faktisk interagerer med siden. For at teste det automatisk skal du simulere et klik og måle responstiden omkring det. Udvid scriptet med en interaktionsmåling.

async function measureInteraction(page, selector) {
  await page.waitForSelector(selector);
  const before = Date.now();
  await page.click(selector);
  await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(resolve)));
  return Date.now() - before;
}

Ifølge Googles officielle dokumentation om INP er tærsklerne faste: en INP på 200 millisekunder eller derunder er “god”, mellem 200 og 500 millisekunder “skal forbedres”, og over 500 millisekunder er “dårlig” responsivitet. Disse tal er målt som 75. percentil af reelle brugerinteraktioner i felten, men de samme grænser er nyttige, når du sammenligner simulerede tiers i et testmiljø.

MetricGodSkal forbedresDårlig
Largest Contentful Paint (LCP)≤ 2,5 sek.2,5–4 sek.> 4 sek.
Interaction to Next Paint (INP)≤ 200 ms200–500 ms> 500 ms
Cumulative Layout Shift (CLS)≤ 0,10,1–0,25> 0,25

Det er værd at huske, at tallene fra denne type test er labdata, ikke feltdata. Google Search Console og CrUX-rapporten viser feltdata fra rigtige brugeres browsere, mens scriptet i denne guide producerer kontrollerede labresultater fra en simuleret CPU-profil. De to datakilder supplerer hinanden: feltdata viser, hvad der reelt sker for jeres brugere lige nu, mens labdata fra en kalibreret tier giver jer et reproducerbart signal, I kan handle på, længe før en ændring rammer produktion og påvirker den rigtige CrUX-score.

For long tasks, altså JavaScript-opgaver der blokerer hovedtråden i mere end 50 millisekunder, kan du tilføje en PerformanceObserver i siden, inden du navigerer, så den fanger alle “longtask”-entries under hele testforløbet. Brug page.evaluateOnNewDocument() til at injicere observeren, før siden selv begynder at køre sin egen JavaScript, så du ikke går glip af tidlige blokerende opgaver under den første rendering.

await page.evaluateOnNewDocument(() => {
  window.__longTasks = [];
  new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      window.__longTasks.push({ duration: entry.duration, startTime: entry.startTime });
    }
  }).observe({ type: 'longtask', buffered: true });
});

Efter navigationen kan du læse window.__longTasks ud med page.evaluate() og summere den samlede blokeringstid. Det giver dig et konkret antal og en samlet blokeringstid per tier, som er særligt nyttigt, når du skal forklare en langsom interaktion til resten af teamet, i stedet for blot at sige, at “siden føles træg” på en bestemt enhedsklasse.

Trin 12-13: Gem Baseline Og Sammenlign Resultater Over Tid

Et enkelt sæt tal er ikke nyttigt, hvis du ikke kan se, om det bliver bedre eller værre over tid. Gem resultatet fra hver testkørsel som en JSON-fil, og sammenlign den nyeste kørsel med en gemt baseline.

{
  "tier": "low-tier-mobil",
  "rate": 6,
  "wallClockMs": 2140,
  "domContentLoaded": 1380,
  "loadComplete": 1920,
  "lcp": 1760
}

Skriv en lille sammenligningsfunktion, der fejler med en ikke-nul exit-kode, hvis LCP på et givet tier stiger mere end en fastsat procentgrænse i forhold til baseline.

import { readFileSync } from 'fs';

const baseline = JSON.parse(readFileSync('baseline.json', 'utf8'));
const latest = JSON.parse(readFileSync('latest.json', 'utf8'));
const THRESHOLD_PERCENT = 15;
let failed = false;

for (const current of latest) {
  const ref = baseline.find((b) => b.tier === current.tier);
  if (!ref || !ref.lcp || !current.lcp) continue;
  const delta = ((current.lcp - ref.lcp) / ref.lcp) * 100;
  if (delta > THRESHOLD_PERCENT) {
    console.error(`Regression på ${current.tier}: LCP steg ${delta.toFixed(1)}%`);
    failed = true;
  }
}

process.exit(failed ? 1 : 0);

En typisk startgrænse er 10 til 15 procent, så du ikke blokerer builds for små, naturlige udsving, men stadig fanger reelle regressioner. Gem baseline-filen i repositoryet, og opdater den bevidst med et separat commit, når en ændring af LCP er en tilsigtet konsekvens af en ny funktion, ikke en fejl, så historikken i Git tydeligt viser, hvornår og hvorfor referencepunktet blev flyttet.

Trin 14: Integrér Testen I GitHub Actions

Det sidste trin er at lade testen køre automatisk, hver gang en pull request åbnes eller opdateres. Hvis I allerede har en pipeline til CI/CD med GitHub Actions, kan I tilføje performance-testen som et ekstra job parallelt med jeres eksisterende test-suite.

name: performance-test
on: [pull_request]
jobs:
  cpu-tier-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '24'
      - run: npm ci
      - run: node run-performance-test.js
        env:
          TEST_URL: https://staging.dit-domaene.dk
      - run: node baseline-compare.js

Ubuntu-runnere på GitHub Actions har Chrome forinstalleret, men Puppeteer bundler normalt sin egen kompatible Chromium-build, så I undgår versionsmismatch mellem lokal udvikling og CI. Kør jobbet mod et staging-miljø, ikke produktion, så testen ikke selv påvirker rigtige brugere eller analytics.

Sæt jobbet op som en separat status-check i jeres branch protection rules, så en pull request ikke kan merges, hvis performance-testen fejler. De fleste teams starter med jobbet som rent informativt, altså uden at blokere merge, i et par uger, mens de finjusterer tolerancen i baseline-compare.js. Når teamet har tillid til, at tallene er stabile og ikke giver falske alarmer, kan I gøre checket obligatorisk. Det er en langt mindre frustrerende overgang end at gøre det obligatorisk fra dag ét og derefter skulle slukke brande, hver gang en runner er en smule langsommere end normalt.

Hvis I bruger selvhostede runnere i stedet for GitHub-hostede, får I typisk mere stabile og forudsigelige tal, fordi I kontrollerer hardwaren fuldt ud. Det er særligt relevant for teams, der har strenge performance-krav og vil undgå den variation, som delte, offentlige runnere introducerer.

Komplet Projekt: Hele Kildekoden Til Pipelinen

Sæt alle delene sammen, og du har et fuldt fungerende, selvstændigt performance-test-projekt. Mappestrukturen ser sådan ud:

  • chrome-perf-test/package.json – afhængigheder og npm-scripts
  • chrome-perf-test/run-performance-test.js – hovedscriptet fra trin 8-11
  • chrome-perf-test/baseline.json – gemte referenceresultater
  • chrome-perf-test/baseline-compare.js – sammenligningslogik fra trin 12-13
  • .github/workflows/performance-test.yml – GitHub Actions-workflowet fra trin 14
{
  "name": "chrome-perf-test",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "test:perf": "node run-performance-test.js",
    "test:perf:compare": "node run-performance-test.js > latest.json && node baseline-compare.js"
  },
  "dependencies": {
    "puppeteer": "25.13.0"
  }
}

Du kan klone denne struktur direkte ind i et eksisterende repository, eller bruge den som et selvstændigt værktøj, der peger på et hvilket som helst staging-domæne via TEST_URL. Hvis I allerede bruger browserautomatisering med Playwright andre steder i jeres test-suite, kan samme CPU-throttling-princip genbruges der, da Playwright eksponerer en lignende CDP-session under motorhjelmen.

For at køre hele pipelinen lokalt, første gang, følg denne rækkefølge: kør npm install i chrome-perf-test-mappen, sæt TEST_URL til jeres staging-adresse, kør npm run test:perf for at generere den første latest.json, og kopiér den derefter til baseline.json, så I har et startpunkt. Fremover kører I blot npm run test:perf:compare, som automatisk sammenligner den nye kørsel mod den gemte baseline og fejler, hvis en regression overstiger tolerancen. Når scriptet kører uden fejl, får du en exit-kode på 0 og ingen output fra baseline-compare.js, hvilket er det tegn, de fleste CI-systemer leder efter for at markere jobbet som bestået.

Sådan Udvider Du Projektet Videre

Når grundprojektet kører stabilt, er det naturlige næste skridt at tilføje flere målepunkter uden at ændre selve arkitekturen. Du kan tilføje en fjerde tier, der matcher en specifik enhed, jeres supportdata viser er udbredt blandt kunderne, eller tilføje en separat kørsel, der tester med en langsom 3G-netværksprofil kombineret med low-tier-CPU for at simulere det absolutte worst-case-scenarie. Fordi hele pipelinen allerede er bygget op omkring et simpelt array af konfigurationer og en generisk sammenligningsfunktion, kræver den slags udvidelser typisk kun få linjers ændring i TIERS-arrayet, ikke en omskrivning af resten af scriptet.

En anden naturlig udvidelse er at eksportere resultaterne til et format, der kan visualiseres, for eksempel en CSV-fil, der importeres i et regneark, eller en simpel tidsserie i en metrics-database som Prometheus. Det giver teamet et historisk overblik, der gør det muligt at se, om performance generelt bevæger sig i den rigtige retning over flere måneder, ikke kun om den seneste pull request introducerede en regression.

Almindelige Faldgruber

De fleste problemer med denne type performance-test stammer ikke fra selve koden, men fra små metodiske fejl, der gør resultaterne usammenlignelige. Her er de faldgruber, der går igen hos teams, der sætter deres første CPU-tier-pipeline op.

  • At sammenligne tal mellem forskellige maskiner uden kalibrering. En 4x-throttling på to forskellige bærbare computere giver ikke samme reelle hastighed. Brug altid den kalibrerede tilstand, hvis du skal dele tal med kolleger.
  • At teste på en varm cache. Gentagne besøg på samme side i DevTools kan skjule reelle netværksomkostninger, fordi browseren genbruger allerede hentede ressourcer i stedet for at hente dem igen, som en ny besøgende ville opleve det. Ryd cache, eller brug inkognitotilstand mellem målinger, og sørg for, at Puppeteer-scriptet starter en frisk browserkontekst for hver tier.
  • At køre performance-testen på samme runner som resten af CI-jobbet. Delt CPU-belastning fra andre parallelle jobs forurener målingerne, fordi Puppeteer-browseren kæmper om de samme CPU-kerner som jeres øvrige test-suite eller build-step. Kør performance-jobbet isoleret i sit eget workflow-job, eller accepter en bredere tolerance i CI end lokalt, hvis I ikke kan separere dem.
  • At blande throttling-niveauer i samme sammenligning. Hvis baseline er målt med rate 4, og den nye kørsel bruger rate 6, er forskellen i tal meningsløs, fordi I reelt sammenligner to forskellige simulerede hardwareprofiler og ikke en reel ændring i jeres kode. Lås raten fast per tier i konfigurationen, og versionér konfigurationsfilen sammen med resten af koden.
  • At ignorere netværksforhold. CPU-throttling uden samtidig netværksthrottling giver et skævt billede, fordi langsomme mobilnetværk sjældent kommer isoleret fra svagere CPU’er i den virkelige verden. Kombinér med Network-panelets throttling-profiler, hvor det er relevant, og overvej at tilføje en “slow 4G”-profil til jeres automatiserede script, så I tester begge faktorer samtidig i stedet for hver for sig.
  • At måle LCP før siden er færdig med at rendere. Hvis scriptet læser Performance API’et for tidligt, kan LCP-værdien stadig være nul eller forkert. Vent på networkidle0, og tilføj en kort ekstra ventetid for sider med lazy-loaded indhold.

Fejlfinding: 8 Almindelige Problemer

Når scriptet først er sat op, dukker en håndfuld tilbagevendende fejl op, især i CI-miljøer, der er anderledes konfigureret end en udviklers lokale maskine. Her er de otte mest almindelige, og hvordan du løser dem.

  1. Puppeteer kan ikke finde Chrome: Kør npx puppeteer browsers install chrome for at lade Puppeteer hente sin egen kompatible build.
  2. Scriptet timer ud på networkidle0: Nogle sider har vedvarende polling eller websockets, der aldrig bliver stille. Brug networkidle2 eller en fast timeout i stedet.
  3. page.emulateCPUThrottling findes ikke: Du kører en forældet Puppeteer-version. Opdatér til mindst version 25 med npm install puppeteer@latest.
  4. LCP-værdien er null i output: Siden havde intet synligt “largest” element, eller målingen kørte før første render. Tilføj en kort page.waitForTimeout-pause efter navigation.
  5. CI-runneren fejler med “no usable sandbox”: Tilføj launch-flaget args: ['--no-sandbox', '--disable-setuid-sandbox'] i containeriserede CI-miljøer.
  6. DevTools viser ikke en Calibrate-knap: Funktionen ruller gradvist ud til brugere, ligesom mange andre Chrome-features. Tjek, at Chrome er opdateret til mindst version 153, genstart browseren helt, og tjek om funktionen er bag et experiment-flag under chrome://flags, hvis den stadig ikke dukker op efter en opdatering.
  7. Resultaterne varierer meget mellem kørsler på samme maskine: Baggrundsprocesser, antivirus-scanning eller browser-opdateringer kan stjæle CPU-tid midt i en testkørsel. Kør hver tier tre gange, og brug medianen frem for første kørsel, som ofte er påvirket af kold cache og langsom browserstart.
  8. GitHub Actions-jobbet er markant langsommere end lokalt: Delte runnere har svagere og mere variabel CPU-kapacitet end udviklermaskiner, fordi de kører i en virtualiseret, delt infrastruktur. Brug en højere throttling-tolerance i CI end i lokale tests, og sammenlign kun CI-resultater mod andre CI-resultater, aldrig direkte mod tal fra en udviklers lokale maskine.

Avancerede Tips Til Performance-Test I Stor Skala

Når det grundlæggende script kører stabilt, er næste skridt at udvide dækningen uden at gøre pipelinen langsommere end nødvendigt. Kør kun de dyre, flertier-tests på pull requests mod hovedgrenen, og nøjes med den hurtige “desktop”-tier på feature-branches. Det holder feedback-loopet kort for udviklere, mens de vigtige merges stadig får fuld dækning.

Overvej også at gemme historiske resultater, ikke bare den seneste baseline. En simpel CSV- eller JSON-log per commit gør det muligt at se trends over uger og måneder, ikke kun om den seneste ændring var bedre eller værre end i går. Mange teams kobler denne log til et simpelt dashboard i deres eksisterende AI-assistent-opsætning, så en kodeagent kan læse trenden og selv foreslå, hvilken commit der introducerede en regression.

Hvis dit team allerede arbejder i VS Code, kan du binde scriptet til en opgave (task), så en udvikler kan trykke en genvejstast og få et hurtigt performance-tjek før commit, uden at skulle vente på hele CI-pipelinen. Det er især nyttigt på store komponentændringer, hvor en regression ofte kan fanges lokalt på få sekunder i stedet for flere minutter i skyen.

Til sidst: test altid mod et realistisk staging-miljø med samme CDN- og caching-opsætning som produktion. En performance-test mod en lokal dev-server uden komprimering eller CDN giver tal, der ikke stemmer med det, rigtige brugere oplever, uanset hvor præcist CPU-tieret er kalibreret. Hvis jeres kodeagenter allerede kører i VS Code’s agent host, kan I også lade en agent trigge performance-testen automatisk, når den selv har foreslået en ændring i en tung komponent.

Når I har flere sider eller ruter, der hver har deres egne performance-krav, kan I udvide TIERS-arrayet til også at loope over en liste af URL’er, så en enkelt kørsel dækker forsiden, produktsiden og checkout-flowet i samme rapport. Send gerne resultatet videre til en Slack-kanal eller et simpelt internt dashboard, så en regression ikke kun stopper en pull request, men også er synlig for resten af teamet uden at nogen skal lede efter den i CI-logs.

Ofte Stillede Spørgsmål

Er CPU performance tier tilgængelig i alle Chrome-versioner?

Nej. Funktionen kom med Chrome 153 og 154, som rullede til stable i september 2026 ifølge Chrome for Developers’ eget opslag. Ældre versioner har kun den klassiske multiplikator-baserede throttling.

Kan jeg bruge funktionen i Firefox eller Safari?

Nej, CPU performance tier er en Chrome DevTools-specifik funktion bygget på Chrome DevTools Protocol. Firefox og Safari har egne, separate throttling-værktøjer i deres udviklerværktøjer, men ikke den samme kalibrerede tier-model.

Skal jeg bruge den nye setCPUPerformanceOverride eller den gamle setCPUThrottlingRate?

Til manuel profilering i DevTools-UI’et kan du bruge den nye kalibrerede tilstand. Til automatiseret scripting anbefaler denne guide den ældre, fuldt dokumenterede setCPUThrottlingRate, fordi Chrome endnu ikke har offentliggjort det fulde parameterskema for den nye metode.

Hvor lang tid tager det at sætte hele pipelinen op?

Med denne guide kan en udvikler med grundlæggende Node.js-erfaring sætte scriptet, baseline-sammenligningen og GitHub Actions-workflowet op på omkring 90 minutter, inklusive test af de tre CPU-tiers. Den største tidssluger er typisk ikke selve koden, men at finde de rigtige selektorer og sider, der reelt er værd at teste løbende, samt at nå frem til en tolerance-procent, teamet er trygt med.

Virker dette også med Playwright i stedet for Puppeteer?

Ja. Playwright bygger også på Chrome DevTools Protocol og eksponerer en lignende CDP-session, så principperne i denne guide kan overføres med mindre API-justeringer.

Hvor stor en regression skal udløse en fejl i CI?

Der findes ikke et universelt tal, men en tolerance på 10 til 15 procent i forhold til baseline er et almindeligt udgangspunkt. Juster grænsen efter, hvor meget naturlig variation jeres egne CI-runnere har over tid. Teams med meget stabile, selvhostede runnere kan ofte strammes ned mod 5 til 8 procent, mens teams på delte, offentlige runnere typisk har brug for lidt mere luft, før en alarm reelt betyder en kodeændring og ikke bare støj fra infrastrukturen.

Kan jeg måle Cumulative Layout Shift med samme script?

Ja, med en PerformanceObserver for layout-shift-entries kan du akkumulere en CLS-score sideløbende med LCP- og INP-målingerne i samme testkørsel.