Principle:Puppeteer Puppeteer Code Coverage Collection
| Knowledge Sources | |
|---|---|
| Domains | Testing, Performance |
| Last Updated | 2026-02-12 00:00 GMT |
Overview
Code coverage collection is the principle of measuring which portions of JavaScript and CSS code are actually executed or applied during page operation, producing coverage reports that identify unused code.
Description
Code Coverage Collection leverages the browser's built-in profiling capabilities to track which ranges of JavaScript and CSS source code are executed during page interaction. This enables identification of dead code, optimization of bundle sizes, and validation that test scenarios exercise the intended code paths.
The coverage system operates in two independent domains:
JavaScript Coverage:
- Uses the V8 profiler (via Chrome DevTools Protocol's Profiler domain) to instrument JavaScript execution.
- Supports two granularity levels:
- Block-level coverage (default): Tracks execution at the statement/block level, providing precise information about which branches and code blocks were reached.
- Function-level coverage: Tracks only whether each function was called, with lower overhead but less granularity.
- Can optionally include anonymous scripts (inline scripts, eval'd code) in the report.
- Can optionally include raw V8 script coverage data for integration with external analysis tools.
- Configurable behavior on navigation: coverage can either reset on each navigation (useful for single-page analysis) or accumulate across navigations (useful for multi-page workflows).
CSS Coverage:
- Uses the CSS domain to track which CSS rules are actually applied to elements during rendering.
- Reports coverage in terms of byte ranges within stylesheet source text.
- Configurable navigation reset behavior similar to JavaScript coverage.
Each coverage entry in the report contains:
- URL: The source URL of the script or stylesheet.
- Text: The full source text of the file.
- Ranges: An array of start/end byte positions indicating which portions of the source were executed or applied.
The coverage lifecycle follows a start-stop-collect pattern: coverage collection must be explicitly started before the page actions to measure, and stopped to retrieve the results. Starting coverage instruments the browser's execution engine with profiling hooks, and stopping it collects and returns the accumulated data.
Usage
Use code coverage collection to identify unused JavaScript and CSS in production bundles, to verify that automated tests exercise critical code paths, to guide code splitting decisions by understanding which code is loaded but never executed on a given page, and to measure test suite completeness. Coverage collection is particularly valuable in performance optimization workflows where reducing bundle size directly impacts load times.
Theoretical Basis
The coverage collection system instruments the browser's code execution engine and collects range-based coverage data:
CODE COVERAGE MODEL
Script/Stylesheet Source Coverage Data
+---------------------------+ +---------------------------+
| function foo() { | | ranges: [ |
| if (condition) { <-----|---| { start: 0, end: 45 }, | (executed)
| doA(); <-----|---| { start: 45, end: 78 }, | (executed)
| } else { | | |
| doB(); (not called)| | // gap: 78-120 = unused |
| } | | |
| return result; <-----|---| { start: 120, end: 145 } | (executed)
| } | | ] |
+---------------------------+ +---------------------------+
Pseudocode for coverage collection:
1. Start JavaScript coverage:
await page.coverage.startJSCoverage({
resetOnNavigation: true,
reportAnonymousScripts: false,
includeRawScriptCoverage: false,
useBlockCoverage: true
})
// Enables Profiler and Debugger domains
// Instructs V8 to record block-level execution counts
2. Start CSS coverage:
await page.coverage.startCSSCoverage({
resetOnNavigation: true
})
// Enables CSS and DOM domains
// Begins tracking which CSS rules are applied
3. Perform page interactions:
await page.goto(url)
await page.click('#button')
// All script execution and CSS application is tracked
4. Collect JavaScript coverage:
jsCoverage = await page.coverage.stopJSCoverage()
// Returns array of CoverageEntry:
// [{ url: "https://example.com/app.js",
// text: "function foo() { ... }",
// ranges: [{ start: 0, end: 200 }, { start: 350, end: 400 }] }]
5. Collect CSS coverage:
cssCoverage = await page.coverage.stopCSSCoverage()
// Returns array of CoverageEntry for stylesheets
6. Calculate coverage percentage:
FOR EACH entry IN coverage:
totalBytes = length(entry.text)
usedBytes = SUM(range.end - range.start FOR range IN entry.ranges)
coveragePercent = usedBytes / totalBytes * 100
NAVIGATION RESET BEHAVIOR:
IF resetOnNavigation == true:
On each navigation, clear accumulated coverage data
Only report coverage from the most recent page load
ELSE:
Accumulate coverage across all navigations
Report combined coverage from entire session
The key architectural insight is that coverage data is collected at the byte range level within source files, not at the line level. This allows precise identification of which expressions, branches, and statements were executed, even within complex single-line expressions or minified code.