How to Generate Playwright Test Coverage Reports
Learn the difference between Playwright test reports and code coverage, then generate Istanbul, HTML, lcov, and Chromium coverage artifacts.
Direct answer: Playwright test reporters show which tests passed, failed, skipped, or were flaky. They do not measure application code coverage. For JavaScript execution coverage in a Chromium page, call page.coverage.startJSCoverage(), exercise the page, call stopJSCoverage(), and convert the V8 entries with v8-to-istanbul. For source-level statement, branch, function, and line coverage across end-to-end tests, instrument the application with Istanbul tooling, run Playwright, then render the collected data with nyc report.
1. Decide which report you need
| Goal | Use | Browser scope | Typical output |
|---|---|---|---|
| Test outcomes | Playwright reporters | Any configured Playwright project | HTML, JSON, JUnit, blob |
| JavaScript actually executed by a page | Playwright Coverage API plus v8-to-istanbul |
Chromium only | Istanbul JSON, then your chosen report |
| Application source coverage across E2E tests | Istanbul-instrumented build plus nyc |
Browsers that load the instrumented build | Text, HTML, lcov |
Playwright’s Coverage API gathers JavaScript and CSS usage, and its documentation states that Coverage APIs are supported only on Chromium-based browsers. Its reporters describe test results instead.
2. Path A: collect browser JavaScript coverage
Use this path when you need the JavaScript executed during a browser session, such as a page-load or interaction audit. It produces raw coverage entries; converting and reporting them is a separate step.
Install dependencies
npm install -D playwright v8-to-istanbul
Runnable Node.js example
const { chromium } = require('playwright');
const v8toIstanbul = require('v8-to-istanbul');
const fs = require('node:fs/promises');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await page.coverage.startJSCoverage();
await page.goto('https://your-app.example', { waitUntil: 'networkidle' });
await page.getByRole('button', { name: 'Open menu' }).click();
const entries = await page.coverage.stopJSCoverage();
const merged = {};
for (const entry of entries) {
if (!entry.url || !entry.source) continue;
const converter = v8toIstanbul('', 0, { source: entry.source });
await converter.load();
converter.applyCoverage(entry.functions);
const istanbul = converter.toIstanbul();
for (const [file, data] of Object.entries(istanbul)) {
merged[file] = data;
}
}
await fs.mkdir('coverage', { recursive: true });
await fs.writeFile('coverage/coverage-final.json', JSON.stringify(merged, null, 2));
console.log('Wrote coverage/coverage-final.json');
} finally {
await browser.close();
}
})();
The official example follows the same startJSCoverage() → page exercise → stopJSCoverage() sequence and converts V8 entries with v8-to-istanbul. Persist the Istanbul JSON before generating a human-readable report.
Render HTML, text, or lcov
npm install -D nyc
npx nyc report --temp-dir coverage --reporter=text
npx nyc report --temp-dir coverage --reporter=html
npx nyc report --temp-dir coverage --reporter=lcov
Open coverage/index.html for the HTML report. If your collector writes to a different directory, pass that directory with --temp-dir. Coverage APIs are Chromium-only; configure separate Firefox or WebKit jobs for test execution, but do not describe their results as Coverage API data.
3. Path B: instrument the application and use Istanbul/nyc
Choose this path for source-level application coverage across a real Playwright suite. Instrument the exact JavaScript files or bundles that the browser loads. An uninstrumented production bundle produces no meaningful Istanbul counters.
Install and run
npm install -D @playwright/test babel-plugin-istanbul nyc
npx playwright test
npx nyc report --reporter=text
npx nyc report --reporter=html
npx nyc report --reporter=lcov
Instrument a Babel build
// babel.config.cjs
module.exports = {
plugins: process.env.BABEL_ENV === 'coverage'
? ['babel-plugin-istanbul']
: []
};
# Build the app with instrumentation before starting its server
BABEL_ENV=coverage npm run build
npx playwright test
npx nyc report --reporter=html
Keep the instrumented build tied to the same source revision as the tests. If source maps are used, publish them with the build so file and line locations remain useful. The Istanbul tooling documents ISTANBUL_TEMP_DIR for changing the temporary directory from the default .nyc_output.
4. Path C: generate Playwright test-result reports
If “coverage” means a report of test outcomes, configure a Playwright reporter instead of collecting JavaScript coverage.
// playwright.config.js
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
reporter: [
['html', { outputFolder: 'playwright-report', open: 'never' }],
['json', { outputFile: 'test-results/results.json' }],
['junit', { outputFile: 'test-results/results.xml' }]
]
});
npx playwright test
npx playwright show-report playwright-report
The HTML reporter lets you filter by browser, passed tests, failed tests, skipped tests, and flaky tests. It does not create statement, branch, function, or line coverage metrics.
5. Parallel and sharded runs
For test-result data, use Playwright’s blob reporter in each shard and merge the retained artifacts:
# Each shard
npx playwright test --reporter=blob --output=test-results/blob-shard-1
# After downloading all blob artifacts into blob-reports/
npx playwright merge-reports --reporter html blob-reports
Blob merging combines Playwright test-result data, not JavaScript execution coverage. For Istanbul coverage, retain each shard’s coverage files, then merge them with an Istanbul-aware workflow before running nyc report. Ensure every shard uses the same instrumented build and source-map revision.
6. Configuration and edge cases
- Navigation timing: start coverage before navigation so initial scripts are included.
- Interactions: execute the routes, clicks, dialogs, and feature flags you want measured; unvisited code remains uncovered.
- Workers and frames: coverage collected from a page may not represent every execution context. Validate your package versions and inspect the returned entries.
- Third-party scripts: filter external URLs if they would distort your application totals.
- Cache and service workers: a cached response can change which files execute. Use a controlled context when comparing runs.
- Source maps: preserve them through the instrumented build to map generated bundles back to source files.
- Thresholds: apply Istanbul thresholds in CI only after confirming that all required routes are exercised.
7. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
show-report has no coverage percentages |
You generated a test-result report. | Use the Coverage API or Istanbul instrumentation, then run nyc report. |
| Coverage API fails in Firefox or WebKit | The official API is Chromium-only. | Run coverage collection in Chromium, or use an instrumented application build for cross-browser tests. |
| HTML report is empty | No Istanbul JSON was written, or nyc is reading the wrong directory. |
Check the output files and pass the correct --temp-dir. |
| Files show 0% despite tests running | The browser loaded an uninstrumented bundle or a different URL. | Verify the served assets, build mode, and source-map paths. |
| Parallel totals are inconsistent | Artifacts were overwritten or generated from different revisions. | Give each shard a unique artifact name and merge only matching builds. |
| Lines map to generated code | Source maps were missing or invalid. | Generate, serve, and retain source maps for the instrumented build. |
8. Performance, reliability, and cost
- Coverage collection adds work while the browser records execution data and while Istanbul converts it. Keep coverage runs separate from latency-sensitive smoke tests when CI time matters.
- Use a focused route set for pull requests and a broader nightly suite for trend reporting.
- Persist raw coverage artifacts so reports can be regenerated without rerunning browsers.
- Pin compatible versions of Playwright,
v8-to-istanbul, Babel instrumentation, andnyc; third-party coverage packages are version-sensitive. - Do not compare percentages from different route sets, browser engines, builds, or feature-flag states.
9. Or skip the browser setup
If you only need a clean visual capture of a page or report, ScreenshotNeo provides a single screenshot API request. It is separate from Playwright code coverage, but it can remove the browser automation setup for visual artifacts.
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode 'url=https://stripe.com' -o shot.webp
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for options. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server lets Claude, Cursor, and other MCP clients take screenshots. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
10. FAQ
Does Playwright have a built-in code-coverage dashboard?
No. Playwright has test-result reporters and a Chromium Coverage API; Istanbul-compatible tooling turns execution data into coverage reports.
Can one report combine Chromium, Firefox, and WebKit coverage?
The official Coverage API is Chromium-only. Cross-browser application coverage requires instrumenting the application and collecting compatible Istanbul data from each run.
Should I use the Coverage API or Istanbul instrumentation?
Use the API for JavaScript executed in a Chromium page. Use instrumentation when you need source-level application metrics across an end-to-end suite.
Where does the HTML report come from?
npx playwright show-report opens Playwright test results. npx nyc report --reporter=html creates an Istanbul coverage report.


