We use cookies to analyze site traffic and show personalized ads. You can accept all cookies or decline personalized advertising.
Learn more in our Privacy Policy.
Mastering functions, axes, and complex traversals for robust test automation.
In many real-world applications, simple locators like By.id("login") are not enough. Dynamic attributes, repeated labels, and messy HTML force you to use more powerful XPath features.
In this practice lab, you'll learn how to use XPath functions and axes to build more robust and flexible locators.
We'll focus on:
parent:: and following-sibling::.What it does: Matches elements based on their exact visible text.
When to use: When the text is unique and stable (e.g. "Login" button).
Best practice: Use only when you're confident the label won't change often.
What it does: Checks if an attribute or text contains a substring.
When to use: For dynamic attributes like id="submit_123" where "submit" is stable.
Best practice: Combine with other conditions when possible for better uniqueness.
What it does: Matches attributes or text that start with a given prefix.
When to use: For IDs like user_123, user_456, etc.
Best practice: Great for auto-generated IDs with a known prefix.
What it does: Simulates "ends with" by checking for a stable suffix (e.g. "_name").
When to use: When IDs end with a predictable part, like input_name, user_name.
Best practice: Document this clearly in your team, since it's a pattern, not a native function.
What it does: Trims leading/trailing spaces and collapses multiple spaces inside text.
When to use: When the UI has inconsistent whitespace (e.g. "Messy Text").
Best practice: Very useful for labels that come from formatted HTML or CMS content.
What it does: Replaces characters; often used to make case-insensitive comparisons.
When to use: When text casing may change (e.g. UPPERCASE, uppercase, UpperCase).
Best practice: Use it sparingly; it can make XPath expressions longer but powerful.
What it does: Selects an element by its index within a node set (1-based).
When to use: To pick a specific item from a list, like "Item 2".
Best practice: Use only when the relative order is stable.
What it does: Selects the last element in a node set.
When to use: When the number of items changes but you always want the final one.
Best practice: Good for "latest entry" or "last row" patterns.
What it does: Both conditions must be true.
Example: //input[@type='text' and @name='user'].
Best practice: Use to narrow down elements when a single attribute isn't unique.
What it does: At least one condition must be true.
Example: //button[@id='btn-or' or text()='Go'].
Best practice: Use when UI can vary slightly between environments or versions.
What it does: Negates a condition.
Example: //button[not(@disabled)].
Best practice: Useful to pick "enabled" items without relying on a specific class name.
What it does: Moves from a child element up to its immediate parent.
When to use: When the parent has no good attributes, but a child inside it is easy to locate.
What it does: Selects siblings that come after the current node.
When to use: Classic pattern: from a <label> to the <input> next to it.
Parent Div
These interactive elements are designed specifically to practice advanced XPath queries. Click any element to see how different XPath expressions target specific elements.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657// String functions: text(), contains(), starts-with(), normalize-space(), translate()
WebElement loginButton = driver.findElement(By.xpath("//button[text()='Login']"));
loginButton.click();
WebElement submitButton = driver.findElement(By.xpath("//button[contains(text(), 'Sub')]"));
submitButton.click();
WebElement userIdInput = driver.findElement(By.xpath("//input[starts-with(@id, 'user_')]"));
userIdInput.clear();
userIdInput.sendKeys("test-user");
WebElement nameInput = driver.findElement(By.xpath("//input[contains(@id, '_name')]"));
nameInput.clear();
nameInput.sendKeys("Test Name");
WebElement messyTextDiv = driver.findElement(By.xpath("//div[normalize-space()='Messy Text']"));
messyTextDiv.click();
WebElement uppercaseDiv = driver.findElement(By.xpath(
"//div[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='uppercase']"
));
uppercaseDiv.click();
// Node set functions: position(), last()
WebElement secondItem = driver.findElement(By.xpath("//ul[@id='practice-list']/li[position()=2]"));
secondItem.click();
WebElement lastItem = driver.findElement(By.xpath("//ul[@id='practice-list']/li[last()]"));
lastItem.click();
// Boolean operators: and, or, not()
WebElement usernameInput = driver.findElement(By.xpath("//input[@type='text' and @name='user']"));
usernameInput.clear();
usernameInput.sendKeys("AdminUser");
WebElement goButton = driver.findElement(By.xpath("//button[@id='btn-or' or text()='Go']"));
goButton.click();
WebElement enabledButton = driver.findElement(
By.xpath("//div[@id='not']//button[not(@disabled)]")
);
enabledButton.click();
// Axes: parent::, following-sibling::
WebElement parentDiv = driver.findElement(
By.xpath("//button[@id='child-btn']/parent::div")
);
parentDiv.click();
WebElement siblingInput = driver.findElement(
By.xpath("//label[@id='lbl-sibling']/following-sibling::input")
);
siblingInput.clear();
siblingInput.sendKeys("Sibling Found");
// End of interactions
driver.quit();text() to locate the button whose visible text is exactly "Login".contains() on the button text to match "Submit Form" without relying on the full label.starts-with() on the id attribute; this will match inputs like user_123, user_456, etc.id contains the suffix "_name".normalize-space() so the locator works even if the HTML contains irregular spacing such as "Messy Text".translate() to convert the text to lowercase before comparison, making the match effectively case-insensitive.<li> inside the list with id="practice-list" using position().<li> in the same list, no matter how many items are present.and, matching only the text input whose type is "text" and name is "user".or so the button is found if either the id is "btn-or" or the visible text is "Go".id="not" by explicitly excluding elements with a disabled attribute.child-btn and moves up one level with parent::div to get the wrapping container.id="lbl-sibling" and selects the input that sits next to it using the following-sibling:: axis.Start with simple locators (id, name, data-testid) and only move to advanced XPath when necessary. XPath is powerful but easy to overcomplicate.
When you do use advanced XPath, keep expressions small and readable. If you can't explain your XPath in one sentence, it may be too complex.
Treat functions like contains(), starts-with(), and normalize-space() as tools for stability, not shortcuts. Use them deliberately to handle dynamic or messy DOMs.