7

Dear Selenium Webdriver Experts,

I am wondering whether the string matches method in Selenium Webdriver is working properly with the following code snippet in Java:

if (property.findElements(By.xpath("./dl[@class='cN-featDetails']/dd[matches(class,'propertytype type-house']")).size() > 0 ) {    // line 229

Below is the xhtml webpage where line 229 is reading from:

<dl class="cN-featDetails">
<dt class="proptype">Property type</dt>
<dd id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl01_EliteListingTemplate_ddPropertyType" class="propertytype type-house" title="Property type: House">House</dd>

However, this resulted in the following error:

Address: 28B/171 Gloucester Street, Sydney
Exception in thread "main" org.openqa.selenium.InvalidSelectorException: The given selector ./dl[@class='cN-featDetails']/dd[matches(class,'propertytype type-house'] is either invalid or does not result in a WebElement. The following error occurred:
[InvalidSelectorError] Unable to locate an element with the xpath expression ./dl[@class='cN-featDetails']/dd[matches(class,'propertytype type-house'] because of the following error:
[Exception... "The expression is not a legal expression."  code: "51" nsresult: "0x805b0033 (NS_ERROR_DOM_INVALID_EXPRESSION_ERR)"  location: "

I have also tried matches(class,'propertytype.*$']") without success either.

The name of class changes depending on whether the property is a house (type-house) or apartment (type-apartment)…..

Any suggestion on how to use regex in matches to check whether there is value / valid tree node in this property type element?

This code snippet is looking up this URL.

I am using Selenium 2.25.0, Java 1.7.0_11 on Windows XP & 7 platforms.

Your advice would be much appreciated.

Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
George Smith
  • 289
  • 1
  • 8
  • 17

1 Answers1

13

Unfortunately, the matches() function is a part of XPath 2.0.

WebDriver uses the Wicked Good XPath library that only supports XPath 1.0.

Therefore, your XPath expression is illegal and you should rewrite it to only use features and functions from XPath 1.0.

I think you could simply replace the matches() call in your examply with contains(). That said, it's not considered a good practise to match class names via contains(), because type-house would also match type-houses. Also if you match for propertytype type-house and the classes happen to be in different order, they won't get matched. XPath doesn't know anything about classes nor about space-separated lists used in CSS. For more discussion on this, see e.g. this.

You should really use a CSS selector instead:

dl.cN-featDetails > dd.propertytype.type-house
Community
  • 1
  • 1
Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
  • Also, you're missing a `@` infront of `class` in the `matches()` call. – Petr Janeček Jun 08 '13 at 16:05
  • 1
    One way around the contains-matches-too-much problem is to use `contains(concat(' ', @class, ' ') ', type-house ')`. – Ross Patterson Jun 08 '13 at 22:55
  • Yep. Also, the classes-in-different-order problem can be overcome using `contains(@class, "first-class") and contains(@class, "second-class")`. Ideally both chained with the `concat()` and possibly `normalize-space()`. – Petr Janeček Jun 08 '13 at 23:00
  • Thank you to the detail answers from Slanec & Ross Patterson. I use if (property.findElements(By.xpath("./dl[@class='cN-featDetails']/dd[starts-with(@class,'propertytype')]")).size() > 0 ) { because it is much simpler and is working simply by knowing that the class name will not be the other way round - typeproperty. However, I am interested in learning how the equivalent command in CSS but know every little of how it work. Thanks a lot again, Jack – George Smith Jun 10 '13 at 12:31
  • @JackBush `dl.cN-featDetails > dd.propertytype` is almost the equivalelnt of your XPath right there. The only difference is that it matches the `dd` elements that have the `propertytype` class. It will work even if they have some other class, too. To match your XPath exactly, I'd write `dl.cN-featDetails > dd[class^='propertytype']`. [See this page for more info.](http://www.w3.org/TR/selectors/#attribute-selectors) – Petr Janeček Jun 10 '13 at 14:56
  • I am looking for a CSS solution that could do what XPath 2.0 match() could have done, by catering for space between class name. Thanks so much, Jack – George Smith Jun 12 '13 at 12:40
  • @JackBush I don't really get what you're after, the CSS selector I provided in the answer is the best thing you can do imho. But okay, the most literal fit for the XPath in your question is: `dl.cN-featDetails > dd[class*='propertytype type-house']` – Petr Janeček Jun 12 '13 at 17:46
  • @Slanec What happen when class="propertytype type-apartment" or class="propertytype type-unit"... I am looking for a generic CSS selector that will pickup the class that consists of propertytype and the rest. e.g. "^propertytype.*$" including space in between. – George Smith Jun 13 '13 at 12:38
  • @JackBush Shouldn't this be handled by the new [`data`](http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/all-you-need-to-know-about-the-html5-data-attribute/) attributes? Anyway. `dl.cN-featDetails > dd[class*='propertytype type-']` matches what you would expect - any `dd` element with `class` containing `propertytype type-`. – Petr Janeček Jun 18 '13 at 09:50