ScreenshotNeo

BlogHow-to

How to Measure UI Test Coverage with Cypress

Measure Cypress coverage in two ways: source-code execution with Istanbul and nyc, and interactive UI coverage from Cypress Cloud Test Replay.

By the ScreenshotNeo team4 October 20269 min read

Cypress coverage has two distinct meanings. Code coverage measures which application statements, branches, functions, and lines ran during tests. It requires instrumenting your application and collecting the resulting counters. Cypress UI Coverage measures which interactive elements and views tests exercised, using recorded Cypress Cloud Test Replay data. They answer different questions and work best as complementary checks.

Use code coverage to find executed-code gaps; use UI Coverage to find controls and views tests have not reached. Neither percentage alone proves that a test made a meaningful assertion.

1. Choose the coverage question

Approach What it counts What it helps you find Requirements
Source-code coverage Executed statements, branches, functions, and lines Application logic that test runs never execute Instrumented application code and a coverage collector such as @cypress/code-coverage
Cypress UI Coverage Interactive elements and views exercised by tests Untested controls, views, and pages tests never visit Cypress v13 or later, runs recorded to Cypress Cloud with Test Replay, and organization access to UI Coverage

Pick the metric that matches the question. If you need both, configure and interpret each separately. Source coverage is not a count of controls tested; UI Coverage is not a measure of source branches executed.

2. Measure application code coverage

Cypress does not instrument application code automatically. You need to instrument the application before the browser runs it, then collect the browser’s coverage counters during Cypress runs. Cypress documents two common instrumentation routes: use nyc as a build step, or add babel-plugin-istanbul to the application’s transpilation pipeline.

Instrument the app and collect counters

  1. Configure the application build to instrument its own source code. The instrumentation adds counters that are exposed in the browser at window.__coverage__. If that property is missing in the application-under-test frame, the app probably was not instrumented or the test is inspecting the wrong frame.
  2. Install and configure @cypress/code-coverage so Cypress can collect coverage from the application and write the raw data.
  3. Run Cypress against the instrumented build, then generate a report from the collected data.

A common Cypress configuration shape is shown below. Keep your existing configuration and adapt its support-file and event setup to your project. The plugin’s setup task must be registered in setupNodeEvents, and the updated config must be returned.

// cypress.config.js
const { defineConfig } = require('cypress');
const codeCoverageTask = require('@cypress/code-coverage/task');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      codeCoverageTask(on, config);
      return config;
    },
  },
});
// cypress/support/e2e.js
import '@cypress/code-coverage/support';

Use the equivalent support entry point for component tests if you collect coverage there. The package’s documented setup and your Cypress version’s configuration format are the source of truth for exact integration details.

Generate useful reports

The plugin stores raw coverage data in .nyc_output. Generate a concise summary or a report that identifies uncovered lines:

npx nyc report --reporter=text-summary
npx nyc report --reporter=text
npx nyc report --reporter=lcov

Use the text report while iterating locally. An LCOV report can be consumed by tools that accept that format. Inspect the statement, branch, function, and line columns, then open the uncovered locations in the source. An aggregate percentage cannot tell you whether a missed path is important.

Set a threshold only after reviewing the denominator

nyc report --check-coverage can fail a command when configured thresholds are not met. For example, a project’s nyc configuration can set line, statement, function, and branch thresholds; the plugin README shows 80% lines as an example. That is a configuration example, not a universal quality target.

npx nyc report --check-coverage

Choose thresholds based on the risk and behavior you need covered. A line executing does not establish that the test asserted the right result. A high total can also conceal an important missed branch in a critical flow.

Use gaps to plan the next test

  1. Locate an uncovered statement or branch in the report.
  2. Decide whether a realistic user flow should reach it and what observable behavior should be asserted.
  3. Add an end-to-end test when the behavior belongs to a user journey; use a unit test when the logic or edge case is difficult or inappropriate to reach through the UI.
  4. Run the relevant suites again and review the changed report rather than optimizing only the headline percentage.

Coverage from unit and end-to-end runs can be combined. If runs happen on parallel machines, collect their partial outputs and merge them with nyc merge before reporting. Ensure each run’s coverage data is available to the merge step.

3. Measure exercised UI with Cypress UI Coverage

UI Coverage is a Cypress Cloud feature based on recorded Test Replay data. Current setup documentation requires Cypress v13 or later, a project recording runs to Cypress Cloud, Test Replay enabled, and UI Coverage enabled for the organization. It is not included in standard Cloud plans. When available, it does not require adding a coverage plugin, changing tests, or instrumenting application source code.

  1. Meet the version and organization requirements, and configure the project to record runs to Cypress Cloud with Test Replay.
  2. Run the end-to-end or component tests and let the run finish recording.
  3. Open the run’s UI Coverage tab.
  4. Review the overall share of interactive elements tested, scores by view, untested elements, and pages reachable by links that tests never visited.
  5. Use the uncovered items to decide which user interactions or views need a meaningful test.

