19

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?

culebrón
  • 34,265
  • 20
  • 72
  • 110

2 Answers2

30

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
  • 240,661
  • 26
  • 293
  • 431
  • I've tried this before: (//input[@name='question'])[2], error = Error: Element (//input[@name='question'])[2] not found – culebrón Jul 30 '10 at 16:46
  • @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 Jul 30 '10 at 17:31
  • @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 Jul 30 '10 at 17:57
  • 2
    Then use Dave Hunt's answer -- this has better chances to be supported by what Selenium calls "XPath". – Dimitre Novatchev Jul 30 '10 at 18:38
  • 6
    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 Jul 30 '10 at 21:46
  • 3
    @Dave-Hunt: So, will `xpath=(//input[@name='question'])[7]` then be acceptable? – Dimitre Novatchev Jul 31 '10 at 00:55
  • Note that for longer paths the whole path has to wrapped in brackets. E.g. //form[@id='test']//input[@name='question'][7] becomes (//form[@id='test']//input[@name='question'])[7] – Carl Pritchett Feb 01 '13 at 05:20
  • @CarlPritchett, This has nothing to do with the length of the XPath expression, but the true reason is that the XPath `[]` operator has higher precedence (priority) than the Xpath pseudo-operator `//`. By definition the goal of using brackets is to override the default precedence/priority. This is one of the most FAQ in XPath and I have answers where this is explained in detail. For example see this: http://stackoverflow.com/a/3676557/36305 – Dimitre Novatchev Feb 01 '13 at 05:38
  • My answer above works in xpath but I couldn't get it to work in Selenium - I had to use the answer by @DaveHunt below – Carl Pritchett Feb 01 '13 at 06:09
  • @CarlPritchett, It is quite possible that Selenium is a non-compliant (buggy) XPath implementation. – Dimitre Novatchev Feb 01 '13 at 12:50
  • Parentheses! Thank you! I was loosing my mind on this one. – David Sopko Jan 24 '17 at 03:49
6

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
  • 8,191
  • 4
  • 38
  • 39
  • I'm fairly sure that `//` is an exact synonym for `descendant-or-self::`, so I don't think this would help. – AakashM Jul 30 '10 at 13:46
  • 2
    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 Jul 30 '10 at 21:43