11 KiB
Selector Strategies Guide
Selector Types Overview
| Selector Type | Stability | Speed | Best For |
|---|---|---|---|
| CSS ID | ⭐⭐⭐⭐⭐ | Fast | Unique IDs |
| Smart Mode (text) | ⭐⭐⭐⭐ | Medium | Text-based UI |
| XPath (text) | ⭐⭐⭐⭐ | Medium | Text content |
| CSS Class | ⭐⭐ | Fast | Stable classes |
| XPath (structure) | ⭐⭐ | Medium | DOM structure |
Decision Tree
Does element have unique ID?
├─ YES → Use CSS: #element-id
└─ NO ↓
Is element identified by text?
├─ YES → Use Smart Mode: --text "Submit"
└─ NO ↓
Does element have stable class?
├─ YES → Use CSS: .stable-class
└─ NO ↓
Can use ARIA attributes?
├─ YES → Use XPath: //*[@role='button']
└─ NO ↓
Use structural XPath
CSS Selectors
By ID (Most Stable)
node .browser-pilot/bp click -s "#login-button"
node .browser-pilot/bp click -s "#user-menu"
By Class
node .browser-pilot/bp click -s ".submit-btn"
node .browser-pilot/bp click -s ".modal .close-button"
By Attribute
node .browser-pilot/bp fill -s "input[name='email']" -v "test@example.com"
node .browser-pilot/bp click -s "button[data-action='submit']"
Complex Selectors
# Direct child
node .browser-pilot/bp click -s "div.modal > button.primary"
# Descendant
node .browser-pilot/bp click -s "form .submit-button"
# Nth-child
node .browser-pilot/bp click -s "ul > li:nth-child(3)"
# Multiple classes
node .browser-pilot/bp click -s "button.btn.btn-primary.btn-lg"
XPath Selectors
Text-Based (Most Reliable)
With Tag Name (Recommended):
# Specific tag with text
node .browser-pilot/bp click -s "//button[contains(text(), 'Submit')]"
node .browser-pilot/bp click -s "//a[contains(text(), 'Learn More')]"
# Exact text match
node .browser-pilot/bp click -s "//button[text()='Sign In']"
With Wildcard (Avoid):
# Searches all elements (slow, imprecise)
node .browser-pilot/bp click -s "//*[contains(text(), 'Submit')]"
Indexed XPath (Duplicates)
# First match
node .browser-pilot/bp click -s "(//button[contains(text(), 'Delete')])[1]"
# Third match
node .browser-pilot/bp click -s "(//button[contains(text(), 'Delete')])[3]"
# Last match (if 5 exist)
node .browser-pilot/bp click -s "(//button[contains(text(), 'Delete')])[5]"
Attribute-Based
# By type
node .browser-pilot/bp fill -s "//*[@type='email']" -v "test@example.com"
# By role
node .browser-pilot/bp click -s "//*[@role='button']"
# By data attribute
node .browser-pilot/bp click -s "//*[@data-testid='submit']"
# Partial match
node .browser-pilot/bp click -s "//*[contains(@href, 'checkout')]"
Structural XPath
# Parent-child
node .browser-pilot/bp click -s "//div[@class='modal']//button[@type='submit']"
# Following sibling
node .browser-pilot/bp click -s "//h1[contains(text(), 'Welcome')]/following-sibling::button"
# Preceding sibling
node .browser-pilot/bp click -s "//button[contains(text(), 'Next')]/preceding-sibling::button"
# Parent
node .browser-pilot/bp click -s "//button[@type='submit']/parent::form"
Smart Mode Selectors
Basic Text Search
node .browser-pilot/bp click --text "Submit"
node .browser-pilot/bp click --text "Add to Cart"
node .browser-pilot/bp fill --text "Username" -v "test"
With Type Filter
# Only buttons
node .browser-pilot/bp click --text "Submit" --type button
# Only links
node .browser-pilot/bp click --text "Learn More" --type a
# Only text inputs
node .browser-pilot/bp fill --text "Email" -v "test@example.com" --type input-text
Type Aliases (Auto-Expanded)
# Generic type (matches all subtypes)
node .browser-pilot/bp click --text "Search" --type input
# Expands to: input, input-text, input-search, input-password, etc.
# Specific type (exact match)
node .browser-pilot/bp fill --text "Email" --type input-search -v "query"
# Matches only: input-search
# Button aliases
node .browser-pilot/bp click --text "Submit" --type button
# Expands to: button, button-submit, button-reset, etc.
Tag-Based Search
# Filter by HTML tag (broader matching)
node .browser-pilot/bp click --text "Submit" --tag button
# Matches all <button> elements regardless of type
node .browser-pilot/bp fill --text "Email" --tag input -v "user@example.com"
# Matches all <input> elements regardless of type
# Combined with text search
node .browser-pilot/bp click --text "Next" --tag button --viewport-only
Tag vs Type:
--tag: Matches HTML tag name (<button>,<input>)--type: Matches interaction map classification (more specific)- Use
--tagwhen type filtering fails or for broader matches
With Indexing
# Second "Delete" button
node .browser-pilot/bp click --text "Delete" --index 2 --type button
# First "Submit" button
node .browser-pilot/bp click --text "Submit" --index 1
With Visibility Filter
# Only visible elements
node .browser-pilot/bp click --text "Next" --viewport-only
# Visible "Add to Cart" buttons
node .browser-pilot/bp click --text "Add to Cart" --viewport-only --type button
Common Patterns
Form Filling
Direct mode (fast but brittle):
node .browser-pilot/bp fill -s "#email" -v "user@example.com"
node .browser-pilot/bp fill -s "#password" -v "secret"
node .browser-pilot/bp click -s "#login-btn"
Smart mode (slower but reliable, map auto-generated on page load):
node .browser-pilot/bp fill --text "Email" -v "user@example.com"
node .browser-pilot/bp fill --text "Password" -v "secret"
node .browser-pilot/bp click --text "Login" --type button
Chain mode (Direct):
node .browser-pilot/bp chain navigate -u <url> fill -s #email -v <email> fill -s #password -v <password> click -s #login-btn
Chain mode (Smart - recommended):
node .browser-pilot/bp chain navigate -u <url> fill --text Email -v <email> fill --text Password -v <password> click --text Login --type button
Note: Chain mode auto-waits for map generation after navigation and adds human-like delays between commands.
Clicking Nth Item
CSS nth-child:
node .browser-pilot/bp click -s "ul.products > li:nth-child(3) button"
XPath indexing:
node .browser-pilot/bp click -s "(//ul[@class='products']//button)[3]"
Smart mode indexing:
node .browser-pilot/bp click --text "Add to Cart" --index 3
Modal Interactions
CSS scoping:
node .browser-pilot/bp click -s ".modal .btn-primary"
node .browser-pilot/bp click -s "#confirm-modal button.submit"
XPath scoping:
node .browser-pilot/bp click -s "//div[@class='modal']//button[contains(text(), 'Confirm')]"
Smart mode:
node .browser-pilot/bp click --text "Confirm" --type button --viewport-only
Dynamic Content
Wait then interact:
node .browser-pilot/bp wait -s ".loading-complete" -t 5000
node .browser-pilot/bp click --text "Load More" --viewport-only
Chain mode:
node .browser-pilot/bp chain wait -s ".loading-complete" -t 5000 click --text "Load More" --viewport-only
Best Practices
1. Prefer Stable Identifiers
Good: Unique ID
node .browser-pilot/bp click -s "#checkout-button"
Good: Data attribute
node .browser-pilot/bp click -s "button[data-testid='submit']"
Avoid: Generated classes
node .browser-pilot/bp click -s ".btn-a7s9d2f" # Likely to change
2. Use Smart Mode for Text-Based UI
Good: Text content is stable
node .browser-pilot/bp click --text "Continue to Checkout"
Avoid: CSS classes for text buttons
node .browser-pilot/bp click -s ".checkout-btn-primary-lg"
3. Scope Selectors When Possible
Good: Scoped to container
node .browser-pilot/bp click -s "#user-menu button.logout"
Avoid: Global selector
node .browser-pilot/bp click -s "button.logout" # May match wrong button
4. Handle Duplicates Explicitly
Good: Specific index
node .browser-pilot/bp click --text "Delete" --index 2
Good: Scoped selector
node .browser-pilot/bp click -s "#product-123 button.delete"
Avoid: Ambiguous selector
node .browser-pilot/bp click --text "Delete" # Which Delete button?
5. Verify Element Visibility
Good: Checks visibility
node .browser-pilot/bp click --text "Submit" --viewport-only
Consider: May be off-screen
node .browser-pilot/bp click --text "Submit"
Troubleshooting
"Element not found"
Solution 1: Use Smart Mode
# Map auto-generates on page load, just use text search
node .browser-pilot/bp click --text "Submit"
Solution 2: Wait for element
node .browser-pilot/bp wait -s "#submit-button" -t 5000
node .browser-pilot/bp click -s "#submit-button"
Solution 3: Check selector in DevTools
// In browser console:
document.querySelector('#submit-button') // CSS
$x("//button[contains(text(), 'Submit')]") // XPath
"Multiple elements match"
Solution 1: Use indexing
node .browser-pilot/bp click --text "Delete" --index 2
Solution 2: Add more specificity
# Before
node .browser-pilot/bp click -s "button"
# After
node .browser-pilot/bp click -s "#product-list button.delete"
Solution 3: Filter by type
node .browser-pilot/bp click --text "Submit" --type button
"Wrong element clicked"
Solution 1: Inspect map
# Check interaction map JSON
cat .browser-pilot/interaction-map.json | jq '.indexes.byText'
# Find element IDs with your text
# Verify positions and types
Solution 2: Use visibility filter
node .browser-pilot/bp click --text "Add to Cart" --viewport-only
Solution 3: Be more specific
# Before
node .browser-pilot/bp click --text "Submit"
# After
node .browser-pilot/bp click --text "Submit Order" --type button
Framework-Specific Tips
React Applications
- Use
data-testidattributes if available - Text-based XPath works well (stable)
- Smart Mode recommended for dynamic classes
- Avoid CSS classes (often generated)
Angular Applications
- Use
ng-attributes if available - Text-based selectors are stable
- Smart Mode recommended
- Avoid dynamic
_ngcontentattributes
Vue Applications
- Use
data-attributes if available - Text-based XPath works well
- Smart Mode recommended
- Avoid scoped CSS classes
Plain HTML
- CSS selectors work well
- IDs and classes are usually stable
- Direct mode is often sufficient
- Use Smart Mode for dynamic content