1

I have html like

<html>
<body>
  <div class='open'>
    <h1>Title</h1>
    <div>Opened</div>
  </div>
</body>
</html>

And in my Selenium WebDriver 3 tests I am trying to select the div.open element using the following xpath:

//h1/../.[contains(@class, 'open')]

In the following command in c#:

driver.FindElement(By.XPath("//h1/../.[contains(@class, 'open')]"));

Which results in

OpenQA.Selenium.InvalidSelectorException : invalid selector: Unable to locate an element with the xpath expression //h1/../.[contains(@class, 'open')] because of the following error:: Failed to execute 'evaluate' on 'Document': The string '//h1/../.[contains(@class, 'open')]' is not a valid XPath expression.

Searching by the same Xpath in Firefox console successfully locates the element.

Any ideas why WebDriver considers this xpath invalid?

Note: my example is of course simplified

3 Answers 3

3

I would say that's probably down to the specification doesn't explicitly state whether predicate after abbreviated step i.e . and .. should be allowed, neither the specification ever mention an example involving predicate after abbreviated step.

So some XPath implementations just don't support it. One of these, I noticed, is XPath implementation in .NET framework. Other implementations explicitly disallow it, and even provide useful suggestion for the user to replace . or .. in their XPath with the equivalent unabbreviated expression, in order, self::node() or parent::node(), for example, the implementation used by xpathtester. But as you noticed, other implementations might support it.

As of workaround, there are many alternatives XPath which equivalent or close enough to you original attempted XPath. The easiest is to expand the . to its unabbreviated expression, as I mentioned above. Personally, I prefer not to go down the tree and then back up to return a parent element, if possible. Instead, just stop at the parent element which we want to return, and check for the child in predicate :

//*[contains(@class, 'open')][h1]
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot! That's clear to me now. Even though I find this inconsistent from implementation point, as the same lib considers the following path valid "//*[contains(@class, 'open')][h1]" - when * is stated as abbreviation in the specification, too. Anyway great answer, thanks again!
Glad it helped. Side note, * itself isn't abbreviation (it is explicitly mentioned in Name Test section). * mentioned in abbreviated syntax section, I believe, because it selects child elements without specifying the axis i.e * instead of the more verbose expression child::*.
1

If you want to reference "div" with respect to "h1" tag then you can change the xpath to following:

//h1/ancestor::div[contains(@class, 'open')]

2 Comments

I think this is not what OP asking
Thx! If I use parent, instead of ancestor (as ancestor selects more nodes) //h1/parent::div[contains(@class, 'open')], this works, and looks like it does exactly the same. But as Narendra points out, it would be nice if I know the reason WHY my xpath is failing.
0

Instead of //h1/../.[contains(@class, 'open')] xpath can you try //div[@class='open'] xpath

2 Comments

Not really. You can consider I have many <div class='open'>, so this won't work. Also my main question is why this is considered INVALID XPATH.
@Jeni I think if you are stuck, you need to ask a proper Question supported by the relevant work/research you have done along with the Test Environment details & error stack trace. Please update your Question & Details accordingly. Thanks

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.