How to define Element Selectors
Introductionβ
With the goal to keep parity, between components and practices, we aim to assign unique test ID's to the elements in order to interact in our tests. Test ID's are the least likely to change over time and are locale-agnostic.
We are basing our own guidelines in those of WebdriverIO.
π Selector Guidelines
β
When to Add data-testidβ
Adding a data-testid will increase the bundle size, thus aim to add them only when necessary:
- The element is a custom component not exposing native semantics
- You need to target a key interactive element (e.g. button, input)
- Visual text is dynamic, localized, or unstable
- The test needs to disambiguate similar elements
β Do NOT add data-testid for:β
- Static or purely visual content (e.g., headings, icons, paragraphs)
- Decorative elements or wrappers (e.g., divs used for layout)
- Every DOM node β this clutters markup, greatly increase the bundle size and hurts maintainability
π§© Naming Conventionβ
Use this format:
<component>--<element>--<index>
This ensures predictable, structured selectors.
Examplesβ
| Context | Selector |
|---|---|
| Component | action-button |
| Element in Component | action-button--loading-bar |
π‘ For dynamic lists, include the index, p.e., radio-list--item--0
π§ͺ WebdriverIO Best Practicesβ
β
Select by data-testid:β
await $('[data-testid="${ACTION_BUTTON}"]').click();
π« Avoid:β
- XPath selectors (
//button[contains(text(),"Submit")]) - CSS selectors based on structure (
div > button:nth-child(3)) - Class names (
.btn-primary) β subject to change with styling
π‘ Implementation Exampleβ
const ACTION_BUTTON = "action-button";
const LOADING_BAR = `${ACTION_BUTTON}--loading-bar`;
module.exports = {
ACTION_BUTTON,
LOADING_BAR,
};
export const ActionButton: FunctionComponent<ActionButtonProps> = ({
(...)
return (
<button type="button" (...) data-testid={ACTION_BUTTON}>
{children}
<div className={loadingBarClassNames} (...) data-testid={LOADING_BAR}/>
</button>
);
};
π Rules Summaryβ
| Rule | Description |
|---|---|
| β Use for key interactive elements | Buttons, inputs, toggles |
| β Use when role/text is unreliable | Custom UI or dynamic content |
| β Use consistent naming | <component>--<element>, if needed add an index <component>--<element>--<index> |
| β Donβt overuse | Avoid static, presentational elements |
| β Donβt duplicate semantic roles | Prefer built-in queries if available |