{
  "doc": {
    "id": "ui-coverage/core-concepts/interactivity",
    "title": "How UI Coverage detects interactive elements",
    "description": "Understand what drives your UI Coverage score: the interactive elements Cypress tracks, the commands that mark them as tested, and the links to unvisited pages it flags, plus the overrides that make each one match your app.",
    "section": "ui-coverage",
    "source_path": "/llm/markdown/ui-coverage/core-concepts/interactivity.md",
    "version": "29f95bf8bb06f320986f3749f5bf09a35a409eab",
    "updated_at": "2026-09-04T10:49:54.630Z",
    "headings": [
      {
        "id": "ui-coverage/core-concepts/interactivity#how-ui-coverage-measures-interactivity",
        "text": "How UI Coverage measures interactivity",
        "level": 1
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#interactive-elements",
        "text": "Interactive Elements",
        "level": 2
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#visibility",
        "text": "Visibility",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#overriding-interactive-elements-with-data-cy-ui-interactive",
        "text": "Overriding interactive elements with data-cy-ui-interactive",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#how-elements-get-marked-tested",
        "text": "How elements get marked tested",
        "level": 2
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#customizing-interaction-commands",
        "text": "Customizing interaction commands",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#untested-links",
        "text": "Untested Links",
        "level": 2
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#which-hrefs-count-as-links",
        "text": "Which hrefs count as links",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#1-counted-as-untested-links-and-interactive-elements",
        "text": "1. Counted as untested links and interactive elements",
        "level": 4
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#2-counted-only-as-interactive-elements",
        "text": "2. Counted only as interactive elements",
        "level": 4
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#3-not-counted-in-ui-coverage",
        "text": "3. Not counted in UI Coverage",
        "level": 4
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#exclude-link-destinations-from-your-score",
        "text": "Exclude link destinations from your score",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#untested-link-details",
        "text": "Untested link details",
        "level": 3
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#referrers",
        "text": "Referrers",
        "level": 4
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#urls",
        "text": "URLs",
        "level": 4
      },
      {
        "id": "ui-coverage/core-concepts/interactivity#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "content": {
    "type": "root",
    "children": [
      {
        "type": "heading",
        "depth": 1,
        "children": [
          {
            "type": "text",
            "value": "How UI Coverage measures interactivity"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Your UI Coverage score is built from the interactive parts of your app: the buttons, inputs, links, and controls a user can actually touch. To read your report with confidence, it helps to know the three rules that shape it:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": true,
        "start": 1,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "An element is counted when it is interactive and has been visible in the run, which forms the denominator of your score."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "An interactive element is marked as tested when a recognized Cypress command targets it, which forms the numerator. Links are also marked tested when a test visits their destination."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "A link is flagged as untested when its destination is never visited during your run."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In short, your score is the share of interactive elements your tests exercise: tested ÷ total. Links share this total: each distinct destination counts as one unit. See "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/faq.md#How-is-the-UI-Coverage-score-calculated",
            "children": [
              {
                "type": "text",
                "value": "how the score is calculated"
              }
            ]
          },
          {
            "type": "text",
            "value": " for the details."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This page covers all three, along with the configuration and markup overrides you can use to make each one match your application. All three are computed from "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/cloud/features/test-replay.md",
            "children": [
              {
                "type": "text",
                "value": "Test Replay"
              }
            ]
          },
          {
            "type": "text",
            "value": " data, so they work automatically with no setup or instrumentation. Reach for the overrides only when the defaults don't fit."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "UI Coverage turns your runs into a visual map of the interactive elements your tests exercise and the ones they miss, with no code changes or instrumentation. "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://www.cypress.io/ui-coverage?utm_medium=premium-solution-tip&utm_source=docs.cypress.io&utm_content=Schedule%20a%20demo",
            "children": [
              {
                "type": "text",
                "value": "Schedule a demo"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "How UI Coverage turns a recorded run into a coverage score."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Interactive Elements"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "UI Coverage follows the WHATWG definition of "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://html.spec.whatwg.org/dev/dom.html#interactive-content",
            "children": [
              {
                "type": "text",
                "value": "interactive content"
              }
            ]
          },
          {
            "type": "text",
            "value": ", extended with a few Cypress-specific rules. An element is counted as interactive when it falls into any of these categories:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Natively interactive elements: elements the browser already treats as interactive, such as `<a>` links, `<button>`, form controls like `<input>`, and `<select>`, and any element that is `contenteditable`. A `<label>` counts on its own and is "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/core-concepts/element-grouping.md#Automatic-grouping",
                    "children": [
                      {
                        "type": "text",
                        "value": "grouped with the control it identifies"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Most elements with an interactive ARIA role: an explicit `role` such as `button`, or `checkbox`."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Tab-navigable elements: any element made keyboard-focusable with a `tabindex` of `0` or greater. Sometimes required on certain interactive roles."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An element with a widget role is skipped when it contains another interactive element, so a `role=\"button\"` wrapper around a real `<button>` is counted once rather than twice."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Contact links are the one deliberate exception: anchors whose `href` uses `tel:`, `mailto:`, or `sms:` are not treated as interactive."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Because detection is based on semantics rather than event handlers, a real `<button>` is recognized automatically, while a `<div>` wired up with a click handler is not, until you give it interactive semantics or use the `data-cy-ui-interactive` attribute, described below."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Visibility"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An interactive element only counts toward your score if it was visible in at least one snapshot during the run."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "A control that only ever sits inside a collapsed menu your tests never open is not counted at all. It neither raises nor lowers your score. This is why your total element count can be lower than the number of controls you know exist in your markup. UI Coverage will flag the trigger element as untested, as an unopened door."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "UI Coverage finds each interactive element in a snapshot and tracks whether your tests exercise it."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Overriding interactive elements with `data-cy-ui-interactive`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "When the automatic rules don't match your app, the `data-cy-ui-interactive` attribute lets you override them directly in your markup. It accepts four values, matched case-insensitively:"
          }
        ]
      },
      {
        "type": "table",
        "align": [
          null,
          null
        ],
        "children": [
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Value"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Effect"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`include`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Treats the element as interactive even when it wouldn't be otherwise."
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`exclude`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Removes the element from UI Coverage, even when it's normally interactive."
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`exclude-all`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Removes the element and every element nested inside it."
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`reset`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Re-enables tracking for a subtree nested inside an element marked `exclude-all`."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Only the closest `data-cy-ui-interactive` ancestor applies."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Include a custom control the browser doesn't recognize:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "<!-- A custom drag handle built from a div -->\n<div role=\"presentation\" data-cy-ui-interactive=\"include\">Reorder</div>"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Exclude a whole section while keeping one control inside it tracked:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "<section data-cy-ui-interactive=\"exclude-all\">\n  <!-- Nothing in this experimental panel is tracked... -->\n  <button>Beta action</button>\n  <div data-cy-ui-interactive=\"reset\">\n    <!-- ...except controls inside here -->\n    <button>Save feedback</button>\n  </div>\n</section>"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Excluding an element removes it from UI Coverage entirely: it no longer counts as an interactive element, and if it's a link, it also stops appearing as an untested link, so neither affects your score."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In most cases we recommend not using `include`, and instead updating the HTML to be something the browser already treats as interactive, such as a real `<button>` in place of a clickable `<div>`. That produces better overall behavior, including for accessibility, since the same semantics that let assistive technology operate a control are what UI Coverage reads. `data-cy-ui-interactive` is a fallback for situations where that isn't possible."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To exclude elements without editing your application's markup, "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/elementfilters.md",
            "children": [
              {
                "type": "text",
                "value": "`elementFilters`"
              }
            ]
          },
          {
            "type": "text",
            "value": " does the same job from Cypress Cloud configuration. Prefer it when the team that manages configuration isn't the team that owns the markup:"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "App Quality Config"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "{\n  \"elementFilters\": [\n    {\n      \"selector\": \".intercom-launcher, .intercom-launcher *\",\n      \"include\": false,\n      \"comment\": \"Third-party chat widget, not owned by our tests\"\n    }\n  ]\n}"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "How elements get marked tested"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An interactive element is marked as tested when a recognized Cypress command targets it. UI Coverage recognizes this built-in set of commands:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`blur`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`check`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`clear`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`click`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`dblclick`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`focus`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`rightclick`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`scrollIntoView`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`scrollTo`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`select`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`selectFile`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`submit`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`trigger`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`type`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`uncheck`"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "`scrollIntoView` is a special case: it credits the element's nearest scrollable ancestor, not the element you called it on. That container must have a non-negative `tabindex` or be otherwise interactive in order to appear in the report."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Using at least one of them on every interactive element is what drives your coverage score, so a test flow naturally marks the elements it exercises as tested:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// Each command marks its target element as tested\ncy.get('[data-cy=\"signup-email\"]').type('sarah@example.com')\ncy.get('[data-cy=\"signup-plan-pro\"]').check()\ncy.get('[data-cy=\"signup-submit\"]').click()"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An element moves to the Tested list once a recognized command interacts with it, with the interaction count shown per element."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "A command only counts when it logs a "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/core-concepts/element-identification.md#Snapshots",
            "children": [
              {
                "type": "text",
                "value": "snapshot"
              }
            ]
          },
          {
            "type": "text",
            "value": " that highlights the element it acts on, which is how UI Coverage knows which element to credit. Built-in commands do this automatically; a custom command must do it explicitly (see "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/additionalinteractioncommands.md#Requirements-for-a-custom-command",
            "children": [
              {
                "type": "text",
                "value": "Requirements for a custom command"
              }
            ]
          },
          {
            "type": "text",
            "value": ")."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Customizing interaction commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Two configuration options let you change which commands count:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/additionalinteractioncommands.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`additionalInteractionCommands`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " extends the built-in set everywhere, so custom commands or ones from third-party libraries (such as "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "https://github.com/dmtrKovalenko/cypress-real-events",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cypress-real-events`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "' `realClick`) count as coverage on any element."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/allowedinteractioncommands.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`allowedInteractionCommands`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " works per element: for elements matching a selector, only the commands you list count. This lets you both require a meaningful interaction (a chart that must be hovered, not just clicked) and credit commands the defaults ignore (an `assert` on a read-only badge)."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "For example, to require a hover rather than a plain click on a chart before it counts as tested:"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "App Quality Config"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "{\n  \"uiCoverage\": {\n    \"allowedInteractionCommands\": [\n      {\n        \"selector\": \"[data-cy='revenue-chart']\",\n        \"commands\": [\"trigger\"],\n        \"comment\": \"Only a hover (dispatched via trigger) proves the tooltip works\"\n      }\n    ]\n  }\n}"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "See "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/overview.md#Which-option-do-I-need",
            "children": [
              {
                "type": "text",
                "value": "Which option do I need?"
              }
            ]
          },
          {
            "type": "text",
            "value": " for a side-by-side comparison."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Untested Links"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "UI Coverage tracks links (`<a>` elements) whose destination URLs are never visited during testing. A link to a page no test navigates to is a coverage gap even when no "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/core-concepts/views.md",
            "children": [
              {
                "type": "text",
                "value": "view"
              }
            ]
          },
          {
            "type": "text",
            "value": " exists for that page, so untested links surface the flows your suite is missing, not just the elements it skipped."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An untested link highlighted in the report: its destination was never visited during the run."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Which hrefs count as links"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The `href` value determines how UI Coverage treats each anchor. At a glance:"
          }
        ]
      },
      {
        "type": "table",
        "align": [
          null,
          null,
          null
        ],
        "children": [
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`href` example"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Interactive element?"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Can be an untested link?"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`/settings`, `#/settings`, `https://external.com`, `blob:`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Yes"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Yes"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`#pricing` (page fragment)"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Yes"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "No"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`#` or `javascript:`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Yes"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "No"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Empty or missing `href`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Yes"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "No"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`tel:`, `mailto:`, `sms:`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "No"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "No"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The three cases in detail:"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "1. Counted as untested links and interactive elements"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An `href` that resolves to a URL is treated as a link to a destination, and the anchor is also an ordinary interactive element. This covers:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "URLs: relative paths like `/settings`, hash routes like `#/settings`, and absolute URLs, including links to entirely external websites."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Other schemes: any other scheme, such as `ftp:`, `file:`, `data:`, `blob:`, or a custom app scheme, treated the same as an `http(s)` URL."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The anchor counts as tested once a test either interacts with it or visits its destination. If the destination is never visited, the link appears as an untested link and counts against your score, unless a "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/viewfilters.md",
            "children": [
              {
                "type": "text",
                "value": "`viewFilters`"
              }
            ]
          },
          {
            "type": "text",
            "value": " rule excludes the destination URL."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "2. Counted only as interactive elements"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "These anchors are ordinary interactive elements that must be interacted with to count as tested. They point to no separate destination, so they never appear as untested links:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Document fragments: an `href` pointing to a fragment on the current page, such as `#pricing`."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`#` and `javascript:` values: anchors with `href=\"#\"` or a `javascript:` value."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Empty or missing `href`: an anchor with no usable `href` value."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "3. Not counted in UI Coverage"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`tel:`, `mailto:`, and `sms:` values: contact links are not considered interactive elements. They don't appear in reports and don't count toward your coverage score. To track one anyway, add "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Overriding-interactive-elements-with-data-cy-ui-interactive",
                    "children": [
                      {
                        "type": "text",
                        "value": "`data-cy-ui-interactive=\"include\"`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " to the anchor."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Exclude link destinations from your score"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To keep destinations you'll never test, such as external sites, your marketing pages, or a status page, from counting against your score, exclude them by URL:"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "App Quality Config"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "{\n  \"viewFilters\": [\n    {\n      \"pattern\": \"https://status.example.com/*\",\n      \"include\": false,\n      \"comment\": \"External status page, linked from the footer but not owned by our team\"\n    }\n  ]\n}"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Excluding a destination with `viewFilters` also removes the links that point to it, so they stop appearing as untested links. See "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/guides/ignore-views-and-links.md",
            "children": [
              {
                "type": "text",
                "value": "Ignore views and links"
              }
            ]
          },
          {
            "type": "text",
            "value": " for the full workflow. To exclude a single link from your markup instead, add "
          },
          {
            "type": "link",
            "title": null,
            "url": "#Overriding-interactive-elements-with-data-cy-ui-interactive",
            "children": [
              {
                "type": "text",
                "value": "`data-cy-ui-interactive=\"exclude\"`"
              }
            ]
          },
          {
            "type": "text",
            "value": " to the anchor."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Untested link details"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Expanding an untested link in the report reveals where it came from and which destinations it covers, so you can decide whether to test it or exclude it. That detail is organized into two tabs, Referrers and URLs."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Referrers"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The Referrers tab lists the views that contain a link to the untested destination, so you can trace it back to the pages that link to it and the flows that lead there."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "URLs"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The URLs tab groups similar destinations for dynamic routing. For instance, links to `/users/1`, `/users/2`, and `/users/3` are grouped as `/users/*`. Only path segments that are numbers or UUID-like IDs are treated as dynamic, so `/users/alice` and `/users/bob` stay separate. The grouping shows the scope and variations of a dynamic route, and the pattern it surfaces is the one to use when writing "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/views.md",
            "children": [
              {
                "type": "text",
                "value": "`views`"
              }
            ]
          },
          {
            "type": "text",
            "value": " and "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/ui-coverage/configuration/viewfilters.md",
            "children": [
              {
                "type": "text",
                "value": "`viewFilters`"
              }
            ]
          },
          {
            "type": "text",
            "value": " rules."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "See also"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/core-concepts/element-identification.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "Element Identification"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": how UI Coverage gives each interactive element a stable identity across snapshots."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/additionalinteractioncommands.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`additionalInteractionCommands`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": count custom or plugin commands as interactions everywhere."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/allowedinteractioncommands.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`allowedInteractionCommands`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": control which commands count for specific elements."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/elementfilters.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`elementFilters`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": remove elements from reports and scores using configuration."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/configuration/viewfilters.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`viewFilters`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": exclude views and link destinations from reports."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/ui-coverage/faq.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "UI Coverage FAQ"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": ": common questions about interactive elements, interaction commands, and links."
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  "token_estimate": 2567
}