AND operator in complicated XPATH

Hi!

I would like to ask for your help with element location using AND operator.
There are two conditions that I need to use via AND operator

//td[contains(@data-label,'Location')]/div[string-length() > 0] 
//td//div[contains(text(),'Storage')]

I tried:

//td[contains(@data-label,'Location')]/div[string-length() > 0][//div[contains(text(),'Storage')]]

//td[[contains(@data-label,'Location')]/div[string-length() > 0] and ./div[contains(text(),'Storage')]]

but there is something wrong. Please advise.

Thank you in advance for your answers!

2 Likes

Converting/combining your original xpath’s

//div[(./ancestor::td[contains(@data-label, 'Location')] and string-length() > 0) or (./ancestor::td and contains(text(), 'Storage'))]

I’m not too sure about your use of the string-length() call, that’s a pretty uncommon function to use in xpath. I’d bet theres a better way to say what you’re trying to say. If you can share an example of the HTML for both of the element sets you are targeting, we could probably clean that up. Otherwise, if the above works for you, cheers!

2 Likes

Another way to do an “AND” is to just put your individual attributes within square brackets without using the AND, like:
//div[string-length() > 0][contains(text(),'Storage')]
is equivalent to:
//div[string-length() > 0 and contains(text(),'Storage')]

2 Likes

True, adjacent predicates are treated as AND. The only reason I didn’t do it this way was the additional predicate they had for one of the parent td nodes (//td[contains(@data-label,‘Location’)]). But this way would be much simpler if that is not actually a requirement.

2 Likes

Also, question for the OP, is the goal for this combined xpath to return a set of elements that match both of those xpaths? If so, you’re not actually wanting an AND, but rather an OR, which is what I put in my original solution. I.e. if you want a broader set of elements to be returned, use OR, but if you truly want an AND, then use @grylion54’s recommended solution(s).

2 Likes

@Brandon_Hein @grylion54 thank you for your answers.
I have to admit that I can’t achieve an expected result applying the provided solutions

I will clarify the problem.

there is a table
I need locate rows (//tr) that
has not blank Location field AND has Refurbishment Status = ‘Storage’ (please review the picture below)

Location and Refurbishment Status are sibling elements. I need to specify condition for this elements but I need to locate their parent element (//tr)

Thank you in advance for your help!

1 Like

I made a HTML as testbed.

<html>
  <head>

  </head>
  <body>
    <table id="mytbl">
      <tbody>
        <tr>
          <td @data-label="foo"><div>bar</div></td>
          <td @data-label="Refurbishment Status"><div>Storage</div></td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

The following test case targets the above mentioned HTML, can find a list of <tr> as you wanted to.

import java.nio.file.Path
import java.nio.file.Paths
import org.openqa.selenium.WebElement
import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

TestObject makeTestObject(String id, String xpath) {
	TestObject tObj = new TestObject(id)
	tObj.addProperty("xpath", ConditionType.EQUALS, xpath)
	return tObj
}
Path page = Paths.get("page.html")
WebUI.openBrowser(page.toFile().toURI().toURL().toExternalForm())

String expr = """
//table[@id='mytbl']
  /tbody
    /tr[td[contains(@data-label,'foo') and string-length(.) > 0] 
        and 
        td[contains(@data-label,'Refurbishment Status') and contains(normalize-space(.), 'Storage')]
       ]
"""

List<WebElement> elements = WebUI.findWebElement(makeTestObject("FOO", expr), 10)
assert elements != null
assert elements.size() > 0
WebUI.closeBrowser()
2 Likes

Thank you for the info. So you’re expecting the row element back, not the div elements? Based on your original attempts at this, you were targeting divs. Try this instead:

//tr[.//td[contains(@class, 'umn-location_name') and ./div[string-length(text()) > 0]] and .//td[contains(@class, 'umn-refurbishment_status') and ./div[normalize-space()='Storage']]]
1 Like