Making Code Coverage Work with Playwright (Without Losing Your Mind)
A couple months ago, I spent half a day trying to figure out how to get code coverage stats for my Playwright end-to-end and component tests. You’d think it would be straightforward, right? Well, it is — if you’re using Vite. With Vite, you can just drop in a plugin like vite-plugin-istanbul and you’re mostly good to go.
But if you’re using a custom setup or something that’s not Vite (which I was), then it becomes a surprisingly tedious process. There’s Babel config, Istanbul instrumentors, coverage directories, test.beforeAll() and test.afterAll() blocks, and honestly it seemed like so much work that I was convinced that someone would’ve faced the same issue and maybe somebody would’ve made a tool that would make it easier.
Luckily, I stumbled across an open-source package that did exactly just that; and also saved me a ton of time and effort installing and configuring this manually.
The Manual Way (aka: the way I didn’t do it)
Before I get into how I did it using @bgotink/playwright-coverage, I want to talk briefly about how you’d usually set up coverage for Playwright the hard way- using Istanbul manually.
That process typically involves:
• Adding Babel and babel-plugin-istanbul to your build process
• Configuring a custom loader for transforming your app code
• Writing setup and teardown logic in your test files to start/stop collecting coverage
• Merging the raw coverage output and generating a final report with nyc or similar tools
Basically it was a whole bunch of boilerplate, config, and complexity for something that feels like it should be built-in. Although I tried to do it this way first, with every step I realized how deep this rabbit hole went.
What I Actually Used (and Loved)
I then found @bgotink/playwright-coverage, an npm package that wraps all this complexity into a simple-to-use helper. It made integrating code coverage into my Playwright setup super easy.
Here’s how I used it:
Installed the package:
npm install @bgotink/playwright-coverageCreated a new file (e.g. e2e/fixtures/coverageFixture.js) and merged the base test with the coverage fixtures:
import {mergeTests, test as base, expect} from '@playwright/test';
import {test as testWithCoverage} from '@bgotink/playwright/coverage';
const test = mergeTests(
testWithCoverage,
base, // You can add your own extended test object here as well
);
export {test, expect}Replaced the original import in my E2E test files:
// Before
import { test, expect } from '@playwright/test';
// After
import { test, expect } from ‘../fixtures/coverageFixture’;Added the reporter to my
playwright.config.jsfile:
import {defineCoverageReporterConfig} from '@bgotink/playwright-coverage';
import {defineConfig} from '@playwright/test';
export default defineConfig({
// ...
reporter: [
['list'],
[
'@bgotink/playwright-coverage',
defineCoverageReporterConfig({
/* Path to the root files should be resolved from, most likely your repository root */
sourceRoot: __dirname,
/* Files to ignore in coverage, useful
- if you're testing the demo app of a component library and want to exclude the demo sources
- or part of the code is generated
- or if you're running into any of the other many reasons people have for excluding files */
exclude: ['path/to/ignored/code/**'],
/* Directory in which to write coverage reports */
resultDir: path.join(__dirname, 'results/e2e-coverage'),
/* Configure the reports to generate.
The value is an array of istanbul reports, with optional configuration attached. */
reports: [
/* Create an HTML view at <resultDir>/index.html */
['html'],
/* Create <resultDir>/coverage.lcov for consumption by tooling */
[
'lcovonly',
{
file: 'coverage.lcov',
},
],
/* Log a coverage summary at the end of the test run */
[
'text-summary',
{
file: null,
},
],
],
/* Configure watermarks, see https://github.com/istanbuljs/nyc#high-and-low-watermarks */
// watermarks: {},
}),
],
],
});No Babel config. No writing hooks. No complex loaders. The coverage reports just worked.
Cool Takeaways
• The @bgotink/playwright-coverage package uses V8 coverage under the hood, which means it works without needing source transformation — so no Babel or Webpack magic needed.
• You can generate reports using standard tools like nyc and even output to lcov for CI integration.
• By extending the Playwright test object using mergeTests, you can make this kind of instrumentation feel like a native part of your testing setup.
⚠️ One Caveat: V8-Only (Chromium Support Only)
Since this package relies on the native V8 coverage API, it only works in Chromium-based browsers like Chrome and Edge.
If you run tests on WebKit (Safari) or Firefox, you won’t get coverage results because those engines don’t expose V8-style instrumentation.
So in practice:
• ✅ Coverage works on Chromium
• ❌ No coverage on Firefox or WebKit
• So, you might want to enable coverage only for Chromium runs in CI
But What About Component Tests?
This was all for Playwright E2E tests. As of now, I couldn’t find any libraries or tools that do something similar for Playwright Component Tests (playwright-ct).
If you’re reading this and do know of a way to get coverage for Playwright CT — please let me know! Would love to update this post with better info.
And that’s pretty much it — fast, clean Playwright coverage without the setup nightmare.
@bgotink/playwright-coverage turned what looked like an afternoon of configs into a five-minute setup, and it’s been smooth sailing since.
Sure, it’s Chromium-only for now, but for most projects that’s a fair trade-off for simplicity.
So, if you’ve been putting this off, try giving it a shot! You’ll get meaningful coverage data without the boilerplate. And if you ever figure out how to get the same magic working for Playwright Component Tests, I’d love to hear about it! Thanks for reading!