Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Locators

Locators are the foundation of web automation testing - they tell RocketQA how to find and interact with elements on your web pages. This comprehensive guide covers everything you need to know about creating, organizing, and maintaining effective locators.

Locators are selectors that identify specific elements on web pages. In RocketQA, they’re defined in a YAML file (locators.yml) using CSS selectors, XPath expressions, or text-based selectors.

locators.yml
pageName:
elementName: "selector"
anotherElement: "another-selector"

Used in tests as:

When click $pageName.elementName
Then $pageName.anotherElement is visible

CSS selectors are the most commonly used locator type due to their performance and readability.

basicSelectors:
# By ID (most reliable)
loginButton: "#login-btn"
# By class
errorMessage: ".error-text"
# By attribute
submitButton: "[type='submit']"
# By tag name
mainHeading: "h1"
# Combination
primaryButton: "button.btn-primary"
advancedSelectors:
# Child selectors
firstNavItem: "nav > ul > li:first-child"
# Descendant selectors
footerLinks: "footer a"
# Attribute contains
emailField: "[name*='email']"
# Pseudo-selectors
lastMenuItem: ".menu-item:last-child"
checkedCheckbox: "input[type='checkbox']:checked"
# Multiple conditions
activeNavLink: "nav a.active[href='/dashboard']"

XPath provides powerful traversal capabilities, especially useful for complex element relationships.

xpathSelectors:
# Simple XPath
loginButton: "//button[@id='login']"
# By text content
welcomeMessage: "//h1[text()='Welcome']"
# Contains text
errorMessage: "//*[contains(text(), 'Error occurred')]"
# By position
firstProduct: "(//div[@class='product'])[1]"
advancedXPath:
# Parent traversal
inputLabel: "//input[@name='email']/../label"
# Following sibling
nextButton: "//button[text()='Back']/following-sibling::button"
# Complex conditions
specificUser: "//tr[td[1][text()='John'] and td[2][text()='Doe']]"
# Attribute conditions
enabledButton: "//button[@disabled='false' or not(@disabled)]"
# Multiple text conditions
complexElement: "//*[contains(text(), 'Username') and @class='field-label']"

The most reliable approach is using data-testid attributes specifically for testing.

<!-- HTML with test IDs -->
<button data-testid="submit-form">Submit</button>
<div data-testid="user-profile">Profile Content</div>
<input data-testid="search-input" type="text">
# locators.yml using test IDs
testIdSelectors:
submitButton: "[data-testid='submit-form']"
userProfile: "[data-testid='user-profile']"
searchInput: "[data-testid='search-input']"
# With additional conditions
activeTab: "[data-testid='tab'][aria-selected='true']"

Select elements by their visible text content.

textBasedSelectors:
# Exact text match
loginLink: "text=Login"
# Partial text match
welcomeMessage: "text*=Welcome"
# Case insensitive
submitButton: "text=/submit/i"
# Multiple text options
confirmButton: "text=OK,text=Confirm,text=Yes"

Organize locators by page or view:

homepage:
navigation:
logo: ".site-logo"
menuButton: ".hamburger-menu"
searchBox: "#global-search"
hero:
headline: ".hero-title"
ctaButton: ".hero-cta"
footer:
socialLinks: ".social-links a"
loginPage:
form:
emailField: "#email"
passwordField: "#password"
submitButton: "#login-submit"
links:
forgotPassword: ".forgot-password"
signUp: ".signup-link"
dashboard:
sidebar:
navigation: ".sidebar-nav"
userMenu: ".user-menu"
content:
mainArea: ".dashboard-content"
widgets: ".widget"

Organize by reusable UI components:

components:
modal:
overlay: ".modal-overlay"
container: ".modal-container"
header: ".modal-header"
closeButton: ".modal-close"
content: ".modal-body"
footer: ".modal-footer"
dataTable:
container: ".data-table"
headers: ".table-header th"
rows: ".table-row"
cells: ".table-cell"
pagination: ".pagination"
notification:
container: ".notification"
message: ".notification-message"
icon: ".notification-icon"
closeButton: ".notification-close"

