Implementation:Puppeteer Puppeteer Cdp Coverage
| Property | Value |
|---|---|
| sources | packages/puppeteer-core/src/cdp/Coverage.ts |
| domains | Code Coverage, Profiling, CDP |
| last_updated | 2026-02-12 00:00 GMT |
Overview
Description
The Coverage module provides methods to gather information about which parts of JavaScript and CSS were actually used by a page. It consists of three main classes:
- Coverage -- The facade class accessible via
page.coverage. It delegates toJSCoverageandCSSCoveragefor the actual work. - JSCoverage -- Collects JavaScript coverage using the CDP
ProfilerandDebuggerdomains. It tracks script URLs and sources viaDebugger.scriptParsedevents, starts precise coverage viaProfiler.startPreciseCoverage, and computes disjoint coverage ranges when stopped. - CSSCoverage -- Collects CSS coverage using the CDP
CSSandDOMdomains. It tracks stylesheets viaCSS.styleSheetAddedevents and computes coverage ranges fromCSS.stopRuleUsageTrackingresults.
Both JS and CSS coverage support a resetOnNavigation option that clears collected data on page navigation. The module includes a convertToDisjointRanges utility that uses a sweep-line algorithm to merge overlapping coverage ranges into non-overlapping intervals.
Coverage output can be converted to Istanbul-compatible format using the puppeteer-to-istanbul package.
Usage
Coverage is accessed via page.coverage. The typical workflow is: start coverage, navigate to a page, then stop coverage and analyze the results.
Code Reference
Source Location: packages/puppeteer-core/src/cdp/Coverage.ts (508 lines)
Signature:
export class Coverage {
constructor(client: CDPSession);
async startJSCoverage(options?: JSCoverageOptions): Promise<void>;
async stopJSCoverage(): Promise<JSCoverageEntry[]>;
async startCSSCoverage(options?: CSSCoverageOptions): Promise<void>;
async stopCSSCoverage(): Promise<CoverageEntry[]>;
}
export class JSCoverage {
constructor(client: CDPSession);
async start(options?: JSCoverageOptions): Promise<void>;
async stop(): Promise<JSCoverageEntry[]>;
}
export class CSSCoverage {
constructor(client: CDPSession);
async start(options?: CSSCoverageOptions): Promise<void>;
async stop(): Promise<CoverageEntry[]>;
}
Import:
import { Coverage, JSCoverage, CSSCoverage } from 'puppeteer-core/lib/cdp/Coverage.js';
import type { CoverageEntry, JSCoverageEntry, JSCoverageOptions, CSSCoverageOptions } from 'puppeteer-core/lib/cdp/Coverage.js';
I/O Contract
Inputs:
| Parameter | Type | Required | Description |
|---|---|---|---|
| JSCoverageOptions.resetOnNavigation | boolean |
No | Whether to reset coverage on every navigation (default: true) |
| JSCoverageOptions.reportAnonymousScripts | boolean |
No | Whether anonymous scripts (eval, new Function) are reported (default: false) |
| JSCoverageOptions.includeRawScriptCoverage | boolean |
No | Whether to include raw V8 script coverage entries (default: false) |
| JSCoverageOptions.useBlockCoverage | boolean |
No | Whether to collect block-level (true) or function-level (false) coverage (default: true) |
| CSSCoverageOptions.resetOnNavigation | boolean |
No | Whether to reset coverage on every navigation (default: true) |
Outputs:
| Output | Type | Description |
|---|---|---|
| JSCoverageEntry[] | Array<{url, text, ranges, rawScriptCoverage?}> |
JavaScript coverage entries with URL, source text, used byte ranges, and optional raw V8 data |
| CoverageEntry[] | Array<{url, text, ranges}> |
CSS coverage entries with URL, stylesheet text, and used byte ranges |
| ranges | Array<{start: number, end: number}> |
Disjoint byte ranges of code that was executed/used |
Usage Examples
// Calculate percentage of initially used code
await Promise.all([
page.coverage.startJSCoverage(),
page.coverage.startCSSCoverage(),
]);
await page.goto('https://example.com');
const [jsCoverage, cssCoverage] = await Promise.all([
page.coverage.stopJSCoverage(),
page.coverage.stopCSSCoverage(),
]);
let totalBytes = 0;
let usedBytes = 0;
const coverage = [...jsCoverage, ...cssCoverage];
for (const entry of coverage) {
totalBytes += entry.text.length;
for (const range of entry.ranges) {
usedBytes += range.end - range.start - 1;
}
}
console.log(`Bytes used: ${(usedBytes / totalBytes) * 100}%`);
// Include raw V8 script coverage data
await page.coverage.startJSCoverage({
includeRawScriptCoverage: true,
resetOnNavigation: false,
});
await page.goto('https://example.com');
const entries = await page.coverage.stopJSCoverage();
for (const entry of entries) {
console.log(entry.url, entry.rawScriptCoverage);
}
// Report anonymous scripts (eval, new Function)
await page.coverage.startJSCoverage({
reportAnonymousScripts: true,
});