Optional UI Coverage configuration can refine how views and interactions are counted. Use it when the default grouping does not match your application’s views; record the configuration decisions so comparisons between runs remain useful.

Enforce UI Coverage in CI

A low UI Coverage score does not fail the Cypress run automatically. If you want a gate, use the Cypress Results API from your CI workflow to read the report and compare it with a threshold your team chooses. You can set different requirements by view, such as a stricter threshold for checkout than for a marketing page. Make the gate’s denominator and exception policy clear, and avoid treating every element as equally risky.

4. Interpret both reports without gaming the score

  • Code report: inspect branches and missed logic, not only lines. Consider whether the uncovered code is reachable, important, and better tested at unit or end-to-end level.
  • UI report: inspect the actual untested controls and views. A clicked button counts as exercised, but the report alone does not establish that its effect was asserted correctly.
  • Thresholds: set project-specific targets after understanding what is counted. A percentage copied from an example is not evidence of an appropriate target for your application.
  • Trend changes: compare like with like. Changes to instrumentation, view grouping, or which suites contribute data can change a score even when test quality did not change.

5. Troubleshoot common problems

Symptom Likely cause What to do
No coverage report or empty totals The application is not instrumented, the support module is not loaded, or the Node task is not registered. Check the build instrumentation, support import, and setupNodeEvents registration. Confirm that the updated Cypress config is returned.
window.__coverage__ is undefined The served application bundle lacks instrumentation, or the check is running in the wrong browser frame. Inspect the app-under-test frame and verify that the test server serves the instrumented build.
Coverage files exist but report generation has no useful data Raw files may be absent, stale, or from a different build/source map context. Run the tests against the intended instrumented build, confirm fresh files are collected under .nyc_output, and generate the report from that output.
Third-party code is missing from the report The documented instrumentation approach covers application code, not dependencies under node_modules. Interpret the report as application-source coverage; do not assume dependency code is included.
Coverage differs across parallel CI workers Each worker has only its own partial coverage data. Gather the partial outputs and merge them with nyc merge before generating the combined report.
UI Coverage tab or report is unavailable A prerequisite is missing: Cypress version, Cloud recording, Test Replay, or organization enablement. Verify Cypress v13 or later, recorded runs with Test Replay, and that UI Coverage is enabled for the organization.
UI Coverage score does not fail CI Reports are generated after recording, but a falling score does not fail the run by itself. Read the report through the Results API in CI and apply your own view-specific threshold.
Score falls after configuration changes The counted views, interactions, or contributing runs may have changed. Review the configuration and data included in each comparison before concluding that tests regressed.

6. Performance, reliability, and cost considerations

Coverage instrumentation adds counters to application code and produces data that must be collected and reported. Keep the instrumented build aligned with the source version being tested, and generate reports from the data for that same run. For parallel suites, plan an explicit artifact collection and merge step so the final report includes all workers.

UI Coverage reports depend on Cypress Cloud recording and Test Replay availability and organization access. Reports appear after recording finishes, so a CI gate needs a follow-up API step rather than expecting the Cypress test process itself to reject a low score. Check current Cypress documentation for plan and feature availability as it can change.

Coverage gives evidence about execution or exercised UI, not proof of correctness. Spend review time on high-risk behaviors, meaningful assertions, and edge cases. For budgeting, account for your existing Cypress Cloud access requirements and CI/reporting work; do not treat a coverage percentage as a substitute for deciding which tests provide value.

7. Capture screenshots for visual evidence

Coverage answers whether code ran or controls were exercised. When a test failure needs a visual artifact for triage, a screenshot can show the rendered state at that point. For reproducible comparisons, keep the route, viewport, data, and browser state consistent. A screenshot is supporting evidence; it does not replace assertions about behavior or either coverage report.

Or skip the browser setup

If you need a screenshot artifact without setting up a browser capture flow, ScreenshotNeo provides a website screenshot API and MCP server. A GET request with a URL returns PNG, JPEG, WebP, or PDF. The example below saves a screenshot response; it does not measure Cypress test coverage.

See the ScreenshotNeo API documentation for request options.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(async (fs) => {
  await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
});

ScreenshotNeo accepts cookie or consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and responses include X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. You can use 1,000 screenshots per month free without a card; paid plans start at $5 for 3,000. See ScreenshotNeo and the API docs, then sign up for 1,000 free screenshots a month with no card.

Frequently asked questions

Does Cypress measure UI coverage automatically?

No. Source coverage needs application instrumentation and collection setup. UI Coverage is a separate Cypress Cloud feature with its own prerequisites.

Is code coverage the same as UI Coverage?

No. Code coverage counts executed source constructs; UI Coverage reports exercised interactive elements and views.

Does a high coverage score prove my tests are good?

No. A score does not show whether assertions meaningfully verify behavior. Review the tests and the uncovered or exercised items.

Should every project target 80%?

No. That value appears as an example in plugin documentation. Set targets to fit your application’s risks and testing strategy.

Can I combine unit and end-to-end code coverage?

Yes. Cypress documentation describes combining coverage from both and merging partial reports from parallel machines with nyc merge.