How to use findElemenFromElement with xpath

Please help me. I tried a long time. Thank you in advance! :slight_smile:

How to use findElemenFromElement with xpath to get the grrand parent of a found element?

I am using the webdriverio Javascript driver. But the behaviour should be client agnostic.

const allImages = await driver.$$('~FlowListImage');
const image = allImages[0];
console.log("-->>> image", image);

// below are 4 trials but I think the “//…” with elementID should be used.
//const imageGrandMother = await driver.findElementFromElement(ref, ‘xpath’, ‘…/…/');
//const imageGrandMother = await driver.findElementFromElement(ref, ‘xpath’, '//…/…/
’);
//const imageGrandMother = await driver.findElementFromElement(image.elementId, ‘xpath’, ‘…/…/');
const imageGrandMother = await driver.findElementFromElement(image.elementId, ‘xpath’, '//…/…/
’);
console.log(“–>>> imageGrandMother”, imageGrandMother);

Note that this doc says maybe one should use the reference to the element and not the elementId. I have tried it too.
https://webdriver.io/docs/api/browser/$.html

Here is how the image element looks like with console.log

–>>> image Element {
sessionId: ‘60ae8dcf-c4bc-4887-a5d3-24f1cb2dbd77’,
elementId: ‘ce183094-1403-49b7-b94c-a7dd54c44778’,
‘element-6066-11e4-a52e-4f735466cecf’: ‘ce183094-1403-49b7-b94c-a7dd54c44778’,
selector: ‘~FlowListImage’,
parent:
Browser {

ANd below is the error.

2020-01-17T16:13:59.630Z INFO webdriver: COMMAND findElementFromElement(“48e66342-c6ca-493d-88ce-4747ddf700c8”, “xpath”, "//…/…/")
2020-01-17T16:13:59.630Z INFO webdriver: [POST] http://localhost:4723/wd/hub/session/9a9b44c2-25d7-4842-8674-6451a0c468df/element/48e66342-c6ca-493d-88ce-4747ddf700c8/element
2020-01-17T16:13:59.630Z INFO webdriver: DATA { using: ‘xpath’, value: '//…/…/
’ }
2020-01-17T16:14:09.846Z INFO webdriver: RESULT { error: ‘no such element’,
message:
‘An element could not be located on the page using the given search parameters.’,

I have read your link. So xpath on an element only has access to the descendents, not the parent.

What alternative can I use?

If I have an element, how do I get its parent?

There is no clear thread about this. Here they say one must fetch the absolute xpath of the element and then add “/…”. But how can I get the absolute xpath of a known element?

How to locate to parent node?

It seems to me that if appium does not support relative xpaths, it is a design choice, maybe motivated by techinical challenges. Which difficulty? You need to decide if it is a missing feature or a design choice.

In the w3c specification for webdriver, there is no statement that xpath on an element should only have access to children, and not to the parent.

And the xpath spec allows relative paths:
https://www.w3.org/TR/1999/REC-xpath-19991116/

And a relative xpath is standard in web selenium (the json wire protocol is deprecated, and only the w3c spec is “official”):

JsonWireProtocol · SeleniumHQ/selenium Wiki · GitHub

Appium metions in its docs the axiom 1 that one should not modify the application to test it. But if xpath is too limited, one must modify the application to add accessibility ids, sometimes in a tricky way with dynamic ones in lists.

When I use selenium in a web brother, then the axiom stands 100% since the selectors a very powerful. Appium does not have css selectors, it has other types of selectors.

In the android UIAutomotar API for a selector, there is content description, resource, id, text, etc, but I could not find xpath. SO maybe the native test framework does not support relative xpath on a particular element.

But you can specifiy has a certain descendant and the depth, so you can find the parent of an element.

UiSelector  |  Android Developers
BySelector  |  Android Developers

It seems to me there are 2 solutions to my problem.

  1. Get the absolute xpath (how?) and add /… to it.
  2. Find a parent bu cheking if an element has a certain descendant. This could be done manually in the test suite. I don’t see how but one can list all elements and then check equality wit hthe child reference. IT doses not seem simple and efficient.

So it order to try something, I need to find a way to know the xpath of a known element. And then I add … to it.

Maybe we could find an acceptable solution if you properly describe describe your task.

For the example above when we have a cell identified by FlowListImage and would like to find its dierct parent the following xpath could be used (pseudocode):

//*[@class="Table" and ./*[@class="Cell" and @id="FlowListImage"]]

in case we 'd like to make the task more complicated and we also have the concrete cell index (let say 2) then

//*[@class="Table" and ./*[@class="Cell" and @id="FlowListImage"][2]]

OK thanks.

On what does the [2] apply to in your example? To the table? There is no need of parentheses? Or is there a typo (or an intentional exercise for me) and should it be ][2]?

I found that my solution cannot work since I cannot fetch the xpath from a known element.

With your solution, I cannot use class because it is platform specific, I would need one for each platform.

Could this work?

//*[./*[@content-desc="FlowListImage"]]

I tried and it found an element.

I could add an accessibility id on the grand parent element. But the axiom 1 of appium doc says one should not modify the application to test it.

I tried this below but it does not work. No element can be found although I see it in the appium-desktop tree. It is hard to know which element I get because appium-desktop generates new element ids every time I update the appium-desktop view.

The ground mother is found (well an element is found but maybe not the right one). But then I cannot search in it for a certain accessibility id, nothing is found.

const imageGrandMother = await driver.$('//*[./*/*[@content-desc="FlowListImage"]]'); console.log("-->>> imageGrandMother", imageGrandMother); const imageLabel = await driver.findElementFromElement(imageGrandMother.elementId, 'accessibility id', 'FlowListImageLabel');

EDIT: this below worked, so maybe it will suit me. I am just not sure what gran mother child I got. But I tested if the grand child I search for is not visible then the query blocks if it is present then the query works so maybe it suits me.
const imageLabel = await driver.findElementFromElement(imageGrandMother.elementId, 'xpath', '//*[@content-desc="FlowListImageLabel"]');

nope it stopped working. THe element is there when I tried and my selector cannot find it from the grand mother node. It seems to me that it picks a grand mother mother node but for any of the multiple FlowListImage present, and not for the first one in the tree.

Most rational explanation why sometimes it works and sometimes not.

But I can move forward with a new accessibility id. It seems by trial to pick a grand mother does not work all the time even if sometimes it works. the .// is too strange for appium.

@MagicPoulp Just curious to know details regarding your statement
“I could add an accessibility id on the grand parent element. But the axiom 1 of appium doc says one should not modify the application to test it.”

Can you please provide exact link where it’s mentioned in appium documentation that 'one should not modify the application to test it."

here is the exact quote:

“1. You shouldn’t have to recompile your app or modify it in any way in order to automate it.”

http://appium.io/docs/en/about-appium/intro/?lang=en

But I think the principle (not axiom) is too radical. Adding a few automation ids is acceptable. The excessive time it took me to try and fail to make the relative access to the grand mother work was not really worth it if a new accessibility id solves the problem very fast. I saw it work once using the relative path with many stars. Then it stopped working. But I am not totally sure maybe I have a wrong code.

Since appium strongly discourage xpaths in the doc, (due to native limitations), it is not really a good direction to solve the relative xpath when an new id is good enough.

There is big issue to make dynamic ids in dynamic lists. BUt it is not needed if you put an id on the parent, and then you fetch all children and you pick a certain index. the ordering follows the tree. Hence you don’t need ids for children of a parent.

Many rules have exceptions. HEre is a minimal one.

Maybe you could update the principle to say “unless for new accessibility ids”

@MagicPoulp Thanks for your response.