{
  "doc": {
    "id": "ui-coverage/configuration/elementgroups",
    "title": "elementGroups: group related elements in UI Coverage",
    "description": "The elementGroups configuration in Cypress UI Coverage groups related elements into a single unit, so testing one element counts as testing them all.",
    "section": "ui-coverage",
    "source_path": "/llm/markdown/ui-coverage/configuration/elementgroups.md",
    "version": "e6c8d867c21227247f14714fb5690c7c018983c5",
    "updated_at": "2026-08-08T12:39:37.868Z",
    "headings": [
      {
        "id": "ui-coverage/configuration/elementgroups#group-related-elements-elementgroups",
        "text": "Group related elements - elementGroups",
        "level": 1
      },
      {
        "id": "ui-coverage/configuration/elementgroups#why-use-elementgroups",
        "text": "Why use elementGroups?",
        "level": 2
      },
      {
        "id": "ui-coverage/configuration/elementgroups#setting-elementgroups",
        "text": "Setting elementGroups",
        "level": 2
      },
      {
        "id": "ui-coverage/configuration/elementgroups#syntax",
        "text": "Syntax",
        "level": 2
      },
      {
        "id": "ui-coverage/configuration/elementgroups#options",
        "text": "Options",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#how-are-elementgroups-rules-applied",
        "text": "How are elementGroups rules applied?",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#validation-rules",
        "text": "Validation rules",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#examples",
        "text": "Examples",
        "level": 2
      },
      {
        "id": "ui-coverage/configuration/elementgroups#consolidate-repeated-elements",
        "text": "Consolidate repeated elements",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#give-a-group-a-readable-name",
        "text": "Give a group a readable name",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#order-specific-rules-before-broad-ones",
        "text": "Order specific rules before broad ones",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#group-all-elements-in-a-container",
        "text": "Group all elements in a container",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#group-form-controls-with-their-labels",
        "text": "Group form controls with their labels",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#group-elements-inside-a-shadow-dom",
        "text": "Group elements inside a shadow DOM",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#group-elements-inside-an-iframe",
        "text": "Group elements inside an iframe",
        "level": 3
      },
      {
        "id": "ui-coverage/configuration/elementgroups#config",
        "text": "Config",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#html",
        "text": "HTML",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
        "text": "Elements shown in UI",
        "level": 4
      },
      {
        "id": "ui-coverage/configuration/elementgroups#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "ui-coverage/configuration/elementgroups#why-use-elementgroups",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Why use elementGroups?",
      "heading_level": 2,
      "content_markdown": "## Why use elementGroups?\n\nUI Coverage already [groups elements automatically](/llm/markdown/ui-coverage/core-concepts/element-grouping.md) based on their structure in the DOM. Use `elementGroups` when the automatic grouping doesn't match how your application actually behaves:\n\n*   **Consolidate repeated elements**: Collapse dynamic or repeated elements, such as items in a list, carousel, or data table, into one logical group.\n*   **Correct the automatic grouping**: Split apart elements that were grouped together incorrectly, or unite elements that the automatic rules kept separate.\n*   **Make reports readable**: Replace machine-generated selectors with meaningful names like \"Remove Item Button\" or \"Shipping Method Option\", so anyone reading the report understands what needs testing.\n*   **Group across markup differences**: Combine elements that share behavior but not structure, such as form controls and their labels.\n\nIf your goal is different, such as renaming a single element or removing elements from reports entirely, see [Which option do I need?](/llm/markdown/ui-coverage/configuration/overview.md#Which-option-do-I-need) in the configuration overview.\n",
      "section": "ui-coverage",
      "anchors": [
        "why-use-elementgroups"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 199
    },
    {
      "id": "ui-coverage/configuration/elementgroups#setting-elementgroups",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Setting elementGroups",
      "heading_level": 2,
      "content_markdown": "## Setting elementGroups\n\nTo add or edit `elementGroups`, open the **App Quality** tab in your project settings in Cypress Cloud. See [Setting configuration](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) for details, including how to regenerate past reports with a new configuration without rerunning your tests.\n",
      "section": "ui-coverage",
      "anchors": [
        "setting-elementgroups"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 52
    },
    {
      "id": "ui-coverage/configuration/elementgroups#syntax",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Syntax",
      "heading_level": 2,
      "content_markdown": "## Syntax\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": string,        \"name\": string,        \"documentScope\": [string],        \"comment\": string      }    ]  }}\n```\n\n### Options\n\n| Option | Required | Default | Description |\n| --- | --- | --- | --- |\n| `selector` | Required |  | A CSS selector matched against each interactive element. Supports standard CSS selector syntax, including IDs, classes, attributes, combinators, and functional pseudo-classes like `:has()`. |\n| `name` | Optional | `selector` | A human-readable name for the group, displayed in UI Coverage reports. Each group's name must be unique. |\n| `documentScope` | Optional |  | An ordered array of CSS selectors that match document hosts (iframes or shadow DOM hosts) in the element's ancestor chain, from outermost to innermost. Use this to group only elements inside a specific iframe or shadow DOM. Hosts that aren't listed may appear between the ones that are. |\n| `comment` | Optional |  | A note about why this rule exists, for your team's benefit. Comments have no effect on how elements are grouped. |\n\n### How are elementGroups rules applied?\n\nRules are evaluated in order, and each element joins the group of the **first** rule whose `selector` matches it. Elements that match no rules fall back to the automatic [element grouping rules](/llm/markdown/ui-coverage/core-concepts/element-grouping.md).\n\nA few behaviors are worth knowing:\n\n*   **Custom groups override automatic grouping.** An element that matches an `elementGroups` rule is grouped by that rule alone. This includes links, which would otherwise be grouped by their `href` patterns.\n*   **A group forms even with one match.** Unlike the automatic rules, which require at least two similar elements, an `elementGroups` rule that matches a single element still produces a named group in the report.\n*   **Filtered elements are never grouped.** Elements excluded from reports by [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md) are not considered for grouping.\n*   **The `data-cy-ui-group` attribute wins.** Elements that declare a group directly in your markup with the [`data-cy-ui-group` attribute](/llm/markdown/ui-coverage/core-concepts/element-grouping.md#Grouping-with-markup-attributes) keep that group, even if they also match an `elementGroups` rule.\n\n### Validation rules\n\nCypress Cloud rejects a configuration when:\n\n*   Two rules have the same `selector` (with the same `documentScope`). Duplicates can never both apply, because the first matching rule wins.\n*   Two rules have the same `name`, or one rule's `name` matches another rule's `selector`. Groups are identified by these values, so they must be unique.\n*   A `selector` or `documentScope` entry isn't valid CSS selector syntax.\n*   A rule contains a property other than the four listed above.\n",
      "section": "ui-coverage",
      "anchors": [
        "syntax"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 556
    },
    {
      "id": "ui-coverage/configuration/elementgroups#options",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Options",
      "heading_level": 3,
      "content_markdown": "### Options\n\n| Option | Required | Default | Description |\n| --- | --- | --- | --- |\n| `selector` | Required |  | A CSS selector matched against each interactive element. Supports standard CSS selector syntax, including IDs, classes, attributes, combinators, and functional pseudo-classes like `:has()`. |\n| `name` | Optional | `selector` | A human-readable name for the group, displayed in UI Coverage reports. Each group's name must be unique. |\n| `documentScope` | Optional |  | An ordered array of CSS selectors that match document hosts (iframes or shadow DOM hosts) in the element's ancestor chain, from outermost to innermost. Use this to group only elements inside a specific iframe or shadow DOM. Hosts that aren't listed may appear between the ones that are. |\n| `comment` | Optional |  | A note about why this rule exists, for your team's benefit. Comments have no effect on how elements are grouped. |\n",
      "section": "ui-coverage",
      "anchors": [
        "options"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 209
    },
    {
      "id": "ui-coverage/configuration/elementgroups#how-are-elementgroups-rules-applied",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "How are elementGroups rules applied?",
      "heading_level": 3,
      "content_markdown": "### How are elementGroups rules applied?\n\nRules are evaluated in order, and each element joins the group of the **first** rule whose `selector` matches it. Elements that match no rules fall back to the automatic [element grouping rules](/llm/markdown/ui-coverage/core-concepts/element-grouping.md).\n\nA few behaviors are worth knowing:\n\n*   **Custom groups override automatic grouping.** An element that matches an `elementGroups` rule is grouped by that rule alone. This includes links, which would otherwise be grouped by their `href` patterns.\n*   **A group forms even with one match.** Unlike the automatic rules, which require at least two similar elements, an `elementGroups` rule that matches a single element still produces a named group in the report.\n*   **Filtered elements are never grouped.** Elements excluded from reports by [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md) are not considered for grouping.\n*   **The `data-cy-ui-group` attribute wins.** Elements that declare a group directly in your markup with the [`data-cy-ui-group` attribute](/llm/markdown/ui-coverage/core-concepts/element-grouping.md#Grouping-with-markup-attributes) keep that group, even if they also match an `elementGroups` rule.\n",
      "section": "ui-coverage",
      "anchors": [
        "how-are-elementgroups-rules-applied"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 208
    },
    {
      "id": "ui-coverage/configuration/elementgroups#validation-rules",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Validation rules",
      "heading_level": 3,
      "content_markdown": "### Validation rules\n\nCypress Cloud rejects a configuration when:\n\n*   Two rules have the same `selector` (with the same `documentScope`). Duplicates can never both apply, because the first matching rule wins.\n*   Two rules have the same `name`, or one rule's `name` matches another rule's `selector`. Groups are identified by these values, so they must be unique.\n*   A `selector` or `documentScope` entry isn't valid CSS selector syntax.\n*   A rule contains a property other than the four listed above.\n",
      "section": "ui-coverage",
      "anchors": [
        "validation-rules"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 107
    },
    {
      "id": "ui-coverage/configuration/elementgroups#examples",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Examples",
      "heading_level": 2,
      "content_markdown": "## Examples\n\n### Consolidate repeated elements\n\nElements generated from data often have dynamic attribute values, like an add to cart button rendered for each product in a listing. One rule with an attribute prefix selector collapses all of them into a single group. Without a `name`, the group is displayed under its selector.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy^='add-to-cart-']\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-cy=\"add-to-cart-101\">Add to Cart</button>  <button data-cy=\"add-to-cart-102\">Add to Cart</button>  <button data-cy=\"add-to-cart-103\">Add to Cart</button></body>\n```\n\n#### Elements shown in UI\n\nWithout the rule, the result depends on the automatic grouping heuristics. The buttons may appear individually or under a machine-generated name:\n\n```\n[data-cy=\"add-to-cart-101\"][data-cy=\"add-to-cart-102\"][data-cy=\"add-to-cart-103\"]\n```\n\nWith the rule, the buttons always appear as one group:\n\n```\n[data-cy^='add-to-cart-'] (3 instances)\n```\n\n* * *\n\n### Give a group a readable name\n\nAdd a `name` so the report describes the interface instead of your selectors. Here, the remove buttons in a shopping cart appear as a single \"Remove Item Button\" group, no matter which products the cart contains.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[id^='remove-item-']\",        \"name\": \"Remove Item Button\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button id=\"remove-item-101\">Remove</button>  <button id=\"remove-item-102\">Remove</button>  <button id=\"remove-item-103\">Remove</button>  <button id=\"remove-item-104\">Remove</button></body>\n```\n\n#### Elements shown in UI\n\n```\nRemove Item Button (4 instances)\n```\n\n* * *\n\n### Order specific rules before broad ones\n\nEach element joins the first rule that matches it, so list narrower rules first. Here, the featured product's button gets its own group because its rule comes before the catch-all rule. If the rules were reversed, the catch-all would capture all three buttons and the \"Featured Buy Now Button\" rule would never match anything.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy='buy-now-featured']\",        \"name\": \"Featured Buy Now Button\"      },      {        \"selector\": \"[data-cy^='buy-now-']\",        \"name\": \"Buy Now Button\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-cy=\"buy-now-featured\">Buy Now</button>  <button data-cy=\"buy-now-101\">Buy Now</button>  <button data-cy=\"buy-now-102\">Buy Now</button></body>\n```\n\n#### Elements shown in UI\n\n```\nFeatured Buy Now Button (1 instance)Buy Now Button (2 instances)\n```\n\n* * *\n\n### Group all elements in a container\n\nUse a descendant combinator to group every interactive element inside a container. This example collapses a delivery date picker's day buttons, which would otherwise appear as dozens of separate elements, into one group. The selector must match the interactive elements themselves, not just the container.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"#datepicker button\",        \"name\": \"Datepicker Day\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <div id=\"datepicker\">    <button id=\"day-1\">1</button>    <button id=\"day-2\">2</button>    <button id=\"day-3\">3</button>  </div></body>\n```\n\n#### Elements shown in UI\n\n```\nDatepicker Day (3 instances)\n```\n\n* * *\n\n### Group form controls with their labels\n\nA comma-separated selector list can unite elements with different markup. This example groups a checkout page's shipping method radio inputs together with their clickable labels, using `:has()` to match the labels.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"input[name='shipping'], label:has(input[name='shipping'])\",        \"name\": \"Shipping Method Option\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <label>    <input type=\"radio\" id=\"standard\" name=\"shipping\" />    Standard  </label>  <label>    <input type=\"radio\" id=\"express\" name=\"shipping\" />    Express  </label></body>\n```\n\n#### Elements shown in UI\n\n```\nShipping Method Option (4 instances)\n```\n\n* * *\n\n### Group elements inside a shadow DOM\n\nThird-party widgets and design system components often render inside a shadow DOM. Use `documentScope` to apply a rule only to elements inside a specific shadow DOM host. Here, only the buttons inside the `<support-chat>` component's shadow root are grouped; the button in the main document also matches the `button` selector, but stays separate because it's outside the scope.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"button\",        \"name\": \"Support Chat Actions\",        \"documentScope\": [\"support-chat\"]      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button id=\"open-support-chat\">Chat with us</button>  <support-chat>    #shadow-dom      <button id=\"send-message\">Send</button>      <button id=\"attach-file\">Attach</button>  </support-chat></body>\n```\n\n#### Elements shown in UI\n\n```\n#open-support-chatSupport Chat Actions (2 instances)\n```\n\n* * *\n\n### Group elements inside an iframe\n\n`documentScope` works the same way for embedded services that render inside an iframe, such as a payment provider on a checkout page. The scope selector matches the `iframe` element itself in its parent document. Here, only the buttons inside the `#payment-provider` iframe are grouped, while the coupon button in the main document remains a separate element.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-action]\",        \"name\": \"Payment Widget Actions\",        \"documentScope\": [\"#payment-provider\"]      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-action=\"apply-coupon\">Apply Coupon</button>  <iframe id=\"payment-provider\" src=\"https://payments.example.com\">    <html>      <body>        <button data-action=\"submit-payment\">Pay Now</button>        <button data-action=\"change-method\">Change Payment Method</button>      </body>    </html>  </iframe></body>\n```\n\n#### Elements shown in UI\n\n```\n[data-action=\"apply-coupon\"]Payment Widget Actions (2 instances)\n```\n\nFor nested documents, list one selector per host from outermost to innermost, for example `\"documentScope\": [\"#payment-provider\", \"secure-card-input\"]`.\n\nAfter saving configuration changes, regenerate a recent run to preview your groups without rerunning your tests. See [Setting configuration](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) for how to regenerate a report.\n",
      "section": "ui-coverage",
      "anchors": [
        "examples"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 1115
    },
    {
      "id": "ui-coverage/configuration/elementgroups#consolidate-repeated-elements",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Consolidate repeated elements",
      "heading_level": 3,
      "content_markdown": "### Consolidate repeated elements\n\nElements generated from data often have dynamic attribute values, like an add to cart button rendered for each product in a listing. One rule with an attribute prefix selector collapses all of them into a single group. Without a `name`, the group is displayed under its selector.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy^='add-to-cart-']\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-cy=\"add-to-cart-101\">Add to Cart</button>  <button data-cy=\"add-to-cart-102\">Add to Cart</button>  <button data-cy=\"add-to-cart-103\">Add to Cart</button></body>\n```\n\n#### Elements shown in UI\n\nWithout the rule, the result depends on the automatic grouping heuristics. The buttons may appear individually or under a machine-generated name:\n\n```\n[data-cy=\"add-to-cart-101\"][data-cy=\"add-to-cart-102\"][data-cy=\"add-to-cart-103\"]\n```\n\nWith the rule, the buttons always appear as one group:\n\n```\n[data-cy^='add-to-cart-'] (3 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "consolidate-repeated-elements"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 177
    },
    {
      "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Elements shown in UI",
      "heading_level": 4,
      "content_markdown": "#### Elements shown in UI\n\nWithout the rule, the result depends on the automatic grouping heuristics. The buttons may appear individually or under a machine-generated name:\n\n```\n[data-cy=\"add-to-cart-101\"][data-cy=\"add-to-cart-102\"][data-cy=\"add-to-cart-103\"]\n```\n\nWith the rule, the buttons always appear as one group:\n\n```\n[data-cy^='add-to-cart-'] (3 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "elements-shown-in-ui"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 63
    },
    {
      "id": "ui-coverage/configuration/elementgroups#give-a-group-a-readable-name",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Give a group a readable name",
      "heading_level": 3,
      "content_markdown": "### Give a group a readable name\n\nAdd a `name` so the report describes the interface instead of your selectors. Here, the remove buttons in a shopping cart appear as a single \"Remove Item Button\" group, no matter which products the cart contains.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[id^='remove-item-']\",        \"name\": \"Remove Item Button\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button id=\"remove-item-101\">Remove</button>  <button id=\"remove-item-102\">Remove</button>  <button id=\"remove-item-103\">Remove</button>  <button id=\"remove-item-104\">Remove</button></body>\n```\n\n#### Elements shown in UI\n\n```\nRemove Item Button (4 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "give-a-group-a-readable-name"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 124
    },
    {
      "id": "ui-coverage/configuration/elementgroups#order-specific-rules-before-broad-ones",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Order specific rules before broad ones",
      "heading_level": 3,
      "content_markdown": "### Order specific rules before broad ones\n\nEach element joins the first rule that matches it, so list narrower rules first. Here, the featured product's button gets its own group because its rule comes before the catch-all rule. If the rules were reversed, the catch-all would capture all three buttons and the \"Featured Buy Now Button\" rule would never match anything.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy='buy-now-featured']\",        \"name\": \"Featured Buy Now Button\"      },      {        \"selector\": \"[data-cy^='buy-now-']\",        \"name\": \"Buy Now Button\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-cy=\"buy-now-featured\">Buy Now</button>  <button data-cy=\"buy-now-101\">Buy Now</button>  <button data-cy=\"buy-now-102\">Buy Now</button></body>\n```\n\n#### Elements shown in UI\n\n```\nFeatured Buy Now Button (1 instance)Buy Now Button (2 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "order-specific-rules-before-broad-ones"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 168
    },
    {
      "id": "ui-coverage/configuration/elementgroups#config",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Config",
      "heading_level": 4,
      "content_markdown": "#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-cy='buy-now-featured']\",        \"name\": \"Featured Buy Now Button\"      },      {        \"selector\": \"[data-cy^='buy-now-']\",        \"name\": \"Buy Now Button\"      }    ]  }}\n```\n",
      "section": "ui-coverage",
      "anchors": [
        "config"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 41
    },
    {
      "id": "ui-coverage/configuration/elementgroups#group-all-elements-in-a-container",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Group all elements in a container",
      "heading_level": 3,
      "content_markdown": "### Group all elements in a container\n\nUse a descendant combinator to group every interactive element inside a container. This example collapses a delivery date picker's day buttons, which would otherwise appear as dozens of separate elements, into one group. The selector must match the interactive elements themselves, not just the container.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"#datepicker button\",        \"name\": \"Datepicker Day\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <div id=\"datepicker\">    <button id=\"day-1\">1</button>    <button id=\"day-2\">2</button>    <button id=\"day-3\">3</button>  </div></body>\n```\n\n#### Elements shown in UI\n\n```\nDatepicker Day (3 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "group-all-elements-in-a-container"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 136
    },
    {
      "id": "ui-coverage/configuration/elementgroups#group-form-controls-with-their-labels",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Group form controls with their labels",
      "heading_level": 3,
      "content_markdown": "### Group form controls with their labels\n\nA comma-separated selector list can unite elements with different markup. This example groups a checkout page's shipping method radio inputs together with their clickable labels, using `:has()` to match the labels.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"input[name='shipping'], label:has(input[name='shipping'])\",        \"name\": \"Shipping Method Option\"      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <label>    <input type=\"radio\" id=\"standard\" name=\"shipping\" />    Standard  </label>  <label>    <input type=\"radio\" id=\"express\" name=\"shipping\" />    Express  </label></body>\n```\n\n#### Elements shown in UI\n\n```\nShipping Method Option (4 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "group-form-controls-with-their-labels"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 129
    },
    {
      "id": "ui-coverage/configuration/elementgroups#group-elements-inside-a-shadow-dom",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Group elements inside a shadow DOM",
      "heading_level": 3,
      "content_markdown": "### Group elements inside a shadow DOM\n\nThird-party widgets and design system components often render inside a shadow DOM. Use `documentScope` to apply a rule only to elements inside a specific shadow DOM host. Here, only the buttons inside the `<support-chat>` component's shadow root are grouped; the button in the main document also matches the `button` selector, but stays separate because it's outside the scope.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"button\",        \"name\": \"Support Chat Actions\",        \"documentScope\": [\"support-chat\"]      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button id=\"open-support-chat\">Chat with us</button>  <support-chat>    #shadow-dom      <button id=\"send-message\">Send</button>      <button id=\"attach-file\">Attach</button>  </support-chat></body>\n```\n\n#### Elements shown in UI\n\n```\n#open-support-chatSupport Chat Actions (2 instances)\n```\n\n* * *\n",
      "section": "ui-coverage",
      "anchors": [
        "group-elements-inside-a-shadow-dom"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 160
    },
    {
      "id": "ui-coverage/configuration/elementgroups#group-elements-inside-an-iframe",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Group elements inside an iframe",
      "heading_level": 3,
      "content_markdown": "### Group elements inside an iframe\n\n`documentScope` works the same way for embedded services that render inside an iframe, such as a payment provider on a checkout page. The scope selector matches the `iframe` element itself in its parent document. Here, only the buttons inside the `#payment-provider` iframe are grouped, while the coupon button in the main document remains a separate element.\n\n#### Config\n\nApp Quality Config\n\n```\n{  \"uiCoverage\": {    \"elementGroups\": [      {        \"selector\": \"[data-action]\",        \"name\": \"Payment Widget Actions\",        \"documentScope\": [\"#payment-provider\"]      }    ]  }}\n```\n\n#### HTML\n\n```\n<body>  <button data-action=\"apply-coupon\">Apply Coupon</button>  <iframe id=\"payment-provider\" src=\"https://payments.example.com\">    <html>      <body>        <button data-action=\"submit-payment\">Pay Now</button>        <button data-action=\"change-method\">Change Payment Method</button>      </body>    </html>  </iframe></body>\n```\n\n#### Elements shown in UI\n\n```\n[data-action=\"apply-coupon\"]Payment Widget Actions (2 instances)\n```\n\nFor nested documents, list one selector per host from outermost to innermost, for example `\"documentScope\": [\"#payment-provider\", \"secure-card-input\"]`.\n\nAfter saving configuration changes, regenerate a recent run to preview your groups without rerunning your tests. See [Setting configuration](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) for how to regenerate a report.\n",
      "section": "ui-coverage",
      "anchors": [
        "group-elements-inside-an-iframe"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 217
    },
    {
      "id": "ui-coverage/configuration/elementgroups#elements-shown-in-ui",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "Elements shown in UI",
      "heading_level": 4,
      "content_markdown": "#### Elements shown in UI\n\n```\n[data-action=\"apply-coupon\"]Payment Widget Actions (2 instances)\n```\n\nFor nested documents, list one selector per host from outermost to innermost, for example `\"documentScope\": [\"#payment-provider\", \"secure-card-input\"]`.\n\nAfter saving configuration changes, regenerate a recent run to preview your groups without rerunning your tests. See [Setting configuration](/llm/markdown/ui-coverage/configuration/overview.md#Setting-configuration) for how to regenerate a report.\n",
      "section": "ui-coverage",
      "anchors": [
        "elements-shown-in-ui"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 72
    },
    {
      "id": "ui-coverage/configuration/elementgroups#see-also",
      "doc_id": "ui-coverage/configuration/elementgroups",
      "heading": "See also",
      "heading_level": 2,
      "content_markdown": "## See also\n\n*   [Element Grouping](/llm/markdown/ui-coverage/core-concepts/element-grouping.md) explains the automatic grouping rules that apply when no `elementGroups` rule matches, and the `data-cy-ui-group` markup attribute.\n*   [`elements`](/llm/markdown/ui-coverage/configuration/elements.md) assigns a stable identity and name to a single element rather than a group.\n*   [`elementFilters`](/llm/markdown/ui-coverage/configuration/elementfilters.md) removes elements from reports entirely instead of grouping them.\n*   [Configuration overview](/llm/markdown/ui-coverage/configuration/overview.md) covers where configuration lives and how to regenerate reports after changing it.\n*   [Guide: Reduce noise in UI Coverage reports](/llm/markdown/ui-coverage/guides/reduce-noise.md) walks through combining these options to clean up a report.\n*   [UI Coverage FAQ](/llm/markdown/ui-coverage/faq.md) answers common questions about grouping, scores, and configuration.\n",
      "section": "ui-coverage",
      "anchors": [
        "see-also"
      ],
      "path": "/llm/json/chunked/ui-coverage/configuration/elementgroups.json",
      "token_estimate": 127
    }
  ]
}