Selectors
TestFlowKit uses a smart element detection system that tries multiple selectors until it finds a match. This makes your tests more resilient to UI changes.
How Selectors Work
When you reference an element in a test step:
When the user clicks the "submit_button" button
TestFlowKit:
- Looks up "submit_button" in your configuration
- Tries each selector in order until one matches
- Performs the action on the matched element
Defining Selectors
Elements must be grouped by page or feature context. Groups can be common for shared elements, or match your page names.
Single Selector
For elements with reliable, unique selectors:
frontend:
elements:
common:
header: "#main-header"
footer: ".site-footer"
Multiple Fallback Selectors
For resilience, provide multiple selectors that TestFlowKit will try in parallel:
frontend:
elements:
login_page:
login_button:
- "[data-testid='login']" # Best: Test ID
- "#login-btn" # Good: ID
- "button.login-button" # OK: Class
- "button[type='submit']" # Fallback: Attribute
Selector Types
CSS Selectors
The most common and recommended selector type:
frontend:
elements:
common:
# By ID
header: "#main-header"
# By class
nav: ".navigation"
# By data attribute (recommended)
login_btn: "[data-testid='login']"
# Combination
primary_btn: "button.btn.btn-primary"
# Descendant
nav_link: "nav.main a.nav-link"
# Child
menu_item: "ul.menu > li"
login_page:
# By attribute
email_field: "[name='email']"
submit_btn: "[type='submit']"
XPath Selectors
For complex element location, prefix with xpath::
frontend:
elements:
common:
# Text content matching
submit_by_text: "xpath://button[contains(text(), 'Submit')]"
# Position-based
first_row: "xpath://table/tbody/tr[1]"
last_item: "xpath://ul/li[last()]"
form_page:
# Parent navigation
input_parent: "xpath://input[@id='email']/parent::div"
# Following sibling
error_for_email: "xpath://input[@id='email']/following-sibling::span[@class='error']"
# Complex conditions
active_user: "xpath://tr[contains(@class, 'active') and .//td[text()='Admin']]"
When to Use XPath
Use XPath when you need to:
- Match elements by text content
- Navigate to parent or sibling elements
- Use complex conditional logic
- Access elements by position
⚠️ XPath Performance: CSS selectors are generally faster than XPath. Use XPath only when CSS selectors aren't sufficient.
Selector Best Practices
1. Prefer Test IDs
Work with your development team to add data-testid attributes:
<button data-testid="submit-form">Submit</button>
frontend:
elements:
form_page:
submit_btn: "[data-testid='submit-form']"
Benefits:
- Won't change with styling updates
- Clear purpose (for testing)
- Easy to locate in code
2. Avoid Brittle Selectors
Good:
frontend:
elements:
form_page:
submit_btn:
- "[data-testid='submit']"
- "#submit-btn"
- "[type='submit']"
Bad:
frontend:
elements:
form_page:
submit_btn: "div.container > form > div:nth-child(3) > button"
3. Use Semantic Selectors
Prefer selectors that reflect the element's purpose:
frontend:
elements:
common:
# Good: Role-based
search_input: "[role='search'] input"
navigation: "nav[aria-label='Main']"
# Good: Semantic HTML
main_heading: "main h1"
article_content: "article.post"
4. Order by Reliability
List selectors from most to least reliable:
frontend:
elements:
login_page:
email_input:
- "[data-testid='email']" # 1. Test ID (most reliable)
- "#email" # 2. ID
- "[name='email']" # 3. Name attribute
- "input[type='email']" # 4. Type (least reliable)
Dynamic Selectors
For elements with dynamic content, use variables:
# Store a dynamic value
When I store the "user_123" into "user_id" variable
# Use in selector (if supported)
Then the "[data-user-id='{{user_id}}']" should be visible
Debugging Selectors
Validate in Browser
- Open browser DevTools (F12)
- Go to Console
- Test CSS selectors:
document.querySelector('#my-element') document.querySelectorAll('.my-class') - Test XPath:
$x("//button[text()='Submit']")
Slow Motion Mode
Run tests with slow motion to see element detection:
settings:
think_time: 500 # 500ms delay between actions
Or via command line:
tkit run --think-time 500
Verbose Logging
Enable verbose logging to see which selectors are tried:
tkit run --verbose
Common Selector Patterns
Forms
frontend:
elements:
login_page:
# Input fields
email_field: "[name='email']"
password_field: "[name='password']"
# Checkboxes
remember_me: "[name='remember']"
terms_checkbox: "#accept-terms"
# Submit buttons
login_btn: "form button[type='submit']"
registration_page:
# Select dropdowns
country_select: "select[name='country']"
Tables
frontend:
elements:
table_page:
# Table structure
data_table: "table.data-grid"
table_header: "table.data-grid thead"
table_body: "table.data-grid tbody"
# Specific rows
first_row: "table.data-grid tbody tr:first-child"
last_row: "table.data-grid tbody tr:last-child"
Modals and Dialogs
frontend:
elements:
common:
modal: "[role='dialog']"
modal_title: "[role='dialog'] h2"
modal_close: "[role='dialog'] button[aria-label='Close']"
modal_confirm: "[role='dialog'] button.btn-primary"
Navigation
frontend:
elements:
common:
main_nav: "nav[aria-label='Main navigation']"
nav_home: "nav a[href='/']"
nav_about: "nav a[href='/about']"
mobile_menu_toggle: "button[aria-label='Toggle menu']"
Next Steps
- Variables — Dynamic data in tests
- Frontend Testing — Browser automation features
- Step Definitions — Available test steps