'Appium - Xpath to get direct children not working on Android
For some reason, I am not able to get all direct children of an element on Android. Apparently, the XPath does not work correctly when searching subelements in an element or the XPath scope is not correctly implemented in Appium.
parentElement.findElements(By.xpath(...))
What I tried:
*
./*
.//*
.//child::*
./child::*
//*
//child::*
My results are either zero, all the nodes (even outside the parent element) or all descdentants.
Only thing that worked to me was this:
.//*[@resource-id='RESOURCE-ID-OF-PARENT']/child::*
But this is very poor workaround, because there might be multiple elements with the same resources ID (FYI I am coding a wrapper based on Appium)
I tried to search all the answers here, but couldn't figure out any working solution.
Any help welcomed, thanks.
Solution 1:[1]
I found some kind of workaround:
1) Write a function that compares two elements. Since provided equals method does not work for native elements (Android, iOS), own implementation must be written. I am comparing the most relevant attributes like class, resource ID and content description as well as size, location and bounds. That should work in 99% cases, unless there are two same overlapping elements present.
2) Query an xpath of the parent element (findElementsByXpath
) with most accurate attributes - tagName, class, resource ID and contentDescription. In most cases, there will be only one element, but if there are more, browse the list and use comparison based on the equality function.
3) Mark down indexes of elements in the list and whenever the match is found, run another query in this format:
(//tagName[@class=... and @resource-id= ...etc...])[index]/child::*
Remember that indexing in Xpath starts with 1. The result list are the actual children of the parent element.
The workaround is not the fastest, but still better than nothing.
Solution 2:[2]
This is purely from experimentation, but it seems the xpath handling in Appium does not actually start with the selected node as a root (as you would expect), but with a fake node instead that only contains that current node as a child (and then its tree underneath)
I tried the following: .//*/child::*
and that seemed to work for me.
Solution 3:[3]
It's like Joeri Hendrickx said. The real node and it's tree is hiding under a fake node.
Just try to print all the descendants of your element; you'll see all the descendants (obviously) and also one which has the same id as your element.
His method is tried and it works; for me /*/*
also works.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Marek Teuchner |
Solution 2 | Joeri Hendrickx |
Solution 3 | Dragos Losonti |