Today I was having some trouble coming up with a Selenium XPath locator for a deeply nested selector with a dynamically generated ID. To make matters worse, there were no “near by” logical elements that this element was tied to that had an easy locator. The only clear thing I had was the label for the option I wanted to select. Not wanting to leave the test w/ an extremely brittle, not to mention long and unreadable, xpath locator … I remembered that I had solved this problem before.
Several times now I have wanted to use an XPath expression to find an element by its child element. Queue XPath axes.
Since I have forgotten the syntax and usage of axes several time nows, here I post the solution for posterities sake. If anyone has a better solution to this issue, please let me know … but this seemed to work well enough.
XPath axes allow you to specify a “location step” from a[ny] specified element. What does this mean? We can select elements based on there location relative to another, and NOT just contained within it. They are specified by an “axis specifier” followed by :: and an element selector. Example:
In order to select a SELECT element by one of its OPTIONs:
//OPTION[text() = 'Choice Text']/parent::SELECT
This XPath selects the SELECT element which is the parent of any OPTION elements containing the text node with a value of ‘Choice Text’. While not as simple as a locator directly to the desired element based on its ID, this expression is composed only of elements we actually care about with regard to the test (the option we want and the selector its in) and is more readable than a 50+ character expression going back to the root element and containing numerous indexes.
XPath axes can select a number of things including an elements children, ancestors, siblings (preceding and following), etc.
Also, at least in XPather, an expression that would otherwise return the same element more than once through an axis, only returns the unique list of elements (e.g. //div/ancestor::* will return the root element only once and not once per DIV found)