Skip to content

fix: collect coverage created after page load, and fill README gaps - #1007

Merged
jennifer-shehane merged 6 commits into
masterfrom
claude/wonderful-babbage-lzehgi
Oct 27, 2026
Merged

jennifer-shehane merged 6 commits into
masterfrom
claude/wonderful-babbage-lzehgi

Conversation

@jennifer-shehane

@jennifer-shehane jennifer-shehane commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Additional details

This PR fills five documentation gaps that kept coming up in issues, and fixes one bug found while checking them. Each issue lives in its own commit, so it's easy to review them one at a time.

  • Where NYC options are read from (Clarify NYC configration location #947). A new Where NYC options are read from section explains that the plugin reads NYC options and the coverage:report script from the folder that holds the Cypress config file. Passing --config-file cypress/cypress.config.ts moves that folder into cypress/, which is what tripped up the reporter. The section lists the supported files and the order they're merged in, and includes a monorepo example.

  • ES module import for the task (Cypress Code Coverage cypress.config.ts file - setupNodeEvents(on, config) error required is not defined #715). The Install section now shows import codeCoverageTask from '@cypress/code-coverage/task' next to the require form. On Cypress 15.10 and later, which the plugin requires, both forms work in either kind of config file. The original require is not defined error no longer reproduces. The section also explains that TypeScript's node10 module resolution can't find the plugin's types (TS2307), and which tsconfig.json settings fix it.

  • Coverage works locally but not in CI (Cannot find coverage file .nyc_output/out.json in the automated build. #528). A new Coverage works locally but not in CI entry under Common issues covers the usual cause: CI tests an uninstrumented production build. It covers the Babel env and vite-plugin-istanbul defaults, and adds a small spec that fails when window.__coverage__ is missing. This commit also removes the Cannot find coverage file branch from coverageReport in lib/task.ts. saveCoverage writes out.json on the line right before that check, so the branch could never run (and hasn't since v3.13.4).

  • Component testing (component testing suport #596). A new Component testing section walks through the support import in cypress/support/component.js, registering the task in component.setupNodeEvents, and instrumenting the dev server with vite-plugin-istanbul or with babel-plugin-istanbul in a webpack config. A new test-apps/component-vite app runs cypress run --component against a React component and checks for 100% coverage.

  • SWC and Next.js (Add support for SWC #583). A new Instrument with SWC section shows how to add swc-plugin-coverage-instrument through .swcrc or Next.js experimental.swcPlugins, and explains the SWC plugin version matching that broke things for people in the thread.

    Testing this against a real Next.js 16 app turned up a bug: the report was empty even though window.__coverage__ existed. Next.js runs client component code during hydration, after window:load, and the plugin only captured the coverage object at window:load and at the start of each test. lib/support.ts now reads the window once more in afterEach, before sending coverage. It also reads each page right before it unloads, so a test that visits several such pages keeps all of them. A coverage object that was already captured is updated in place, not sent twice. The new test-apps/swc-webpack app builds with swc-loader and loads its bundle after load. Its test visits two such pages, and reports 0% without the fix and 100% with it.

Both new test apps are in the CI matrix.

Steps to test

  1. Read the five README sections linked above.

  2. Run npm run build, npm test, and npm run format:check. They should all pass.

  3. Run the new test apps:

    npm run build
    cd test-apps/swc-webpack
    npm i
    npm test
    npm run coverage:verify
    npm run coverage:check-files

    Then do the same in test-apps/component-vite.

  4. Optionally, run every test app with npm run apps:test:coverage:verify to confirm the lib/support.ts changes don't affect existing setups.

How has the user experience changed?

Apps that create window.__coverage__ after the page's load event, such as Next.js apps instrumented with SWC, now get a coverage report instead of an empty one. Other setups see no change: every existing test app passes with the same coverage as before.

Users also get new README guidance on where NYC options are read from, importing the task from an ES module config file, debugging empty coverage in CI, setting up coverage for component tests, and instrumenting with SWC.


Generated by Claude Code


Note

Medium Risk
Changes when browser coverage is sampled in every E2E/component run; behavior is regression-tested via new apps and the full CI matrix, but timing-sensitive apps could see different merge results.

Overview
Fixes late window.__coverage__ collection so apps that populate coverage after window:load (e.g. Next.js hydration / post-load bundles) get full reports. lib/support.ts re-reads coverage on window:before:unload, again in afterEach via cy.window(), and defers sending through cy.then() so counters are flushed before tasks run.

lib/task.ts drops the unreachable “cannot find coverage file” early exit after saveCoverage always writes out.json.

README adds setup/troubleshooting for ES module task imports, component testing (Vite/webpack + NYC excludes), SWC / Next.js instrumentation, where NYC config is resolved (Cypress config folder), and empty coverage in CI; existing NYC/custom-report sections link to that.

CI runs new test-apps/component-vite and test-apps/swc-webpack examples; .gitignore adds .swc/.

Reviewed by Cursor Bugbot for commit a11e1b2. Bugbot is set up for automated code reviews on this repo. Configure here.

The plugin reads NYC options and the `coverage:report` script from
`process.cwd()`, which Cypress sets to the folder holding the Cypress
config file. The README only said "package.json" or ".nycrc", so users
who pass `--config-file` in a monorepo put their options in the wrong
folder and saw them silently ignored.

Add a "Where NYC options are read from" section that lists the supported
files, their merge order, and the folder they must live in, with a
monorepo example. Link to it from the sections that mention package.json
and .nycrc.

Closes #947

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
The Install section only showed `require('@cypress/code-coverage/task')`,
so people with a `cypress.config.ts` or `"type": "module"` project
weren't sure how to register the task. Add an `import` example and note
that both forms work in either kind of config file.

Also document that TypeScript's older `node10` module resolution can't
find the plugin's types, because its entry points are only declared in
`exports`, and name the `tsconfig.json` settings that fix it.

Closes #715

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
People who get coverage locally but an empty report in CI are almost
always testing an uninstrumented production build. Add a "Coverage
works locally but not in CI" entry to Common issues that quotes the
warnings they'll see, explains how Babel env config and
vite-plugin-istanbul skip production builds by default, and adds a
spec that fails when `window.__coverage__` is missing.

Also remove the "Cannot find coverage file" branch from coverageReport.
saveCoverage writes out.json on the line before the check, so it could
never run. The README entry mentions the old message for anyone
searching for it.

Closes #528

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
The plugin already collects coverage from component tests, but the
README never said how to set it up, so people kept missing the support
import in `cypress/support/component.js` or the dev server
instrumentation. Add a "Component testing" section covering the support
import, registering the task in `component.setupNodeEvents`, and
instrumenting the dev server with vite-plugin-istanbul or with
babel-plugin-istanbul in a webpack config.

Add test-apps/component-vite, a React component tested with
`cypress run --component`, which checks for 100% coverage of the
component. Add it to the CI matrix and to the README's list of test apps.

Closes #596

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
The plugin captured `window.__coverage__` only at `window:load` and at
the start of each test. Apps that create the coverage object later, such
as a Next.js app whose components run during hydration, ended up with
an empty report. Re-read the window in `afterEach`, before sending
coverage. Objects already captured are matched by identity, so they
aren't sent twice.

Add an "Instrument with SWC" section to the README. It explains that the
plugin reads any Istanbul-format coverage, shows how to add
swc-plugin-coverage-instrument through `.swcrc` or Next.js
`experimental.swcPlugins`, and notes that the SWC plugin version must
match the SWC version that loads it.

Add test-apps/swc-webpack, which builds with swc-loader and loads the
bundle after the page's `load` event. It reports 0% without the fix and
100% with it. Add it to the CI matrix and the README's list of test apps.

Closes #583

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
Re-reading the window in `afterEach` only reached the page a test ended
on. If a test visited a page that created `__coverage__` after
`window:load` and then visited another page, the first page's coverage
was lost. Also read the window on `window:before:unload`, so every page
is read before it goes away.

Extend test-apps/swc-webpack to visit two pages that run different
branches. It reports 75% lines when only the last page is read, and
100% with this change.

Refs #583

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r2ZU5whUYbhTLy6nfa
@jennifer-shehane
jennifer-shehane merged commit 002d288 into master Oct 27, 2026
32 checks passed
@jennifer-shehane
jennifer-shehane deleted the claude/wonderful-babbage-lzehgi branch October 27, 2026 22:01
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 4.1.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants