Xpath syntax error when I use `contains` for `classname` and `text` attributes

What I am trying to do is to get all <checkBox>es, which belong to a <div>, which <div>s classname contains the string “something” and it’s text contains the string of a GlobalVariable.

To achieve this I am trying something like this:

WebDriver driver = DriverFactory.getWebDriver()

List<WebElement> wElements = []

wElements = driver.findElements(By.xpath('//div[contains(@class, "something" and contains(text(), '+GlobalVariable.somethingElse+'))]/div[@class="datagrid-cell cell-checkbox"]/label[@class="checkbox"]'))

for(WebElement element in wElements) {
	String elementText = element.getText()
	println(elementText)
}

And I get this syntax error:

org.openqa.selenium.InvalidSelectorException: invalid selector: Unable to locate an element with the xpath expression //div[contains(@class, "something" and contains(text(), Something Else))]/div[@class="datagrid-cell cell-checkbox"]/label[@class="checkbox"] because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//div[contains(@class, "something" and contains(text(), Something Else))]/div[@class="datagrid-cell cell-checkbox"]/label[@class="checkbox"]' is not a valid XPath expression.

Now, if I remove the part and contains(text(), '+GlobalVariable.somethingElse+') the test case ends successfully. So the syntax error should be here.

I have read this document and this article, and I have try some things like to type = instead of , after text() or to replace text(), with ., or to make it like this:

'//div[contains(@class, "datagrid-row song")][contains(text(), '+GlobalVariable.somethingElse+')]/div[@class="datagrid-cell cell-checkbox"]/label[@class="checkbox"]'

…but, nothing!!! I can’t understand what am I doing wrong here. If someone can see something…

Thank you for your time!!!

─[ EDIT 1]─────

After I changed:
[contains(text(), '+GlobalVariable.somethingElse+')]
To:
[contains(text(), "'+GlobalVariable.somethingElse+'")]
…I am not getting any errors but neither a result!!! So, something should be wrong with the text attribute of this elements now. Because the text I type into [contains(text(), '+GlobalVariable.somethingElse+')] exists for sure…

─[ EDIT 2]─────

To check if the outer div prints it’s text I try this:

WebDriver driver = DriverFactory.getWebDriver()
List<WebElement> wElements = []
wElements = driver.findElements(By.xpath('//div[contains(@class, "datagrid-row song")]'))
for(WebElement element in wElements) {
	print(element.text)
}

And I get the text in three different lines like this!!!:

2022-02-22 13:34:34.681 DEBUG testcase.*** - 1: println(text)
18
La processione degli equinoziKeplero's House
05:59

Could this be the reason why it can’t find the text I am asking?

wrong:

you need to change it to

wElements = 
  driver.findElements(By.xpath('//div[contains(@class, "something") and ...

one ) is missing

So you mean something like this?

wElements = driver.findElements(By.xpath('//div[contains(@class, "datagrid-row song") and contains(text(), "'+GlobalVariable.artistName+'")]'))

Instead of this:

wElements = driver.findElements(By.xpath('//div[contains(@class, "datagrid-row song" and contains(text(), "'+GlobalVariable.artistName+'"))]'))

Because both working but don’t return results…

Yes.

The latter one looks wrong to me.

I do not understand what you mean by saying “working”.

I think you mean your code ran without “syntax error”.

If a XPath does not return a meaningful resutl, I would say, it is not working.

If the former statement does not return a meaningful result, it just means it has more problems to solve.

Yes sorry this is what I mean… It runs without syntax error!!!

Can you please take a look at my 2nd edit and tell me what you think about this?

No, I can not verify any XPath expression without target HTML source code exposed.

Only you can do verify XPath against your target HTML.

This xpath expression would fail

//div[contains(@class, "datagrid-row song") ...

You should change this to

//div[contains(@class, "datagrid-row") and contains(@class, "song") ...

Because your HTML source can be either of

<div class="detagridrow song">

or

<div class="song detagridrow">

Oh… So when an element has more than one class names I should use it like this?

I changed it, no errors, no results!!!

Yes, you should if each class name is significant to identify each HTML element. You can ignore insignificant class names, of course.

I don’t see why.

It’s up to you as you haven’t exposed your target HTML.

And in fact, I do not like to see it. Debugging locators is a task that could be resolved only by TRY-ERROR-TRY AGAIN.

I hope you do not ask others in the remote Forum for help about XPath/CSS Selectors that returns no result. That sort of question just annoys others who can not really help.

Is it normal that returns it’s text in three different lines and not in one?

I do not see what you mean.

Take a look at my 2nd edit please…

Possibly, the target HTML contains \n characters

  • between 18 \n La processione ...
  • between House \n 05

You want to use normalize-space() function of XPath.

1 Like

You want to change this

//div[contains(@class, "datagrid-row song")]

to

normalize-space(//div[contains(@class, "datagrid-row") and contains(@class, "song")])

Then you will get a single line

18 La processione degli equinoziKeplero's House 05:59

Consecutive whitespaces (including \n) between words will be replaced to a single white space character by normalize-space() function. Leading and trailing whitespaces will be trimmed.

It gives me error…

wElements = driver.findElements(By.xpath('normalize-space(//div[contains(@class, "datagrid-row") and contains(@class, "song")])'))
Reason:
org.openqa.selenium.InvalidSelectorException: Given xpath expression "normalize-space(//div[contains(@class, "datagrid-row") and contains(@class, "song")])" is invalid: TypeError: Document.evaluate: Result type mismatch

Ah, I was wrong.

you can not use normalize-space() in the XPath inside fineElement(By.xpath(...)).

By.xpath(…) requires a XPath expression that retuns Node-Set type. But normalize-space() cast a NodeSet to a String.

You have got 3 WebElement instances. Therefore you got 3 lines.

I do not see any more reasonings as I do not see the target HTML.