views:

102

answers:

2

I try to write xpath expressions so that my tests won't be broken by small design changes. So instead of the expressions that Selenium IDE generates, I write my own.

Here's an issue:

//input[@name='question'][7]

This expression doesn't work at all. Input nodes named 'question' are spread across the page. They're not siblings.

I've tried using intermediate expression, but it also fails.

(//input[@name='question'])[2]
error = Error: Element (//input[@name='question'])[2] not found

That's why I suppose Seleniun has a wrong implementation of XPath.

According to XPath docs, the position predicate must filter by the position in the nodeset, so it must find the seventh input with the name 'question'. In Selenium this doesn't work. CSS selectors (:nth-of-kind) neither.

I had to write an expression that filters their common parents:

//*[contains(@class, 'question_section')][7]//input[@name='question']

Is this a Selenium specific issue, or I'm reading the specs wrong way? What can I do to make a shorter expression?

+4  A: 

If you want the 7th input with name attribute with a value of question in the source then try the following:

/descendant::input[@name='question'][7]
Dave Hunt
I'm fairly sure that `//` is an exact synonym for `descendant-or-self::`, so I don't think this would help.
AakashM
You are correct, but `descendant-or-self` is not a direct synonym for `descendant`. See http://www.w3.org/TR/xpath/#path-abbrev"NOTE: The location path `//para[1]` does not mean the same as the location path `/descendant::para[1]`. The latter selects the first descendant `para` element; the former selects all descendant `para` elements that are the first `para` children of their parents."
Dave Hunt
Correct answe (+1).
Dimitre Novatchev
+2  A: 

Here's an issue:

//input[@name='question'][7]   

This expression doesn't work at all.

This is a FAQ.

[] has a higher priority than //.

The above expression selects every input element with @name = 'question', which is the 7th child of its parent -- and aparently the parents of input elements in the document that is not shown don't have so many input children.

Use (note the brackets):

(//input[@name='question'])[7]

This selects the 7th element input in the document that satisfies the conditions in the predicate.

Edit:

People, who know Selenium (Dave Hunt) suggest that the above expression is written in Selenium as:

xpath=(//input[@name='question'])[7]
Dimitre Novatchev
I've tried this before: (//input[@name='question'])[2], error = Error: Element (//input[@name='question'])[2] not found
culebrón
@culebrón: Probably this is how Selenium reacts when an XPath expression doesn't select anything. No node will be selected by this XPath expression if in the document there are less than two `input` elements with attr. `name` with value `'question'`
Dimitre Novatchev
@Dimitre: no. Here's an intermediate expression: [error] locator not found: (//input[@name='question']), error = Error: Element (//input[@name='question']) not found. Works without parenthesis.
culebrón
Then use Dave Hunt's answer -- this has better chances to be supported by what Selenium calls "XPath".
Dimitre Novatchev
Note that Selenium will only interpret a locator as XPath if it starts with `//` or `xpath=` so starting with a `(` will default to attempt locating the element by identifier (`id` or `name`).
Dave Hunt
@Dave-Hunt: So, will `xpath=(//input[@name='question'])[7]` then be acceptable?
Dimitre Novatchev
Yes, I suspect that would work in Selenium.
Dave Hunt