Skip to main content

Pytest plugin for JavaScript coverage via Playwright CDP

Project description

pytest-jscov

Get a single, unified coverage report for your full-stack Python + JS/TS application.

This pytest plugin collects JavaScript and TypeScript code coverage from Playwright browser tests via Chrome's DevTools Protocol (CDP) and merges it into pytest-cov's combined report. In other words, we use Chrome's profiler to track which lines of code were executed.

Using pytest-jscov is perfect if you write your frontend tests in Python with Playwright already. Then you get coverage measurements on top with almost no extra effort.

Features

  • Collects V8 precise coverage from Chromium-based browsers via CDP
  • Resolves inline sourcemaps (e.g. from esbuild) to map coverage back to original .ts source files
  • Merges JS/TS line hits into pytest-cov so everything appears in one report
  • Works with --cov-branch, but no branch coverage is collected
  • Zero-config when coverage is not active: the jscov fixture is a no-op unless --cov is passed
  • Full VS Code integration: coverage gutters work for JS/TS files just for Python

Limitations

Requirements for use

You must implement your tests in Python using the async Playwright API with the Chrome browser for this to work. For details, see the installation section.

Loss of coverage data due to page navigation

Coverage collection is not perfect when doing page nagivation. Coverage data is attached to the currently executing page context. If that page reloads or navigates away before pytest-jscov reads the coverage data, the old execution context is gone and its coverage data with it.

We implement a partial workaround for this issue. When the plugin is active, Playwright browser contexts created via browser.new_context() and pages created via browser.new_page() are automatically instrumented so that reload, goto, go_back, and go_forward flush coverage immediately before those navigations run.

This improves coverage retention for navigations initiated through those page methods in tests, but it does not catch page navigation triggered from JavaScript, such as

window.location.assign(...)

For those cases, call save_coverage(page) just before the action that would replace the page context:

from pytest_jscov import save_coverage

await save_coverage(page)
await page.evaluate("window.location.assign('about:blank')")

Detection of executable lines

We use a custom code to detect executable lines in JS/TS files to keep this project lightweight. This code may not be perfect yet, if you encounter issues, drop an issue.

Installation

pip install pytest-jscov

The plugin requires pytest, pytest-cov, and playwright (with Chromium installed).

Register the coverage.py plugin

In your pyproject.toml:

[tool.coverage.run]
plugins = ["pytest_jscov.covplugin"]

[tool.coverage.pytest_jscov.covplugin]
static_root = "src/myapp/static"

static_root tells the plugin where your JS/TS source files live on disk, so it can match coverage data to real files.

The plugin automatically switches coverage.py to the plugin-compatible tracing ctrace core.

Use normal Playwright page creation

import pytest
from collections.abc import AsyncIterator
from playwright.async_api import Browser, Page

@pytest.fixture
async def page(browser: Browser) -> AsyncIterator[Page]:
   context = await browser.new_context()

   page = await context.new_page()
   await page.goto("http://localhost:8000")
   yield page

   await page.close()
   await context.close()

Any page created from that context will have its coverage recorded. The same holds for pages created directly with browser.new_page().

When the plugin is active, browser.new_context() and browser.new_page() return instrumented objects so that:

  • On each context.new_page(): opens a CDP session and starts V8 precise coverage for the new page
  • During the page lifetime: flushes coverage before page.reload(), page.goto(), page.go_back(), page.go_forward(), and page.close()
  • During the page lifetime: lets you call await save_coverage(page) to persist coverage before JS-triggered navigation
  • On context.close(): collects coverage from all tracked pages, then detaches their CDP sessions

When --cov is not passed to pytest, Playwright is left alone.

Run your tests

pytest --cov=src --cov-report=term

JS and TS files appear alongside Python in the coverage report:

Name                                  Stmts   Miss  Cover
----------------------------------------------------------
src/myapp/main.py                       180     90    50%
src/myapp/static/app.js                 441    144    67%
src/myapp/static/modules/foo.ts         194     14    93%
src/myapp/static/modules/bar.js         103     10    90%
----------------------------------------------------------
TOTAL                                   918    258    72%

Filtering

Just like with Python sources, you can restrict the coverage report to individual files or directories by passing a path to --cov, for example:

pytest --cov=src/myapp/static/foo.js

How it works

  1. The pytest plugin (pytest_jscov.plugin) provides the jscov fixture and a pytest_runtestloop hook. During each test, coverage entries from V8 are accumulated in memory. After all tests complete, the accumulated data is written as a .coverage.jscov file that pytest-cov's combine() step picks up automatically.

  2. The coverage.py plugin (pytest_jscov.covplugin) registers a file tracer and file reporter for JS/TS files. This teaches coverage.py how to find, read, and report on JavaScript and TypeScript source files.

Sourcemap support

When a script contains an inline sourcemap (//# sourceMappingURL=data:application/json;base64,...), the plugin decodes the VLQ mappings and attributes coverage to the original source files. This means if you use a bundler like esbuild to transpile TypeScript with --sourcemap=inline, coverage is reported against your .ts files, not the generated .js.

IDE integration

When using the Python Testing extension in VS Code, coverage gutters work for JS/TS files just like they do for Python. VS Code runs pytest with --cov --cov-branch automatically, and the merged report includes your frontend files — you'll see green/red line markers directly in your .ts and .js sources.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_jscov-0.7.0.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_jscov-0.7.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file pytest_jscov-0.7.0.tar.gz.

File metadata

  • Download URL: pytest_jscov-0.7.0.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pytest_jscov-0.7.0.tar.gz
Algorithm Hash digest
SHA256 453f62551cb79afc954ebd49949f4778f8533fceb868e6ae3b9e48338618fa65
MD5 0ea39daa95fc21216379124be45a5e73
BLAKE2b-256 5ac1a401d0efc41a26556673eb0585c2fe17b3ba934181cb4b6d2e015680f2f3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_jscov-0.7.0.tar.gz:

Publisher: release.yml on HDembinski/pytest-jscov

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_jscov-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_jscov-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pytest_jscov-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 025c80fd753b9e74879d8e779c69639d25304e78d2211d6b5b3756cefe9b621a
MD5 3cea282d2d4871b82123f509a9e47ee3
BLAKE2b-256 db39742872928381cbd90bb5d1eabbbbe9304e8c3304706b62762ad62b9cf7b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_jscov-0.7.0-py3-none-any.whl:

Publisher: release.yml on HDembinski/pytest-jscov

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page