XPath Selectors
This guide explains how TrueAssert's intelligent XPath selector generation works and why XPath selectors are more robust than CSS selectors.
Overview
TrueAssert automatically generates XPath selectors for all recorded and AI-generated tests. These selectors are designed to be:
Robust: Resist breaking when UI changes
Unique: Match exactly one element
Stable: Use semantic attributes over styling
Intelligent: Prioritize better selectors automatically
Why XPath Over CSS?
Advantages of XPath
More Expressive: Can navigate DOM structure more flexibly
Better for Complex Pages: Handles nested structures better
Text Matching: Can match elements by text content
Positional Queries: Can find elements by position
Attribute Combinations: Can combine multiple attributes easily
When CSS is Used
CSS selectors are still supported but XPath is preferred because:
XPath is more robust for complex UIs
XPath handles dynamic content better
XPath provides more selection options
How XPath Generation Works
The Algorithm: "Optimus-SA" (Stable Anchor)
TrueAssert uses a penalty-based algorithm that tries multiple strategies in order of quality:
ID-Based (Penalty: 0) - Best
Data-TestID (Penalty: 1)
ARIA Attributes (Penalty: 5)
Form Attributes (Penalty: 10)
Text Content (Penalty: 20)
Stable Attributes (Penalty: 30)
CSS Classes (Penalty: 50)
Structural Positioning (Penalty: 1000+) - Last resort
Strategy Execution
The algorithm:
Tries each strategy in order (lowest penalty first)
Generates XPath selector for that strategy
Verifies selector matches exactly one element
Returns first successful selector
Falls back to next strategy if current fails
Selector Priority System
Priority 0: ID Selectors
Format: //*[@id='element-id']
Example: //button[@id='submit']
When Used: Element has a unique ID attribute
Quality: Best possible selector
Penalty: 0 (lowest)
Priority 1: Data-TestID
Format: //*[@data-testid='test-id']
Example: //div[@data-testid='user-menu']
When Used: Element has data-testid attribute
Quality: Excellent (explicit test contract)
Penalty: 1
Priority 5: ARIA Attributes
Format: //*[@role='button' and @aria-label='Submit']
Example: //button[@role='button' and @aria-label='Login']
When Used: Element has ARIA role and/or label
Quality: Very good (semantic, user-facing)
Penalty: 5
Note: Can also use standalone @aria-label without role
Priority 10: Form Attributes
Format: //input[@name='email'] or //input[@type='submit']
Example: //input[@name='username']
When Used: Form elements with name, type, or placeholder
Quality: Good (stable for forms)
Penalty: 10
Priority 20: Text Content
Format: //button[normalize-space()='Click Me']
Example: //a[normalize-space()='Login']
When Used: Element has unique, stable text content
Quality: Medium (can break with i18n)
Penalty: 20
Note: Avoided for very long text content
Priority 30: Stable Attributes
Format: //a[@href='/login'] or //img[@alt='Logo']
Example: //a[@href='/dashboard']
When Used: Element has stable, meaningful attributes
Quality: Medium (context-dependent)
Penalty: 30
Priority 50: CSS Classes
Format: //div[contains(@class, 'container')]
Example: //button[contains(@class, 'primary')]
When Used: Element has stable CSS classes
Quality: Lower (styling-coupled)
Penalty: 50
Note: Dynamic classes (like css-abc123) are filtered out
Priority 1000+: Structural Positioning
Format: //div[1]/div[2]/button[1]
Example: //body/div[3]/div[2]/button[1]
When Used: No stable attributes available
Quality: Lowest (brittle, breaks easily)
Penalty: 1000+
Note: Only used as last resort
Advanced Strategies
Parent-Based Selection
When element itself doesn't have good attributes, algorithm looks for stable parent:
Walk up DOM tree
Find parent with good attributes (ID, data-testid, ARIA)
Build path:
//parent[@id='container']//button
Example: //div[@id='form-container']//button[@type='submit']
Positional Indexing
When multiple elements match, add positional index:
Format: (//button[@aria-label='Submit'])[2]
Example: (//div[@class='item'])[3]
When Used: Multiple elements with same attributes
Child-Based Selection
Use stable children to identify parent:
Format: //div[.//button[@id='submit']]
Example: //form[.//input[@name='email']]
When Used: Parent needs identification via children
XPath Syntax
Basic Syntax
Element Selection:
//button- All buttons//button[@id='submit']- Button with ID//div[@class='container']- Div with class
Attribute Matching:
[@id='value']- Exact match[contains(@class, 'value')]- Partial match[@role='button' and @aria-label='Submit']- Multiple conditions
Text Matching:
[normalize-space()='Text']- Exact text (normalized)[contains(text(), 'Text')]- Contains text
Positional:
[1]- First element[last()]- Last element[position() > 2]- Position greater than 2
Combining Conditions
AND:
//button[@role='button' and @aria-label='Submit']OR:
//input[@type='text' or @type='email']Complex:
//div[@id='container' and contains(@class, 'active')]//button[@type='submit']Special Cases
SVG Elements
SVG elements use local-name():
//*[local-name()='path']- SVG path element//*[local-name()='svg']//*[local-name()='circle']- Nested SVG
Shadow DOM
Shadow DOM elements are handled specially:
Algorithm detects shadow boundaries
Generates path through shadow hosts
Uses shadow host as anchor point
Iframes
Iframe content requires special handling:
Algorithm detects iframe context
Generates path including iframe
May require iframe navigation
Selector Quality
What Makes a Good Selector
Stable Attributes: Uses IDs, data-testid, ARIA
Unique: Matches exactly one element
Semantic: Based on meaning, not styling
Resistant to Change: Won't break with minor UI updates
What Makes a Bad Selector
CSS Classes: Styling changes break selectors
Structural Only: Position changes break selectors
Dynamic IDs: IDs that change (e.g.,
id-12345)Long Text: Text content that changes
Best Practices
For Developers
Add IDs: Give important elements stable IDs
Use data-testid: Add data-testid for test elements
ARIA Labels: Use ARIA attributes for accessibility (bonus: better selectors)
Avoid Dynamic Classes: Don't use randomly generated classes
For Test Creators
Review Selectors: Check generated selectors in test detail
Prefer Recorded: Recording generates better selectors than manual
Update When Needed: Fix selectors if page changes
Use Alternatives: Try alternative selectors if available
Troubleshooting
Selector Not Unique
Problem: XPath matches multiple elements
Solution: Algorithm should add positional indexing automatically
If not, manually add
[1]or[2]etc.Or use more specific parent anchor
Selector Too Brittle
Problem: Selector breaks with minor UI changes
Solution:
Check if element has better attributes (ID, data-testid)
Update selector to use stable attributes
Consider using alternative selector
Selector Not Found
Problem: Element doesn't exist on page
Solution:
Verify element actually exists
Check if element loads after page load (add WAIT)
Verify selector syntax is correct
Related Topics
Selector Best Practices - Tips for reliable selectors
Troubleshooting Selectors - Fix selector issues
Browser Plugin Recording - How selectors are generated
Manual Test Creation - Edit selectors manually
Last updated