Puppeteer CoverageEntry: What It Contains
A Puppeteer CoverageEntry contains the resource text, its URL, and ranges of used code. Learn how to collect and interpret coverage safely.
A Puppeteer CoverageEntry describes one JavaScript or CSS resource in a coverage report. It contains ranges, text, and url: positions of covered code, the resource’s full text, and its URL. A JavaScript entry is a JSCoverageEntry; it has those same fields and may also include rawScriptCoverage, the underlying V8 coverage data. Puppeteer CoverageEntry API · JSCoverageEntry API
1. What each property means
| Property | Meaning | How to use it |
|---|---|---|
ranges |
An array of objects with start and end positions for covered portions of the resource. |
Use the positions to identify portions counted as used. They refer to offsets in text; they are not source-map objects. |
text |
The full stylesheet or script content represented by the entry. | Use it with ranges to inspect or transform the coverage report. |
url |
The stylesheet or script URL. | Use it to identify which resource the entry represents. |
rawScriptCoverage |
Optional raw V8 script coverage on a JavaScript entry. | Request it only when downstream processing needs V8’s underlying coverage data. |
The entry is a report record, not a precomputed percentage. To calculate a percentage, you need to define what counts as used and how to count positions. Puppeteer’s example sums each resource’s text length and the lengths of reported ranges, then computes a ratio. If you need a standard report format, Puppeteer documents converting coverage output with puppeteer-to-istanbul. Coverage class example
2. Collect and inspect coverage
Install Puppeteer in a Node.js project, save the following as coverage.mjs, then run it with node coverage.mjs. The script starts JavaScript and CSS coverage before navigation, waits for the page to load, and stops both collectors to obtain arrays of entries.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await Promise.all([
page.coverage.startJSCoverage(),
page.coverage.startCSSCoverage(),
]);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const [jsEntries, cssEntries] = await Promise.all([
page.coverage.stopJSCoverage(),
page.coverage.stopCSSCoverage(),
]);
for (const [kind, entries] of [['JS', jsEntries], ['CSS', cssEntries]]) {
console.log(`${kind}: ${entries.length} resources`);
for (const entry of entries) {
const coveredChars = entry.ranges.reduce(
(sum, range) => sum + (range.end - range.start - 1),
0,
);
console.log({
kind,
url: entry.url,
textLength: entry.text.length,
ranges: entry.ranges,
coveredChars,
});
}
}
} finally {
await browser.close();
}
This follows the documented example’s range-length calculation. Treat the result as a position-based estimate for the collected report, not a universal measure of runtime usefulness. Coverage depends on what the page did during the collection window: code that runs only after a click, scroll, login, or other interaction will not be exercised unless your script performs that action before stopping coverage.
Exercise important states before stopping
For meaningful results, perform the user actions your coverage question concerns between navigation and the stop calls. For example, add a click before stopping:
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.click('[data-testid="open-menu"]');
await page.waitForSelector('[data-testid="menu-panel"]');
const jsEntries = await page.coverage.stopJSCoverage();
Replace the selectors with elements in the page under test. If a selector is absent, Puppeteer’s wait or click can fail; use a selector that exists for the state you are testing or handle the optional state explicitly.
3. JavaScript and CSS collection options
JavaScript and CSS are collected separately. The corresponding calls are startJSCoverage()/stopJSCoverage() and startCSSCoverage()/stopCSSCoverage(). The stop methods return arrays of entries for their resource type. JavaScript has collection options for granularity, navigation reset, anonymous scripts, and raw V8 data. stopJSCoverage() · JSCoverageOptions
| Choice | Documented behavior | When it matters |
|---|---|---|
| Block or function granularity | JavaScript coverage defaults to block-level collection; function-level collection is available. | Choose the granularity relevant to your analysis, and keep it consistent when comparing runs. |
| Anonymous scripts | Anonymous scripts are omitted by default. These include dynamically created scripts such as those from eval or new Function. |
Enable anonymous-script reporting if those scripts are part of the code you need to analyze. Reported scripts use a debugger://VM URL unless a //# sourceURL comment provides a name. |
| Raw V8 coverage | Raw V8 coverage is excluded by default. | Enable it if a consumer needs the underlying V8 entry; otherwise the regular entry fields are usually the relevant surface. |
| Reset on navigation | JavaScript coverage resets on navigation by default. | Disabling reset does not guarantee that coverage survives a page navigation; Chrome may discard the old execution environment and its data. |
Pass options to startJSCoverage() according to the installed Puppeteer version’s JSCoverageOptions reference. The exact option names and defaults are documented there; avoid copying an option object from a different Puppeteer release without checking that reference.
4. Navigation and multi-page collection
Coverage belongs to page execution contexts. When you need reports across navigations, collect each page’s report before leaving that page, start a new collection after navigation, and merge the resulting reports in your own processing step. Setting resetOnNavigation to false is not a preservation guarantee: Chrome can discard the prior page’s execution environment and coverage. Navigation behavior in JSCoverageOptions
- Start JS and/or CSS coverage on the current page.
- Navigate or perform the interactions for that page.
- Stop coverage and retain the returned arrays.
- Navigate to the next page and start a fresh collection.
- Combine or transform the saved reports after collection.
This keeps the collection boundary explicit and avoids assuming that one active collector will preserve every script’s data through navigation.
5. Interpreting and processing entries
For each entry, ranges describes covered offsets while text provides the complete resource contents. The simple formula below mirrors Puppeteer’s documented example:
const totalChars = entries.reduce((sum, entry) => sum + entry.text.length, 0);
const usedChars = entries.reduce(
(sum, entry) => sum + entry.ranges.reduce(
(rangeSum, range) => rangeSum + range.end - range.start - 1,
0,
),
0,
);
const percentage = totalChars === 0 ? 0 : (usedChars / totalChars) * 100;
console.log(`${percentage.toFixed(1)}% by the documented character-count method`);
This is a character-position calculation, not a byte count for every encoding and not necessarily the same as a build tool’s dead-code or bundle-size metric. Preserve the original text and ranges when downstream tooling needs to interpret the positions. To feed an Istanbul-compatible workflow, use the conversion route cited in Puppeteer’s Coverage documentation rather than treating the entry itself as an Istanbul report.
6. Reliability, runtime, and storage considerations
- Collection window: The longer and more complete the exercise of the page, the more application states can contribute to the report. Define the states you care about and trigger them deliberately.
- Repeatability: Use the same navigation conditions, interactions, and coverage settings when comparing runs. Differences in exercised behavior change the report.
- Payload size: Each entry includes full resource text, so retaining many reports can consume storage. Store only what later analysis needs, or process and release entries after use.
- Navigation reliability: Stop coverage before navigating away when the previous page’s report matters. Do not rely on
resetOnNavigation: falseas a data-retention mechanism. - Cost: Puppeteer coverage has no per-entry fee described in the API. Operational cost comes from the browser runtime and the infrastructure where your automation runs; the dossier provides no benchmark or fixed cost estimate.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No entry for a script you expected | It may be anonymous, or the code path did not run while coverage was active. | Check the anonymous-script option and exercise the relevant behavior before stopping coverage. Anonymous scripts can be labeled with a sourceURL comment when appropriate. |
| Coverage appears to disappear after navigation | The old page execution environment may have been discarded even with navigation reset disabled. | Stop coverage before navigating away, then start a new collection and merge the saved reports. |
| Only JavaScript or only CSS appears | Only that resource type’s collector was started and stopped. | Start and stop both collectors if the report needs both JS and CSS. |
| The percentage seems unexpectedly low | The page may load code whose behavior was not exercised during the measurement window. | Trigger relevant interactions, delayed states, and routes before stopping. Keep the workload consistent across comparisons. |
| Raw coverage field is missing | Raw V8 data is excluded by default. | Enable the documented raw-coverage option if your consumer requires it. |
| Range totals look unlike bundle bytes | Ranges are source-text positions and the documented calculation counts characters using a specific offset expression. | Interpret this as a position-based estimate; use the appropriate build or coverage tooling for byte-level bundle analysis. |
8. Or skip the browser setup
If your goal is to get a screenshot of a page rather than inspect JavaScript or CSS coverage, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; it does not replace Puppeteer’s Coverage API.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers indicating the page verdict and billing status. An MCP server lets AI agents, including Claude and Cursor, use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
9. Frequently asked questions
Does CoverageEntry contain a coverage percentage?
No. It contains resource text and covered ranges; calculate a metric from those fields using a definition appropriate to your use case.
Is CoverageEntry the same as JSCoverageEntry?
JSCoverageEntry is the JavaScript-specific entry and can include optional raw V8 coverage data in addition to the base entry fields.
Can coverage tell me whether code is safe to delete?
It tells you what was covered during the run. A single run cannot establish that code is unused in every route, user state, or runtime condition.
Does ScreenshotNeo return Puppeteer coverage data?
No. ScreenshotNeo captures screenshots or PDFs. Use Puppeteer’s Coverage API when you need JavaScript or CSS coverage entries.


