I'm new to Selenium and have searched for a solution for this, but cannot find one.
I am trying to drill down into a nav containing a collapsable directory tree. The folder nodes' content starts as an empty div that is populated when their "+" icon is clicked. The problem is that I cannot find anything within the newly populated div. I am using find_element_by_link_text() and have tried find_element_by_partial_link_text() with a fragment. I've even done a find_elements_by_tag_name() and the new elements (anchors) are not listed with those that exist outside of the new div.
The entire nav is contained within an iframe - is there a switch_to method that would apply?
Here is a sanitized example of what the HTML looks like:
<iframe id="KnownID" ...>
<html ...>
<body ...>
<div id="unknown_id_first_subtree"> ... </div>
<div id="unknown_id_nth_subtree">
<div id="unknown_a">
<table> <tbody> <tr> <td width="however wide the indent"></td>
<td>
<!-- Child nodes begin here -->
<div>
<table> <tbody> <tr>
<td class="treeCell">
<a id="unknown_id"> this toggles the tree node display </a>
<a id="unknown_id">This is the text I know</a>
</td> </tr> </tbody>
</table>
</div>
<div id="subtreeContainer_unknown_id">
<!-- Empty place holder to hold expanded content -->
</div>
<!-- Repeat above pattern for each tree entry -->
</td>
</tr> </tbody> </table>
</div>
</div> ... closes out the nth_subtree
repeats
The empty place holder is populated when the node is expanded. I do not know any of the ID's when my script runs, all that I know is the text of the link ("This is the text I know").
The IDs of the elements are, of course, available at run time, and I can construct the ID of the various elements I need to use by replacing the prefix.
I was able to find_element_by_xpath the first entry:
//a[contains(text(),'A')]
with both FirePath and Seleninum. I can find "B" in "A/B" using FirePath with:
//a[contains(text(),'A')]/../../../../../..//div[@id='subtreeContainer_$div$fileFolder_div_ff8']//a[contains(text(),'B')]
However, that string does not work when I issue it from Selenium using find_elements_by_xpath(). (I use the plural version because several entries match ("A", "A1", "A2") a single left fragment, just look for the one that I want in that list.)
The code that I wrote is basically:
xpath = "//"
segments = [ "A", "B", "C", ...]
for segment in segments:
xpath += "a[contains(text(),'%s')]" % segment
anchors = driver.find_elements_by_xpath(xpath)
subTreeId = None
for anchor in anchors:
if anchor.text == segment:
id = anchor.get_attribute("id")
openId = "open" + id
find_element_by_id(openId).click()
subTreeId = "subTreeId" + id
WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.ID, subTreeId)))
xpath += "/../../../../../..//div[@id='%s']//" % subTreeId
break
if subTreeId is None:
fail()
The first iteration works and opens the first level of sub-tree, however, the second pass fails with this string:
//a[contains(text(),'A')]/../../../../../..//div[@id='subtreeContainer_$div$fileFolder_div_ff8']//a[contains(text(),'B')]
which is the same as the one above and I can cut-n-paste this string into FirePath and it finds the node (nodes, actually, "B", "B1", "B2", ...) - sorry to repeat myself.
My thinking is that when I "click" the "openId" element, Selenium's concept of the "current node" changes and so should the xpath, however, I was not able to get that concept to work by not carrying the prior xpath forward (I changed the "+=" to just "=" for the xpath assignment just before the "break".
Thanks (again), I appreciate the help
switchTo()
method? There are already plenty of answers here, and on stackoverflow, discussing it.