> For the complete documentation index, see [llms.txt](https://docs.bugbug.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bugbug.io/preventing-failed-tests/selectors.md).

# Selectors

How BugBug finds elements: recorded and custom CSS or XPath selectors, nested selectors, several matching elements, Shadow DOM, iframes and fixing element-not-found errors.

A selector tells BugBug which element a step works on, for example "the button with the text Add to cart" or "the element with `data-testid="submit"`". Every step that targets an element, such as a click or an assertion, has a selector.

{% hint style="info" %}
**In short**

* When you record a test, BugBug creates the selectors for you. You can replace any of them with your own CSS or XPath selector.
* If a selector matches several elements, BugBug uses the first one. Make selectors unique.
* Nested selectors narrow the search step by step: first a container, then an element inside it, its parent or its sibling.
* Selectors find elements inside Shadow DOM, open and closed, without extra steps.
* Elements inside an iframe need a **Switch context** step first.
* The most stable selectors use attributes made for testing, such as `data-testid`.
  {% endhint %}

## Recorded selectors

When you [record a test](/recording-tests-steps/bugbug-ai-test-recorder.md), BugBug's Adaptive Locators create several selectors for each element and use AI to pick the most stable one. They prefer testing attributes, semantic roles and text over generated IDs or positions in the page.

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2FdkJQ2Mh5v86noXyDw9Q1%2Fimage.png?alt=media&amp;token=f81e56ba-1ee2-4f5b-a2a5-80adbe4629a8" alt="Selector created automatically during recording"><figcaption><p>Selector created during recording</p></figcaption></figure>

Adaptive Locators are available on all plans, including Free, and need no setup. Which kinds of selectors BugBug creates, and in which order, can be changed in [project settings](/preventing-failed-tests/project-settings.md#selectors).

## Custom selectors

To use your own selector, open the step, click the selector and choose **Customize**, the last option.

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2FzBVPcviGuFyBGOtIbxBz%2Fimage.png?alt=media&amp;token=598aa914-20bb-4ead-ba85-af4f802290bd" alt="Customize option in the selector menu"><figcaption><p>Customizing a selector</p></figcaption></figure>

BugBug accepts:

| Type  | Example                                     |
| ----- | ------------------------------------------- |
| CSS   | `button[data-testid="submit"]`              |
| XPath | `//button[normalize-space()="Add to cart"]` |

In [YAML](/export-import-and-backup/yaml-schema.md), start each selector with `css=` or `xpath=`, for example `css=button[data-testid="submit"]`.

You can use [variables](/editing-tests/variables.md) in selectors, including local variables saved earlier in the run, for example `//tr[contains(., "{{orderId}}")]`.

{% hint style="info" %}
To build an XPath selector without writing it by hand, use the free [XPath Selector Builder](https://bugbug.io/xpath-selector-builder/?ref=docs). It also teaches the HTML basics you need: tags, attributes, parents and children.
{% endhint %}

## Nested selectors

A nested selector finds an element in steps. The first selector finds a container, and the next one searches only in relation to it. This helps when the same element appears many times, for example a **Delete** button in every row of a table: first find the right row, then the button inside it.

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2Fd2j8Nk1mrWjtuxyIOGWo%2Fimage.png?alt=media&amp;token=162eebed-481f-4bef-90d8-1b280aab64bc" alt="Nested selector in the step editor"><figcaption><p>Nested selector</p></figcaption></figure>

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2FdU8WVbyrrAp2A3nrjDFw%2Fimage.png?alt=media&amp;token=a0eac029-1155-4fcf-84f7-1fd4a718079c" alt="Parent and child parts of a nested selector"><figcaption><p>The first selector finds the container, the next one the element in it</p></figcaption></figure>

In the editor, add the next selector with **Then find its child**, **parent** or **sibling**:

| In the app | Next selector searches among…    | YAML operator |
| ---------- | -------------------------------- | ------------- |
| child      | the previous match's descendants | `>>`          |
| parent     | the previous match's ancestors   | `<<`          |
| sibling    | elements on the same level       | `~`           |

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2F5814g3LXgUaUsjR5Y4pQ%2Fimage.png?alt=media&amp;token=fc2b8429-637b-4580-81cb-863e61476dec" alt="Child, parent and sibling options"><figcaption><p>Options for the next selector</p></figcaption></figure>

In YAML, join the parts with the operator, with a space on each side:

```yaml
selector: "xpath=//*[@data-testid='todo-list'] >> xpath=.//li[2]"
```

See [Selectors in the YAML schema](/export-import-and-backup/yaml-schema.md#selectors).

## When a selector matches several elements

BugBug uses the **first** matching element, in the order of the page. It doesn't fail or warn. If the first match is hidden, for example a duplicate button for mobile with `display: none`, the step can fail with a waiting condition or work on the wrong element.

Make the selector match exactly one element:

* Use an attribute that's unique, such as `data-testid` or `id`.
* Use a nested selector: find the right container first.
* As a last resort, pick one match by its position, for example `(//button[text()="Delete"])[2]`. Positions break easily when the page changes.

Only the **Number of elements in the DOM** [assertion](/editing-tests/assertions.md#number-of-elements-in-the-dom) uses all matches: it counts them.

## Shadow DOM

Web components, such as Salesforce Lightning, Shopify or design-system components, keep their content in a shadow root. BugBug finds elements in Shadow DOM without extra steps:

* **Selectors look inside shadow roots.** A CSS or XPath selector finds matching elements in the page and in its shadow roots, also nested ones. With several matches, the first one in the order of the page is used.
* **Closed shadow roots work too.** During recording and test runs, BugBug can access shadow roots that the page creates as closed.
* **Recorded selectors go through the shadow hosts.** For an element inside a shadow root, BugBug records a nested selector: first the shadow host element, then the element inside it, one part for each shadow root level.

When you write your own selector for an element in Shadow DOM:

* A single XPath expression can't cross a shadow boundary. `//my-component//button` doesn't find a button inside the shadow root of `my-component`. Use a nested selector instead: the host first, `//my-component`, then **Then find its child** with `//button`.
* A selector that matches the element by itself, such as `//button[@data-testid="save"]`, finds it in any shadow root. Use a nested selector when the same element exists in several components.

## iframes

Selectors search only the page or the iframe that the test is in. To work with elements inside an iframe, add a **Switch context** step that enters the iframe first, and another one to return to the main page. BugBug records these steps for you. See [Tabs & iframes](/editing-tests/tabs-and-iframes.md).

## Make selectors stable

The most reliable selectors use attributes added for testing, such as `data-testid` or `data-test`. They don't change when texts, styles or the layout change. Ask your developers to add them to the elements you test. BugBug prefers them automatically.

![Element with a data-testid attribute](https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2Fmx9CxOn0e8vCLzp4TkuR%2Fimage.png?alt=media\&token=46bb80ef-54b1-47fe-a446-b0555c9bdccd)

To tell BugBug which attributes to prefer or avoid, go to [Project settings → Selectors](/preventing-failed-tests/project-settings.md#selectors). You can add your own attributes to **Include custom attributes**, exclude attributes, or switch to **Manual** mode and set the order of selector types yourself. It's an advanced setting and usually doesn't need changes.

![Selector settings in project settings](https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2FbKFwKP65w8x8P8EFDIwk%2FScreenshot%202022-04-11%20at%2019.08.53.png?alt=media\&token=b921d690-19d8-4fe0-bb3f-d73f1657cd94)

## Fix "element not found"

When a step fails with `ELEMENT_DOES_NOT_EXIST`, no element matched the selector before the step's timeout. Common reasons:

* **The selector uses a value that changes**, such as a generated ID or class name that's different on every run.
* **It depends on the order of elements**, for example the third product in a list that changes every day.
* **The text changed.** A text-based selector, such as `//DIV[normalize-space(text())="Create account in Example Project"]`, stops working when the text is edited.
* **The element is in an iframe** and the test didn't switch to it.

![Element not found error](https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2Fg89baEK9bYPbv1mpau8R%2FScreenshot%202022-04-12%20at%2010.41.05.png?alt=media\&token=fc38bccf-7ae8-4d94-a7e5-3b057861839c)

To fix it:

1. Look at the failed step's [screenshot](/debugging-tests/screenshots.md) and [DOM snapshot](/debugging-tests/dom-snapshot.md) to see what the page looked like.
2. Check the selector in Chrome: click the copy button next to the selector, open DevTools, press Ctrl+F (Cmd+F on Mac) in the **Elements** tab and paste the selector. Chrome highlights the matching elements.
3. Change the selector to a stable one, or [re-record the step](/recording-tests-steps/re-recording-steps.md).

![Copy selector button](https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2FIQWcH0gfKFjVCiLjJZKu%2Fimage.png?alt=media\&token=708489b4-a602-434d-9470-d45dbc0e9288)

![Selector pasted into Chrome DevTools search](https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2Fo3cyOjY3FqlMkG7qKgo7%2FScreenshot%202022-04-12%20at%2010.49.54.png?alt=media\&token=4aa112e2-757b-4226-87b4-3e1958b02496)

See also [Debug in Chrome](/debugging-tests/debug-in-chrome.md) and [Common selectors issues](/troubleshooting/common-selectors-issues.md).

## FAQ

<details>

<summary>What happens if my selector matches more than one element?</summary>

BugBug uses the first match in the order of the page, without a warning. Make the selector unique, or use a nested selector to search inside the right container. See [When a selector matches several elements](#when-a-selector-matches-several-elements).

</details>

<details>

<summary>Does BugBug work with Shadow DOM and web components?</summary>

Yes, with open and closed shadow roots. Selectors look inside shadow roots, and recorded steps use a nested selector through the shadow host. A single XPath can't cross a shadow boundary, so for your own selectors use a nested selector: the host first, then the element inside it. See [Shadow DOM](#shadow-dom).

</details>

<details>

<summary>Can I use CSS selectors or only XPath?</summary>

Both. In the app, enter either. In YAML, start the selector with `css=` or `xpath=`.

</details>

<details>

<summary>Can a selector find an element inside an iframe?</summary>

Only after a **Switch context** step enters the iframe. See [Tabs & iframes](/editing-tests/tabs-and-iframes.md).

</details>

<details>

<summary>Can I use variables in selectors?</summary>

Yes, for example `//tr[contains(., "{{orderId}}")]`, including local variables saved earlier in the run.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bugbug.io/preventing-failed-tests/selectors.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
