Puppeteer Coverage Entries Explained
Learn what Puppeteer coverage entries contain, how to collect JavaScript and CSS reports, interpret ranges, and account for missing code.
A Puppeteer CoverageEntry is one report for a script or stylesheet observed during a coverage recording. It has a url, the resource source in text, and ranges that mark source positions Puppeteer recorded as covered. JavaScript and CSS use separate start and stop methods, and the results describe activity during the recording window rather than every path the application could ever execute. See the CoverageEntry reference and Coverage API.
Collect JavaScript and CSS coverage
Start the recorders before the navigation and interactions you want to measure. Stop them after that activity to receive arrays of entries. This runnable example saves the raw reports as JSON and prints Puppeteer’s documented used-byte percentage.
// coverage.mjs
import fs from 'node:fs/promises';
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'});
// Exercise the page here: click menus, open dialogs, or visit states
// whose code you want represented in this recording.
const [jsCoverage, cssCoverage] = await Promise.all([
page.coverage.stopJSCoverage(),
page.coverage.stopCSSCoverage(),
]);
const entries = [...jsCoverage, ...cssCoverage];
await fs.writeFile('coverage.json', JSON.stringify({jsCoverage, cssCoverage}, null, 2));
let totalBytes = 0;
let usedBytes = 0;
for (const entry of entries) {
totalBytes += entry.text.length;
for (const range of entry.ranges) {
usedBytes += range.end - range.start - 1;
}
}
console.log(`Entries: ${entries.length}`);
console.log(`Used bytes (Puppeteer example formula): ${totalBytes ? (usedBytes / totalBytes * 100).toFixed(2) : 'n/a'}%`);
} finally {
await browser.close();
}
Run it in a project with Puppeteer installed, for example with npm install puppeteer and node coverage.mjs. The page activity between start and stop defines what the report can show. The official API example uses the same range arithmetic; the percentage is a session-specific summary, not a measure of all possible application behavior.
What each entry field means
| Field | Meaning | How to use it |
|---|---|---|
url |
URL associated with the script or stylesheet. | Group reports by resource and trace unexpected sources. Anonymous scripts may use a debugger://VM URL when reported. |
text |
The content of that script or stylesheet. | Ranges index into this source text. Use it to map covered positions back to code. |
ranges |
An array of objects with numeric start and end positions. |
These are source offsets, not line numbers. They describe covered portions; do not interpret them as line/column pairs. |
The interface defines these fields for both resource kinds: JavaScript coverage returns script entries, while CSS coverage returns stylesheet entries. The JavaScript entry can additionally include rawScriptCoverage when raw V8 data is requested. See CoverageEntry and JSCoverageEntry.
Interpret ranges and calculate a summary carefully
For a quick aggregate, Puppeteer’s example adds range.end - range.start - 1 for every range, then divides by the total source text length. That formula is useful for reproducing the documented example, but it is not a universal semantic definition of “used code.” The report is based on source positions and the selected coverage granularity, and the denominator includes all returned source text.
- Do not assume offsets are line numbers; they are positions within
text. - Do not add overlapping intervals naively in custom code unless you know the intervals are disjoint or merge them first; otherwise the numerator can be double-counted.
- Do not add byte counts from different encodings and treat them as exact file bytes.
text.lengthcounts JavaScript string code units; Puppeteer’s example labels the result as bytes, but for strict storage-byte accounting you would need to encode the text explicitly. - Keep resource type and run configuration alongside the output so comparisons are meaningful.
When you need source-level reports compatible with Istanbul workflows, Puppeteer’s coverage documentation points to puppeteer-to-istanbul. Raw entries are convenient for custom analysis; conversion is preferable when your downstream tooling expects Istanbul coverage structures.
Options that change JavaScript coverage
startJSCoverage(options) accepts four documented options. Defaults below follow the current API reference; consult it when upgrading Puppeteer because documentation versions can change.
| Option | Documented default | Effect |
|---|---|---|
resetOnNavigation |
true |
Resets coverage on navigation by default. Setting false does not guarantee data survives: Chrome may discard the prior page execution context. |
reportAnonymousScripts |
false |
Includes scripts without an associated URL, such as eval or new Function. Reported URLs start with debugger://VM unless a //# sourceURL=... comment supplies one. |
includeRawScriptCoverage |
false |
Includes raw V8 script coverage data on JavaScript entries. |
useBlockCoverage |
true |
Collects block-level coverage. Set false for function-level coverage. |
Example with explicit choices:
await page.coverage.startJSCoverage({
resetOnNavigation: true,
reportAnonymousScripts: true,
includeRawScriptCoverage: false,
useBlockCoverage: true,
});
const scripts = await page.coverage.stopJSCoverage();
CSS has its own startCSSCoverage({resetOnNavigation}) option. CSS coverage omits dynamically injected style tags without sourceURLs, according to the Coverage API reference.
Navigation, interaction, and coverage scope
Coverage is an observation of one recording interval. If you start after page load, initial scripts or styles may be absent. If you stop before exercising a menu, route, or conditional feature, its code may appear unused even though it runs in another state. A single page visit is therefore not a complete inventory of all code paths.
- Choose the page states and user actions relevant to the question.
- Start coverage before the earliest navigation or action to include.
- Perform those actions with deterministic test data where possible.
- Stop and persist each report before navigating away if you need to preserve that page’s data.
- Repeat across routes and states, then merge or compare reports with the configuration recorded.
The JavaScript option reference explicitly warns that disabling navigation resets does not ensure coverage survives navigation, because Chrome can discard the old execution environment. For reliable multi-page collection, stop coverage before leaving a page, then start a new collection and merge the reports. Avoid merging blindly: the same URL can serve different source versions, so consider including a content hash in your own aggregation key.
Why entries or code may be missing
- Anonymous JavaScript is absent: the default excludes scripts without URLs. Enable
reportAnonymousScripts, or add asourceURLcomment when you control generated code. - Injected CSS is absent: dynamically injected style tags without sourceURLs are excluded by CSS coverage. Add a sourceURL where applicable or account for the omission in your reporting.
- Coverage vanishes after navigation: navigation resets by default, and preserving coverage with the option disabled is not guaranteed. Stop and save before navigating, then start a new report.
- A branch appears uncovered: coverage records what ran in this session. Trigger the relevant condition, state, or interaction and collect another run.
- A resource URL is unfamiliar: inspect the entry’s URL and source; the page may load code from a third party or a generated script may use a VM/sourceURL identifier.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Returned arrays are empty or unexpectedly small | Recorder started too late, stopped too early, or the page did little work during the interval. | Start before navigation and include the interactions and waits needed to reach the states under study. |
| Coverage from the first page is missing after a route change | Navigation reset behavior or Chrome discarded the old execution context. | Stop and store the report before navigation; start another recording on the next page. |
eval-generated code is missing |
Anonymous script reporting is disabled by default. | Set reportAnonymousScripts: true; add a sourceURL when you own the generated script. |
| Injected styles do not appear | CSS coverage excludes dynamic style tags without sourceURLs. | Use sourceURLs for generated styles when possible and treat the coverage report as scoped to resources Puppeteer reports. |
| Percentage exceeds 100% or looks implausible | Custom aggregation double-counts overlapping ranges, mismatches source and ranges, or combines incompatible reports. | Use the documented formula on each entry, merge intervals before custom counting, and retain run/source identity. |
| Coverage indicates unused code that is used in production | The browser run did not visit the production state, route, or environment branch. | Expand the scenario matrix; coverage is evidence about observed execution, not proof of universal non-use. |
Performance, reliability, and cost
Coverage collection adds browser instrumentation and retains report data until it is stopped, so keep the recording window focused and persist reports promptly for large pages or repeated runs. The references do not provide a universal overhead benchmark; measure your own workload if runtime matters. For repeatable results, use the same browser version, page state, waits, inputs, and coverage options, and keep reports from different source revisions separate.
Coverage has no per-entry service charge: it is collected by your Puppeteer process and browser. The practical costs are browser runtime, memory, report storage, and analysis time. Do not use one percentage as a release gate without defining which pages and interactions the run covered; missing scenarios can make a healthy feature look unused.
Or skip the browser setup
If your actual goal is a visual record of a page rather than executed JavaScript and CSS ranges, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It does not produce Puppeteer coverage entries; use it when you need the rendered page image instead of instrumentation.
With the ScreenshotNeo API:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Cookie banners are accepted like a visitor and removed along with known consent platforms, newsletter popups, and chat widgets before the capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. An MCP server lets AI agents use screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does one CoverageEntry equal one file?
It represents one reported script or stylesheet resource entry. Do not assume every logical source file maps one-to-one to an entry, especially with generated or bundled resources.
Are ranges line numbers?
No. They are numeric positions in the entry’s source text.
Can I compare percentages between test runs?
Yes, if the source revision, browser conditions, scenarios, options, and aggregation method are comparable. Otherwise, the percentages can reflect different inputs as much as different code use.
Does Puppeteer coverage tell me what a screenshot contains?
No. Coverage reports executed resource positions. A screenshot records rendered pixels; ScreenshotNeo provides that separate output through its API.