Create nested structures that mirror your application:

ecommerce:
header:
logo: ".header-logo"
navigation:
categories: ".category-nav"
searchBox: ".search-input"
cartIcon: ".cart-icon"
userActions:
login: ".login-link"
profile: ".profile-dropdown"
productCatalog:
filters:
container: ".filters-panel"
category: ".filter-category"
price: ".filter-price"
brand: ".filter-brand"
products:
grid: ".products-grid"
card: ".product-card"
image: ".product-image"
title: ".product-title"
price: ".product-price"
checkout:
steps:
shipping: ".checkout-shipping"
payment: ".checkout-payment"
review: ".checkout-review"
forms:
billingAddress: ".billing-form"
shippingAddress: ".shipping-form"
paymentMethod: ".payment-form"
  1. Test-specific attributes: [data-testid='element']
  2. Semantic attributes: [aria-label='Close dialog']
  3. Stable IDs: #user-profile
  4. Stable classes: .navigation-menu
  5. Structural CSS: nav > ul > li
  6. XPath with text: //button[text()='Submit']
  7. Complex CSS: .container:nth-child(3) > div
  8. Brittle selectors: Avoid generated classes, positions
# Good: Stable and meaningful
goodSelectors:
submitButton: "[data-testid='submit-form']"
userProfile: "#user-profile-section"
navigationMenu: "[role='navigation']"
errorMessage: ".validation-error"
# Avoid: Fragile and likely to break
avoidThese:
fragileButton: ".css-1a2b3c4" # Generated class
positionBased: "div:nth-child(5) > span:nth-child(2)"
textOnly: "Submit" # Text might change
complexPath: "html > body > div > div > main > section > div > button"

Use clear, descriptive names that reflect the element’s purpose:

namingExamples:
# Good: Descriptive and clear
loginSubmitButton: "#login-form-submit"
userProfileDropdown: ".user-profile-menu"
productSearchInput: "[name='product-search']"
checkoutTotalPrice: ".checkout-summary-total"
# Avoid: Vague or unclear
btn1: "#btn-1"
field: ".input"
element: ".element"
thing: ".some-class"

Test selectors in browser console:

// Test CSS selectors
document.querySelector('#login-button')
document.querySelectorAll('.product-card')
// Test XPath
$x("//button[text()='Submit']")
$x("//div[@class='user-profile']")
// Check element properties
const element = document.querySelector('#user-name');
console.log(element.textContent);
console.log(element.getAttribute('data-testid'));
# Problem: Element not found
problematicSelector: ".dynamic-class-123"
# Solutions:
solutions:
useTestId: "[data-testid='stable-identifier']"
useRole: "[role='button'][aria-label='Submit']"
useStructural: "form .submit-button"
waitForElement: ".appears-after-loading"
# Problem: Selector matches multiple elements
ambiguousSelector: ".button"
# Solutions:
specificSelectors:
firstMatch: ".button:first-of-type"
byContext: ".login-form .button"
byText: "button:has-text('Login')"
byPosition: "(.button)[1]" # XPath
# Problem: Content changes dynamically
dynamicContent:
# Use partial matches
notification: "[class*='notification']"
# Use stable attributes
userInfo: "[data-user-id]"
# Use structural relationships
siblingElement: ".username + .user-role"

Mastering locators enables you to:

  • ✅ Create reliable, maintainable element selectors
  • ✅ Organize locators for team collaboration
  • ✅ Handle dynamic and complex web applications
  • ✅ Debug and troubleshoot element selection issues

Continue with:

  • Test Files - Advanced feature file techniques and patterns

🎯 Locator Master! You now have the knowledge to create robust, maintainable locators for any web application. Next, let’s explore advanced test file techniques!