{
  "doc": {
    "id": "ui-coverage/troubleshooting",
    "title": "Troubleshooting: UI Coverage",
    "description": "Fix common Cypress UI Coverage report problems: duplicate elements, mis-grouped elements, tested elements shown as untested, configuration that has no effect, and unexpected coverage scores.",
    "section": "ui-coverage",
    "source_path": "/llm/markdown/ui-coverage/troubleshooting.md",
    "version": "fbc9225067c51c52ee13224e3b702cf8a025ec12",
    "updated_at": "2026-08-14T12:36:26.878Z",
    "headings": [
      {
        "id": "ui-coverage/troubleshooting#troubleshooting-ui-coverage",
        "text": "Troubleshooting UI Coverage",
        "level": 1
      },
      {
        "id": "ui-coverage/troubleshooting#one-element-is-reported-as-many",
        "text": "One element is reported as many",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#distinct-elements-are-reported-as-one",
        "text": "Distinct elements are reported as one",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#similar-elements-arent-grouped-together",
        "text": "Similar elements aren't grouped together",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#unrelated-elements-are-grouped-together",
        "text": "Unrelated elements are grouped together",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#an-element-you-tested-is-shown-as-untested",
        "text": "An element you tested is shown as untested",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#an-element-stops-counting-after-you-add-an-allowedinteractioncommands-rule",
        "text": "An element stops counting after you add an allowedInteractionCommands rule",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#a-configuration-rule-has-no-effect",
        "text": "A configuration rule has no effect",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#the-coverage-score-looks-wrong",
        "text": "The coverage score looks wrong",
        "level": 2
      },
      {
        "id": "ui-coverage/troubleshooting#still-stuck",
        "text": "Still stuck?",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "ui-coverage/troubleshooting#one-element-is-reported-as-many",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "One element is reported as many",
      "heading_level": 2,
      "content_markdown": "## One element is reported as many\n\nA single control appears as several separate untested elements, inflating your element count and lowering your score.\n\n**Cause.** UI Coverage recognizes the same element across [snapshots](/llm/markdown/ui-coverage/core-concepts/element-identification.md#Snapshots) by building an [identity](/llm/markdown/ui-coverage/core-concepts/element-identification.md) from its attributes and position in the DOM. When an identifying attribute changes on every render, such as a framework-generated `id`, a hashed class, or a per-request token, each snapshot looks like a new element and the one control is split apart.\n\nUI Coverage already ignores the most common unstable values, including UUIDs and long hexadecimal hashes on any attribute, digit-only `id`, `name`, `for`, and `aria-*` values, and generated class names like `jss*` and `ng-tns-*`. Add configuration only when your application produces unstable identifiers these built-in filters don't cover.\n\n**Fixes.**\n\n*   **Ignore the dynamic attribute** with [`attributeFilters`](/llm/markdown/ui-coverage/configuration/attributefilters.md) so UI Coverage falls back to a stable attribute. When you filter a dynamic `id`, also filter the attributes that reference it, such as `for`, `aria-labelledby`, and `aria-describedby`, or the element can still be identified by those related values.\n    \n    App Quality Config\n    \n    ```\n    {  \"attributeFilters\": [    {      \"attribute\": \"id|for|aria-labelledby|aria-describedby\",      \"value\": \":r.*\",      \"include\": false,      \"comment\": \"React useId() values like :r0: are regenerated on every render\"    }  ]}\n    ```\n    \n*   **Pin one specific element's identity** with an [`elements`](/llm/markdown/ui-coverage/configuration/elements.md) rule that identifies it by a selector that stays the same across snapshots. This is the right tool for a single control; if many elements share the same generated attribute, filter the attribute instead.\n    \n    App Quality Config\n    \n    ```\n    {  \"uiCoverage\": {    \"elements\": [      {        \"selector\": \"#checkout-form [id^='card-']\",        \"name\": \"Card Number Field\"      }    ]  }}\n    ```\n    \n\nPatterns in `attributeFilters` match the whole value (they're anchored as `^(...)$`, unless your pattern already starts with `^` or ends with `$`), and JSON requires backslashes to be doubled, so a token like `\\d` must be written `\\\\d`. Note that `attribute` is matched **case-insensitively** while `value` is matched **case-sensitively**. See [How rules are applied](/llm/markdown/ui-coverage/configuration/attributefilters.md#How-rules-are-applied).\n",
      "section": "ui-coverage",
      "anchors": [
        "one-element-is-reported-as-many"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 425
    },
    {
      "id": "ui-coverage/troubleshooting#distinct-elements-are-reported-as-one",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "Distinct elements are reported as one",
      "heading_level": 2,
      "content_markdown": "## Distinct elements are reported as one\n\nTwo or more controls that should each be tracked appear as a single element, so testing one marks all of them tested and real gaps are hidden.\n\n**Cause.** The elements resolve to the same identity because their identifying attribute shares a generic value, such as several buttons that all carry `data-test=\"button\"`, or because the attribute that would distinguish them isn't one UI Coverage uses by default.\n\n**Fixes.**\n\n*   **Add distinct identifiers** in your application, giving each control its own `data-cy` or `data-test` value. This is the most durable fix.\n    \n*   **Prefer a distinguishing attribute** your markup already has with [`significantAttributes`](/llm/markdown/ui-coverage/configuration/significantattributes.md). Attributes you list are checked before the defaults, so an attribute whose value differs between the elements gives each a distinct identity. Icon-only controls, for example, are often distinguishable by their `aria-label`.\n    \n    App Quality Config\n    \n    ```\n    {  \"significantAttributes\": [\"aria-label\"]}\n    ```\n    \n    Listing `aria-label` also turns the report into an inventory of your label text, where a vague label that passes automated accessibility checks (`aria-label=\"button\"`) stands out next to a clear one (`aria-label=\"Close dialog\"`).\n",
      "section": "ui-coverage",
      "anchors": [
        "distinct-elements-are-reported-as-one"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 239
    },
    {
      "id": "ui-coverage/troubleshooting#similar-elements-arent-grouped-together",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "Similar elements aren't grouped together",
      "heading_level": 2,
      "content_markdown": "## Similar elements aren't grouped together\n\nRepeated components, such as a row action rendered for every record, appear as many separate untested elements instead of one [group](/llm/markdown/ui-coverage/core-concepts/element-grouping.md), so a single table can dominate your untested list.\n\n**Cause.** Each instance carries a per-instance value in its identifier, such as a database ID in an `id` attribute, so UI Coverage treats the instances as unrelated rather than as one repeated component.\n\n**Fixes.**\n\n*   **Ignore the per-instance value** with [`attributeFilters`](/llm/markdown/ui-coverage/configuration/attributefilters.md) so the otherwise-identical elements collapse into one group on their own.\n    \n    App Quality Config\n    \n    ```\n    {  \"attributeFilters\": [    {      \"attribute\": \"id\",      \"value\": \"delete-user-\\\\d+\",      \"include\": false,      \"comment\": \"Row action ids include the per-user database id\"    }  ]}\n    ```\n    \n*   **Force the grouping** with an [`elementGroups`](/llm/markdown/ui-coverage/configuration/elementgroups.md) rule that matches all the instances by a shared selector and names them, which also makes the report readable.\n    \n    App Quality Config\n    \n    ```\n    {  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[id^='delete-user-']\",        \"name\": \"Delete User Button\"      }    ]  }}\n    ```\n    \n*   **Group in your markup** by adding the [`data-cy-ui-group` attribute](/llm/markdown/ui-coverage/core-concepts/element-grouping.md#Grouping-with-markup-attributes) to the elements, or to a wrapper around them, when the team that owns the markup also owns the grouping decision. Groups defined this way take priority over `elementGroups` configuration.\n",
      "section": "ui-coverage",
      "anchors": [
        "similar-elements-arent-grouped-together"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 267
    },
    {
      "id": "ui-coverage/troubleshooting#unrelated-elements-are-grouped-together",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "Unrelated elements are grouped together",
      "heading_level": 2,
      "content_markdown": "## Unrelated elements are grouped together\n\nControls that do different things are combined into one group, so an interaction with one wrongly marks the others tested.\n\n**Cause.** The elements share an overly generic selector or attribute value, so UI Coverage's automatic grouping or a shared identifying attribute treats them as instances of the same component.\n\n**Fixes.**\n\n*   **Add distinct identifiers** so each control resolves to its own identity, which prevents the shared-attribute grouping in the first place.\n    \n*   **Define the correct split** with [`elementGroups`](/llm/markdown/ui-coverage/configuration/elementgroups.md). A custom group overrides the automatic grouping, so a rule for each action separates controls the heuristics lumped together. List specific rules before broad ones, because the **first** matching rule wins.\n    \n    App Quality Config\n    \n    ```\n    {  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy='quick-view']\",        \"name\": \"Quick View Button\"      },      {        \"selector\": \"[data-cy='add-to-cart']\",        \"name\": \"Add to Cart Button\"      }    ]  }}\n    ```\n",
      "section": "ui-coverage",
      "anchors": [
        "unrelated-elements-are-grouped-together"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 192
    },
    {
      "id": "ui-coverage/troubleshooting#an-element-you-tested-is-shown-as-untested",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "An element you tested is shown as untested",
      "heading_level": 2,
      "content_markdown": "## An element you tested is shown as untested\n\nYour test interacts with an element on every run, but the report still lists it as untested.\n\n**Cause.** UI Coverage marks an element tested only when a recognized [interaction command](/llm/markdown/ui-coverage/core-concepts/interactivity.md#Interaction-Commands) targets it. The built-in set is `blur`, `check`, `clear`, `click`, `dblclick`, `focus`, `rightclick`, `scrollIntoView`, `scrollTo`, `select`, `selectFile`, `submit`, `trigger`, `type`, and `uncheck`. Anything outside that set, including your own custom commands and plugin commands such as `cypress-real-events`' `realClick` and `realHover`, doesn't count until you declare it.\n\n**Cause: you used `scrollIntoView`.** That command credits the element's nearest scrollable ancestor rather than the element you called it on, so it usually marks nothing tested. Use `click`, `focus`, or `trigger` on the element instead.\n\n**Fixes.**\n\n*   **Count a custom or plugin command everywhere** by adding it to [`additionalInteractionCommands`](/llm/markdown/ui-coverage/configuration/additionalinteractioncommands.md).\n    \n    App Quality Config\n    \n    ```\n    {  \"uiCoverage\": {    \"additionalInteractionCommands\": [\"realClick\", \"realHover\"]  }}\n    ```\n    \n*   **Count a command for specific elements**, or **credit an interaction the defaults ignore** such as an `assert` on a read-only badge or total, with [`allowedInteractionCommands`](/llm/markdown/ui-coverage/configuration/allowedinteractioncommands.md).\n    \n    App Quality Config\n    \n    ```\n    {  \"uiCoverage\": {    \"allowedInteractionCommands\": [      {        \"selector\": \"[data-cy='order-total']\",        \"commands\": [\"assert\"],        \"comment\": \"This total is only ever validated, never interacted with\"      }    ]  }}\n    ```\n    \n\nTwo details often explain a command that still doesn't count. First, a custom command only produces coverage if it logs a [snapshot](/llm/markdown/ui-coverage/core-concepts/element-identification.md#Snapshots) that references the element it acts on, so register it with [`prevSubject`](/llm/markdown/api/cypress-api/custom-commands.md#Arguments) and log the subject as `$el` (see [Requirements for a custom command](/llm/markdown/ui-coverage/configuration/additionalinteractioncommands.md#Requirements-for-a-custom-command)). Second, command names you add are matched **case-sensitively**, exactly as you registered them.\n",
      "section": "ui-coverage",
      "anchors": [
        "an-element-you-tested-is-shown-as-untested"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 345
    },
    {
      "id": "ui-coverage/troubleshooting#an-element-stops-counting-after-you-add-an-allowedinteractioncommands-rule",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "An element stops counting after you add an allowedInteractionCommands rule",
      "heading_level": 2,
      "content_markdown": "## An element stops counting after you add an allowedInteractionCommands rule\n\nAn element that was tested is now reported as untested, right after you added an [`allowedInteractionCommands`](/llm/markdown/ui-coverage/configuration/allowedinteractioncommands.md) rule.\n\n**Cause.** Once an element matches a rule, **only** the commands listed in the matching rules count for it, and the default commands no longer apply. If your test exercises the element with a command you didn't list, such as a plain `click`, it's now treated as untested.\n\n**Fix.** Add the command your test actually uses to the rule's `commands` list, or tighten the `selector` so the rule matches only the elements you intended to restrict.\n",
      "section": "ui-coverage",
      "anchors": [
        "an-element-stops-counting-after-you-add-an-allowedinteractioncommands-rule"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 137
    },
    {
      "id": "ui-coverage/troubleshooting#a-configuration-rule-has-no-effect",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "A configuration rule has no effect",
      "heading_level": 2,
      "content_markdown": "## A configuration rule has no effect\n\nYou saved a rule, but the report looks the same.\n\nWork through these causes in order:\n\n*   **The run predates the change.** Reports use the configuration saved when they were processed. [Regenerate](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) the run to apply your current configuration. This is by far the most common cause.\n*   **An earlier rule matched first.** For [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md), [`viewFilters`](/llm/markdown/ui-coverage/configuration/viewfilters.md), [`elementGroups`](/llm/markdown/ui-coverage/configuration/elementgroups.md), and [`attributeFilters`](/llm/markdown/ui-coverage/configuration/attributefilters.md), the **first** matching rule wins, so a broad rule above your specific one prevents it from applying. List specific rules first, and place `include: true` exceptions before the `include: false` rule they should override. ([`elements`](/llm/markdown/ui-coverage/configuration/elements.md) is the exception: when several rules match one element, the **last** one wins.)\n*   **A nested list replaced a root one.** Nesting a shared property such as `elementFilters` or `attributeFilters` under a `uiCoverage` or `accessibility` key **completely replaces** the root-level list for that product; the two are never merged. Repeat any root rules you still want in the nested list. See [Configuration scope](/llm/markdown/ui-coverage/configuration/overview.md#Configuration-scope).\n*   **The property is in the wrong place.** `elementGroups`, `elements`, `additionalInteractionCommands`, and `allowedInteractionCommands` are UI Coverage–only and must live under the `uiCoverage` key, while `views` is set at the root. A misplaced property is rejected by the schema.\n*   **The pattern or selector doesn't match.** `attributeFilters` patterns are anchored to the whole value, so `value: \"user\"` matches only the exact string `user`; use `value: \"user-.*\"` for a prefix. Selectors in `elementFilters`, `elements`, and `elementGroups` must match the interactive element itself, not a wrapper around it, so `footer` matches only the `<footer>`, while `footer *` matches the controls inside it.\n*   **The configuration didn't save.** Cypress Cloud validates against a strict schema and rejects any property it doesn't recognize, including a misspelled name, a value of the wrong type, or a freeform note on a field that doesn't accept one. Keep notes in a [`comment`](/llm/markdown/ui-coverage/configuration/overview.md#Comments), and correct the flagged property so the configuration can save.\n",
      "section": "ui-coverage",
      "anchors": [
        "a-configuration-rule-has-no-effect"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 424
    },
    {
      "id": "ui-coverage/troubleshooting#the-coverage-score-looks-wrong",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "The coverage score looks wrong",
      "heading_level": 2,
      "content_markdown": "## The coverage score looks wrong\n\nYour score is lower than the state of your tests suggests.\n\n**Cause.** The score compares tested interactive elements to the total, and a few sources of untested elements commonly drag it down without pointing to a real gap in your suite:\n\n*   **Third-party widgets.** Chat launchers, cookie banners, and analytics overlays are interactive elements your team doesn't own, and each counts as untested.\n*   **Untested links.** A link to a page no test visits, including your own help center, marketing pages, and entirely external sites, counts against your score as an [untested link](/llm/markdown/ui-coverage/core-concepts/interactivity.md#Untested-Links), even though no view exists for it.\n*   **Duplicated elements.** One control split into many by an unstable attribute (see [One element is reported as many](#One-element-is-reported-as-many)) adds untested elements that are really the same control.\n\n**Fixes.**\n\n*   **Remove third-party elements** with [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md), which drops them from the report and the score.\n    \n    App Quality Config\n    \n    ```\n    {  \"elementFilters\": [    {      \"selector\": \".intercom-launcher\",      \"include\": false,      \"comment\": \"Third-party chat widget, not part of our tested UI\"    }  ]}\n    ```\n    \n*   **Exclude destinations you don't intend to test** with [`viewFilters`](/llm/markdown/ui-coverage/configuration/viewfilters.md), which also removes the links that point to them.\n    \n    App Quality Config\n    \n    ```\n    {  \"viewFilters\": [    {      \"pattern\": \"https://status.my-app.com/*\",      \"include\": false,      \"comment\": \"External status page, linked from the footer but out of scope\"    }  ]}\n    ```\n    \n\nTo keep only your own application's URLs, list `include: true` rules for them followed by a catch-all `{ \"pattern\": \"*\", \"include\": false }`. Because the first matching rule wins, the catch-all must come last. See [Include only your application's URLs](/llm/markdown/ui-coverage/configuration/viewfilters.md#Include-only-your-applications-URLs).\n",
      "section": "ui-coverage",
      "anchors": [
        "the-coverage-score-looks-wrong"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 348
    },
    {
      "id": "ui-coverage/troubleshooting#still-stuck",
      "doc_id": "ui-coverage/troubleshooting",
      "heading": "Still stuck?",
      "heading_level": 2,
      "content_markdown": "## Still stuck?\n\n*   The [UI Coverage FAQ](/llm/markdown/ui-coverage/faq.md) answers focused questions about scores, views, grouping, identification, interaction commands, and profiles.\n*   Each configuration page has its own troubleshooting and \"How rules are applied\" section: [`attributeFilters`](/llm/markdown/ui-coverage/configuration/attributefilters.md), [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md), [`elementGroups`](/llm/markdown/ui-coverage/configuration/elementgroups.md), [`elements`](/llm/markdown/ui-coverage/configuration/elements.md), [`viewFilters`](/llm/markdown/ui-coverage/configuration/viewfilters.md), [`views`](/llm/markdown/ui-coverage/configuration/views.md), and [`profiles`](/llm/markdown/ui-coverage/configuration/profiles.md).\n*   The [Configuration overview](/llm/markdown/ui-coverage/configuration/overview.md) covers where configuration lives, how rules are applied, and how to regenerate reports.\n*   If a report still looks wrong after working through this page, reach out to your Cypress point of contact with a link to the run.\n",
      "section": "ui-coverage",
      "anchors": [
        "still-stuck"
      ],
      "path": "/llm/json/chunked/ui-coverage/troubleshooting.json",
      "token_estimate": 113
    }
  ]
}