{
  "doc": {
    "id": "ui-coverage/guides/identify-coverage-gaps",
    "title": "Identify coverage gaps with UI Coverage",
    "description": "A step-by-step workflow to find the untested buttons, forms, links, and pages in your app using Cypress UI Coverage reports, with no code instrumentation required.",
    "section": "ui-coverage",
    "source_path": "/llm/markdown/ui-coverage/guides/identify-coverage-gaps.md",
    "version": "fbc9225067c51c52ee13224e3b702cf8a025ec12",
    "updated_at": "2026-08-14T12:36:26.878Z",
    "headings": [
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#identify-coverage-gaps",
        "text": "Identify coverage gaps",
        "level": 1
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-1-record-a-run-to-cypress-cloud",
        "text": "Step 1: Record a run to Cypress Cloud",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#example-visit-every-sitemap-url-in-a-single-test",
        "text": "Example: Visit every sitemap URL in a single test",
        "level": 3
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#example-visit-each-url-in-separate-tests",
        "text": "Example: Visit each URL in separate tests",
        "level": 3
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-2-read-your-overall-coverage-score",
        "text": "Step 2: Read your overall coverage score",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-3-start-with-your-lowest-scoring-views",
        "text": "Step 3: Start with your lowest-scoring views",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-4-drill-into-a-view-to-see-untested-elements",
        "text": "Step 4: Drill into a view to see untested elements",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-5-find-the-pages-your-tests-never-visit",
        "text": "Step 5: Find the pages your tests never visit",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-6-sharpen-the-signal-with-configuration",
        "text": "Step 6: Sharpen the signal with configuration",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#step-7-turn-gaps-into-tests",
        "text": "Step 7: Turn gaps into tests",
        "level": 2
      },
      {
        "id": "ui-coverage/guides/identify-coverage-gaps#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-1-record-a-run-to-cypress-cloud",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 1: Record a run to Cypress Cloud",
      "heading_level": 2,
      "content_markdown": "## Step 1: Record a run to Cypress Cloud\n\nUI Coverage needs a recorded run to analyze. Any run recorded to [Cypress Cloud](https://on.cypress.io/cloud) with Test Replay produces a report automatically, with no configuration or code changes. If you already record your tests, you're ready; open the run and skip to [Step 2](#Step-2-Read-your-overall-coverage-score).\n\nIf your project has few or no Cypress tests yet, you can still get a first report. Drive UI Coverage from your sitemap or a list of URLs: visiting each page captures the interactive elements on it, giving you a baseline for the pages your suite doesn't reach yet.\n\nIf your project lacks existing Cypress tests, a common approach is to drive UI Coverage from a sitemap or an array of target URLs. Visiting each URL performs a light interaction that UI Coverage records, giving you a first coverage report for the pages your suite doesn't reach yet.\n\n### Example: Visit every sitemap URL in a single test\n\nThe example below fetches your `sitemap.xml` at runtime and visits every URL it finds in a single test. This is the quickest way to get started, with no list to maintain:\n\n```\ndescribe('UI Coverage Scan', () => {  it('Checks UI Coverage against the URLs in sitemap.xml', () => {    cy.request('https://<YOUR_WEBSITE>/sitemap.xml').then((response) => {      const parser = new DOMParser()      const xml = parser.parseFromString(response.body, 'application/xml')      const urls = [...xml.querySelectorAll('loc')].map(        (loc) => loc.textContent      )      urls.forEach((url) => {        // UI Coverage captures the interactive elements on each page you visit        cy.visit(url)      })    })  })})\n```\n\n### Example: Visit each URL in separate tests\n\nThe single-test scan above is quick to set up, but putting each URL in a separate test is often the better choice:\n\n*   **Tracking**: each page becomes a distinct test in Cypress Cloud, so a page that fails to load shows up as its own failing test with its own [Test Replay](/llm/markdown/cloud/features/test-replay.md), instead of collapsing the whole scan into one pass/fail.\n*   **Performance**: a single test that visits every page keeps accumulating DOM snapshots and command history as it runs, which slows it down and makes it harder to debug. Separate tests each start clean, so they stay fast.\n*   **Isolation**: [test isolation](/llm/markdown/app/core-concepts/writing-and-organizing-tests.md#Test-Isolation) resets the browser state between pages, so one page's cookies or storage can't leak into the next.\n\nBecause Cypress defines tests when the spec loads (before any command runs), the list of URLs has to exist before the run starts rather than come from a sitemap fetched at runtime. You can hard-code it in the spec, import it from a committed file, or generate it in your Cypress config and pass it to the spec. This documentation's own test suite takes the last approach: it builds the list of pages in `cypress.config` and loops over them to create one test per page.\n\n```\nconst urls = ['/', '/about-us', '/pricing', '/contact', '/request-trial']describe('UI Coverage Scan', () => {  urls.forEach((url) => {    it(`Visits ${url}`, () => {      // UI Coverage captures the interactive elements on this page      cy.visit(url)    })  })})\n```\n\nThe result is a first-load coverage report for every URL in your site. Any Cypress tests you write for specific workflows automatically expand the coverage area to include the states and variations those workflows reach.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-1-record-a-run-to-cypress-cloud"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 712
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#example-visit-every-sitemap-url-in-a-single-test",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Example: Visit every sitemap URL in a single test",
      "heading_level": 3,
      "content_markdown": "### Example: Visit every sitemap URL in a single test\n\nThe example below fetches your `sitemap.xml` at runtime and visits every URL it finds in a single test. This is the quickest way to get started, with no list to maintain:\n\n```\ndescribe('UI Coverage Scan', () => {  it('Checks UI Coverage against the URLs in sitemap.xml', () => {    cy.request('https://<YOUR_WEBSITE>/sitemap.xml').then((response) => {      const parser = new DOMParser()      const xml = parser.parseFromString(response.body, 'application/xml')      const urls = [...xml.querySelectorAll('loc')].map(        (loc) => loc.textContent      )      urls.forEach((url) => {        // UI Coverage captures the interactive elements on each page you visit        cy.visit(url)      })    })  })})\n```\n",
      "section": "ui-coverage",
      "anchors": [
        "example-visit-every-sitemap-url-in-a-single-test"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 133
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#example-visit-each-url-in-separate-tests",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Example: Visit each URL in separate tests",
      "heading_level": 3,
      "content_markdown": "### Example: Visit each URL in separate tests\n\nThe single-test scan above is quick to set up, but putting each URL in a separate test is often the better choice:\n\n*   **Tracking**: each page becomes a distinct test in Cypress Cloud, so a page that fails to load shows up as its own failing test with its own [Test Replay](/llm/markdown/cloud/features/test-replay.md), instead of collapsing the whole scan into one pass/fail.\n*   **Performance**: a single test that visits every page keeps accumulating DOM snapshots and command history as it runs, which slows it down and makes it harder to debug. Separate tests each start clean, so they stay fast.\n*   **Isolation**: [test isolation](/llm/markdown/app/core-concepts/writing-and-organizing-tests.md#Test-Isolation) resets the browser state between pages, so one page's cookies or storage can't leak into the next.\n\nBecause Cypress defines tests when the spec loads (before any command runs), the list of URLs has to exist before the run starts rather than come from a sitemap fetched at runtime. You can hard-code it in the spec, import it from a committed file, or generate it in your Cypress config and pass it to the spec. This documentation's own test suite takes the last approach: it builds the list of pages in `cypress.config` and loops over them to create one test per page.\n\n```\nconst urls = ['/', '/about-us', '/pricing', '/contact', '/request-trial']describe('UI Coverage Scan', () => {  urls.forEach((url) => {    it(`Visits ${url}`, () => {      // UI Coverage captures the interactive elements on this page      cy.visit(url)    })  })})\n```\n\nThe result is a first-load coverage report for every URL in your site. Any Cypress tests you write for specific workflows automatically expand the coverage area to include the states and variations those workflows reach.\n",
      "section": "ui-coverage",
      "anchors": [
        "example-visit-each-url-in-separate-tests"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 379
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-2-read-your-overall-coverage-score",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 2: Read your overall coverage score",
      "heading_level": 2,
      "content_markdown": "## Step 2: Read your overall coverage score\n\nOnce your run has recorded, start with the **overall coverage score**. It's the percentage of your application's interactive elements that your tests exercised, so it's the single number that tells you how much of your UI is covered and gives you a baseline to improve against. The score appears on the runs list and on each individual run.\n\n[Grouped elements](/llm/markdown/ui-coverage/core-concepts/element-grouping.md) count once toward the score, so a list of 50 identical \"Remove\" buttons is a single unit, and one test that removes one item covers all of them. This keeps repeated elements from inflating or sinking your score. For the exact calculation, see [how the score is calculated](/llm/markdown/ui-coverage/faq.md#How-is-the-UI-Coverage-score-calculated).\n\nA low score isn't automatically a problem, and a high one isn't automatically safe. The goal isn't a perfect number, but to make sure the score reflects _real_ coverage of the flows that matter. The next steps show you where the untested elements behind the number actually are.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-2-read-your-overall-coverage-score"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 219
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-3-start-with-your-lowest-scoring-views",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 3: Start with your lowest-scoring views",
      "heading_level": 2,
      "content_markdown": "## Step 3: Start with your lowest-scoring views\n\nOpen the run's **UI Coverage** tab. A [view](/llm/markdown/ui-coverage/core-concepts/views.md) is a distinct page or state of your application. For end-to-end tests, URLs are grouped into patterns; for component tests, each spec file is a view. The **Views** section lists every view with:\n\n*   **Snapshots**: how many DOM snapshots were captured for the view.\n*   **Tested elements**: how many of the view's interactive elements were tested, out of the total found.\n*   **Coverage %**: the percentage of the view's interactive elements that were tested.\n\nThe table is sorted by **Views** by default. Sort by **Coverage %** to bring your lowest-covered views to the top, then weigh each one against how important it is to your users. A checkout or signup view at 40% is a more urgent gap than a settings page at the same score. This is where you decide which views deserve the next test.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-3-start-with-your-lowest-scoring-views"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 204
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-4-drill-into-a-view-to-see-untested-elements",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 4: Drill into a view to see untested elements",
      "heading_level": 2,
      "content_markdown": "## Step 4: Drill into a view to see untested elements\n\nSelecting a view breaks it down into the specific elements your tests did and didn't reach, so you can see each gap in context. A view drilldown includes:\n\n*   **Untested elements**: interactive elements that no recognized Cypress command targeted during the run.\n*   **Tested elements**: interactive elements a test exercised, with a count of how many times.\n*   **Snapshot**: a full-page, inspectable snapshot of the view as it appeared during the run. Tested elements are highlighted green, untested elements red.\n*   **Snapshot navigation**: previous and next controls, labeled `Snapshot {n} of {total}`, to move between the view's different states during the run.\n*   **Snapshot coverage score**: the coverage score for the specific snapshot you're viewing.\n*   **Test Replay**: a menu of the tests that reached the snapshot, each opening [Test Replay](/llm/markdown/cloud/features/test-replay.md) at that point, where you can see the element within the full test run instead of a single captured state.\n\nBecause the snapshot is a real, inspectable DOM, you can open your browser's developer tools on it to understand why an element went untested: it may be hidden behind a menu, rendered only after login, or shown in a state your tests never reach. That context tells you what setup a new test needs.\n\nThe **Untested elements** section at the top level of the tab collects untested elements across _all_ views, so you can spot a single control that's missed everywhere, such as a \"Download\" button repeated on every report page, and cover it in one place.\n\nExpanding an untested element shows its selector and, when needed, switches to an example snapshot. It also lists any views where the element appears without any interaction, so you can trace the same element back to every page it shows up on.\n\nFor tested elements, the individual interactions are listed and can be traced through to the tests themselves.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-4-drill-into-a-view-to-see-untested-elements"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 424
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-5-find-the-pages-your-tests-never-visit",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 5: Find the pages your tests never visit",
      "heading_level": 2,
      "content_markdown": "## Step 5: Find the pages your tests never visit\n\nUntested elements only exist for pages your tests actually open. To find the pages your suite never reaches at all, use the **Untested links** section. It lists links (`<a>` elements) whose destination URL was never visited during the run. Each one is a flow your tests are missing, even when no view exists for that page yet.\n\nExpanding an untested link organizes its detail into two tabs:\n\n*   **Referrers**: the tested pages that link to this destination, showing you where in your app the untested page is reachable from.\n*   **URLs**: the concrete destinations behind the link, grouped for dynamic routes, so you can see the full scope of a route your tests haven't reached.\n\nSelecting a referrer opens that view with the untested link highlighted in red, so you can see where in the page the link lives.\n\nUntested links count against your coverage score, which is deliberate: a page you link to but never test is a real gap. But links to destinations you'll never own, such as external sites, a marketing page, or a status page, shouldn't drag your score down. That's what the next step addresses.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-5-find-the-pages-your-tests-never-visit"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 267
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-6-sharpen-the-signal-with-configuration",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 6: Sharpen the signal with configuration",
      "heading_level": 2,
      "content_markdown": "## Step 6: Sharpen the signal with configuration\n\nBefore you invest in writing tests, make sure the gaps you're looking at are _real_ gaps. Two kinds of noise commonly lower a score without pointing to anything worth testing:\n\n*   **Third-party widgets**, such as chat launchers, cookie banners, and ad embeds, are interactive elements you don't own, each counted as untested.\n*   **Untested links to pages you'll never test**, such as external sites, marketing pages, and a status page, count against your score even though testing them isn't your job.\n\nUI Coverage is configured from the **App Quality** tab of your project settings in Cypress Cloud. Configuration is opt-in: add a rule only to remove noise or sharpen the report. After saving, you can regenerate any historical run from its **Properties** tab to see the effect immediately, with no need to re-run your tests.\n\nExclude a third-party widget so it stops counting as an untested element with [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md):\n\nApp Quality Config\n\n```\n{  \"elementFilters\": [    {      \"selector\": \"#intercom-container, #intercom-container *\",      \"include\": false,      \"comment\": \"Third-party support chat, not part of our app, shouldn't count as a gap\"    }  ]}\n```\n\nExclude a destination you'll never test so its untested links stop counting against your score with [`viewFilters`](/llm/markdown/ui-coverage/configuration/viewfilters.md):\n\nApp Quality Config\n\n```\n{  \"viewFilters\": [    {      \"pattern\": \"https://status.example.com/*\",      \"include\": false,      \"comment\": \"External status page linked from the footer, not owned by our team\"    }  ]}\n```\n\nThe [Reduce noise](/llm/markdown/ui-coverage/guides/reduce-noise.md) guide walks through the other common adjustments, such as grouping repeated elements and stabilizing elements with dynamic attributes. With the noise removed, every remaining gap is one worth a decision.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-6-sharpen-the-signal-with-configuration"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 355
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#step-7-turn-gaps-into-tests",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "Step 7: Turn gaps into tests",
      "heading_level": 2,
      "content_markdown": "## Step 7: Turn gaps into tests\n\nYou now have a prioritized, trustworthy list of gaps: the untested elements on your riskiest views and the pages your suite never opens. The last step is to close them.\n\nYou have a few ways to close them, depending on how you like to work:\n\n*   **Prompt an AI agent with [Cypress Cloud MCP](/llm/markdown/cloud/integrations/cloud-mcp.md)**: point your AI coding assistant at the report and have it pull the untested elements for a view, then draft targeted tests in your editor alongside your existing code and custom commands. See [Work with AI agents](/llm/markdown/ui-coverage/work-with-ai-agents.md).\n*   **Generate a test from Cypress Cloud**: select an untested element in the report and click **Generate test code**. Cypress writes a test that navigates to the element and interacts with it, following your existing patterns. See [Generate tests from UI Coverage reports](/llm/markdown/ui-coverage/guides/address-coverage-gaps.md#Generate-tests-from-UI-Coverage-reports).\n*   **Write the test yourself**: copy the element's selector from the report with the **Copy element selector** action, or inspect it in the DOM snapshot with your browser's developer tools, then write a focused test by hand.\n\nWhichever you choose, the [Address coverage gaps](/llm/markdown/ui-coverage/guides/address-coverage-gaps.md) guide covers the details: writing tests for specific elements, adding navigation to reach untested pages, revealing hidden or conditionally rendered elements, and confirming the score improved on your next run.\n",
      "section": "ui-coverage",
      "anchors": [
        "step-7-turn-gaps-into-tests"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 287
    },
    {
      "id": "ui-coverage/guides/identify-coverage-gaps#see-also",
      "doc_id": "ui-coverage/guides/identify-coverage-gaps",
      "heading": "See also",
      "heading_level": 2,
      "content_markdown": "## See also\n\n*   [Address coverage gaps](/llm/markdown/ui-coverage/guides/address-coverage-gaps.md): close the gaps you found by adding and improving tests.\n*   [Reduce test duplication](/llm/markdown/ui-coverage/guides/reduce-test-duplication.md): consolidate over-tested setup to free suite time for the gaps that matter.\n*   [Monitor changes](/llm/markdown/ui-coverage/guides/monitor-changes.md): track your score over time so new gaps don't slip in.\n*   [Block pull requests and set policies](/llm/markdown/ui-coverage/guides/block-pull-requests.md): enforce a coverage threshold in CI with the [Results API](/llm/markdown/ui-coverage/results-api.md).\n*   [UI Coverage FAQ](/llm/markdown/ui-coverage/faq.md): answers to common questions about scores, views, links, and configuration.\n",
      "section": "ui-coverage",
      "anchors": [
        "see-also"
      ],
      "path": "/llm/json/chunked/ui-coverage/guides/identify-coverage-gaps.json",
      "token_estimate": 104
    }
  ]
}