XPath Cheat Sheet
Quick reference for XPath syntax and patterns in test automation.
Robustness Rules
Follow these rules for selectors that survive DOM changes:
DO
- ✓ Use data-testid attributes
- ✓ Use relative paths (//
- ✓ Target by unique attributes
- ✓ Use aria-* and role attributes
- ✓ Use contains() for classes
- ✓ Anchor to stable parent elements
DON'T
- ✗ Use auto-generated classes
- ✗ Use absolute paths from /html
- ✗ Use position indexes [1], [2]
- ✗ Rely on element order
- ✗ Match exact multi-class strings
- ✗ Use deep nested paths
Quick Navigation
Basic Selectors
//tagname Select all elements with this tag name
//* Select all elements (wildcard)
//div | //span Select multiple element types (union)
. Current node
.. Parent node
Attribute Selectors
//*[@id='value'] Select by ID
//*[@class='value'] Select by exact class (use contains for partial)
//input[@type='text'] Select by attribute value
//*[@data-testid] Select elements that have this attribute
//*[not(@disabled)] Select elements WITHOUT an attribute
//*[@data-testid='login-btn'] Select by data-testid (recommended)
//*[@aria-label='Close'] Select by ARIA label (accessible)
Partial Matching
//*[contains(@class, 'btn')] Class contains "btn"
//*[starts-with(@id, 'user-')] ID starts with "user-"
//a[contains(@href, '/login')] Href contains "/login"
//*[contains(@class, 'btn') and contains(@class, 'primary')] Multiple class conditions
Text Content
//button[text()='Submit'] Exact text match
//button[contains(text(), 'Add')] Text contains
//button[normalize-space()='Submit'] Normalized text (handles whitespace)
//button[starts-with(text(), 'Save')] Text starts with
//*[.='Full Text Here'] Full string value including descendants
Position & Indexing
(//button)[1] First button in document
(//ul/li)[last()] Last li in document
(//tr)[position() > 1] All rows except first (skip header)
//ul/li[1] First li child of each ul
//ul/li[last()-1] Second-to-last li child
(//div)[position() <= 3] First three divs
Axes Navigation
//div/child::span Direct children (same as //div/span)
//form//input Descendants at any level (same as descendant::)
//input/parent::div Direct parent (same as ..)
//input/ancestor::form Any ancestor (parent, grandparent, etc.)
//label/following-sibling::input Sibling after current element
//input/preceding-sibling::label Sibling before current element
//h2/following::p All elements after in document order
Tables
//table/tbody/tr All body rows
//tr[th]/following-sibling::tr Data rows (skip header)
//tr[.//td[text()='John']] Row containing specific cell text
//tr[.//td[text()='John']]//button Button in row with specific text
//td[2] Second column cells (fixed index)
count(//th[text()='Name']/preceding-sibling::th)+1 Dynamic column index by header
Form Elements
//input[@type='email'] Email input
//input[@type='password'] Password input
//button[@type='submit'] Submit button
//select[@name='country'] Select dropdown by name
//input[@required] Required fields
//input[not(@disabled)] Enabled inputs
//label[text()='Email']/following-sibling::input Input by its label text
Conditions & Logic
//input[@type='text' and @required] AND condition
//button[@type='submit' or @type='button'] OR condition
//div[not(@hidden)] NOT condition
//div[@class and @id] Has both attributes
//input[@type='text'][@name='email'] Chained predicates (same as AND)
XPath Functions
count(//li) Count matching elements
string(//title) Get string value of element
concat(@first, " ", @last) Concatenate strings
translate(text(), 'ABC', 'abc') Replace characters (case conversion)
substring(@id, 1, 4) Substring extraction
string-length(text()) Length of text content
sum(//price) Sum of numeric values
XPath Axes Diagram
ancestor::
↑
preceding:: ← [ current node ] → following::
↓
descendant::
self:: = current node | child:: = direct descendants | parent:: = direct ancestor
following-sibling:: = siblings after | preceding-sibling:: = siblings before
Attribute Preference Order
data-testid, data-test, data-cy
Specifically for testing, most stable
id (if static, not auto-generated)
Unique identifiers are reliable
name (for form elements)
Form element names are typically stable
aria-label, role
Accessibility attributes, usually stable
Text content
May change with copy updates
Class names (use semantic only)
Avoid auto-generated or utility classes
Position indexes [1], [2], etc.
Last resort, breaks easily
XPathDecoded.com - The XPath Reference for Test Automation