Find Xpath to an element depending on the value of another element in a table row - selenium

I'm new to Xpath so if this doesn't contain all relevant information apologies & let me know what you need to solve it.
I am trying to find an Xpath to an "expand arrow" element which expands a row in a table. The "expand arrow" element isn't unique so I would like to select it based on text contained in another element on the same row. The layout is as follows:
<td id="__table2-rows-row10-col0">
<div class="sapUiTableCellFlex">
<span id="__table2-rows-row10- treeicon" title="Expand Node" role="button">
<div id="__hbox27-__clone101">
<div id="__data70-__clone101">
<div id="__data71-__clone101">
<span id="__text47-__clone101" title="sys-admin">sys-admin</span>
I'd like to select the element with title = "Expand Node"
<span id="__table2-rows-row10- treeicon" title="Expand Node" role="button">
based on the element with title or text = "sys-admin"
<span id="__text47-__clone101" title="sys-admin">sys-admin</span>
I've played around with various options but I can't seem to get it to work. Any ideas would be much appreciated!
Thanks

To locate the element with title as Expand Node with respect to the element with title or text as sys-admin you can use either of the following Locator Strategies:
xpath and title attribute:
"//span[#title='sys-admin']//ancestor::span[#title='Expand Node']"
xpath and text() attribute:
"//span[text()='sys-admin']//ancestor::span[#title='Expand Node']"

I eventually got it to work using the following xpath:
//span[#title='sys-admin']/../../preceding-sibling::span[#title='Expand Node']

Related

Click on parent element based on two conditions in child elements Selenium Driver

Using Selenium 4.8 in .NET 6, I have the following html structure to parse.
<ul class="search-results">
<li>
<a href=//to somewhere>
<span class="book-desc">
<div class="book-title">some title</div>
<span class="book-author">some author</span>
</span>
</a>
</li>
</ul>
I need to find and click on the right li where the book-title matches my variable input (ideally ignore sentence case too) AND the book author also matches my variable input. So far I'm not getting that xpath syntax correct. I've tried different variations of something along these lines:
var matchingBooks = driver.FindElements(By.XPath($"//li[.//span[#class='book-author' and text()='{b.Authors}' and #class='book-title' and text()='{b.Title}']]"));
then I check if matchingBooks has a length before clicking on the first element. But matchingBooks is always coming back as 0.
class="book-author" belongs to span while class="book-title" belongs to div child element.
Also it cane be extra spaces additionally to the text, so it's better to use contains instead of exact equals validation.
So, instead of "//li[.//span[#class='book-author' and text()='{b.Authors}' and #class='book-title' and text()='{b.Title}']]" please try this:
"//li[.//span[#class='book-author' and(contains(text(),'{b.Authors}'))] and .//div[#class='book-title' and(contains(text(),'{b.Title}'))]]"
UPD
The following XPath should work. This is a example specific XPath I tried and it worked "//li[.//span[#class='book-author' and(contains(text(),'anima'))] and .//div[#class='book-title' and(contains(text(),'Coloring'))]]" for blood of the fold search input.
Also, I guess you should click on a element inside the li, not on the li itself. So, it's try to click the following element:
"//li[.//span[#class='book-author' and(contains(text(),'{b.Authors}'))] and .//div[#class='book-title' and(contains(text(),'{b.Title}'))]]//a"

Selenium preceding returns a pointer/address and full xpath return a different pointer/address to what I believe should be the same object

Relative Xpath
WebElement precedingClass = driver.findElement(By.xpath("//div[contains(#class, 'pictures')]/preceding::div[contains (#class 'monkey picture') ]"));
Absolute Xpath
WebElment fullXpathAddress = driver.findElement(By.xpath("/html/body/div[6]/div/div[1]/div[1]/div/div[2]/ul/li[20]/div[1]");
HTML Code:
<div class="pictures">
<a ref ....>
<a ref ....>
<h1>x's pictures</h1>
</div>
<div class="animals">
<div class="monkey picture" title="happy monkey"></div>
.
.
</div>
Preceding: Select all nodes that come before the current node.
And you want to select node that is after the current node.
Maybe you can use following as below :
"//div[contains(#class, 'pictures')]/following::div[#class='animals']/div[#class='monkey picture']"
please refer this link to learn more about Xpaths
Please optimize the above Xpath based on your whole DOM structure.
Your Xpath seems wrong
Try below Xpath:
//div[#class='pictures']/following-sibling::a//div[#class='monkey picture']
So the code will be like:
WebElment element = driver.findElement(By.xpath("//div[#class='pictures']/following-sibling::a//div[#class='monkey picture']");
I was able to use preceding::div[x] since I was able to determine index of div "monkey picture" in the list of animals and from the start of animals, the number of divs preceding to "pictures was a fixed value.

How to extract the name of the authors using an XPath expression

Given below is the snippet of HTML:
<div class="a-row a-spacing-none">
<span class="a-size-small a-color-secondary">by
</span>
<span class="a-size-small a-color-secondary">
<a class="a-link-normal a-text-normal" href="/Lowell-Fryman/e/B01M3MNJTE/ref=sr_ntt_srch_lnk_1?qid=1550228622&sr=1-1">
Lowell Fryman
</a>
and
</span>
<span class="a-size-small a-color-secondary">
<a class="a-link-normal a-text-normal" href="/Gregory-Lampshire/e/B01N7ZWT5Y/ref=sr_ntt_srch_lnk_1?qid=1550228622&sr=1-1">
Gregory Lampshire
</a>
</span>
</div>
I'm trying to obtain the name of all authors.
This is whatever test follows the word by.
I came up with the following XPath but it doesn't seem to fetch all the authors.
My XPath expression:
//div//span[text()=\"by \"]//following::span[1]//a
Can someone please tell me how to obtain the name of all the authors while somehow managing to skip any element whose text is "and"?
I'm using Selenium's find_element_by_xpath if it helps.
Try the below code.This should work.It will fetch all author.
elements=driver.find_elements_by_xpath("//a[#class='a-link-normal a-text-normal']")
for element in elements:
print(element.text)
Please let me know if this work.
Here xpath you can use to get authors:
//div[./span[normalize-space(.='by')]]//a
or
//div[./span[contains(.,'by')]]//a
Your xpath should be like this:
//span[normalize-space(.='by')]/following-sibling::span//a
you can first get text of all span elements in a list and then slice it from "by" text value
elements = [_.text() for _ in driver.find_elements_by_css_selector('div.span')]
print elements[elements.index('by'):]
To print the name of all the authors which are followed by the word by you can use either of the following solutions:
Using innerHTML:
print([author.get_attribute("innerHTML") for author in driver.find_elements_by_xpath("//span[contains(., 'by')]//following::span/a[#class='a-link-normal a-text-normal'][#href]")])
Using text:
print([author.text for author in driver.find_elements_by_xpath("//span[contains(., 'by')]//following::span/a[#class='a-link-normal a-text-normal'][#href]")])

How to identify an element whose id is dynamically changing and has no other attributes in selenium?

I've this html body for a kendo dropdown list which has only one attribute i.e. id which is dynamically changing, how do i identify this object on every page refresh accurately.
other attributes like class and tab index already are present with same values multiple times on the same page for other dropdowns-
<span role="listbox" unselectable="on" class="k-dropdown-wrap k-state-default" id="dde13a91-2bf3-4e41-af72-bee1b881a8d9" dir="ltr" readonly="false" tabindex="0" aria-disabled="false" aria-readonly="false" aria-haspopup="true" aria-expanded="false" aria-owns="48f666d8-4c3c-43a8-a4dc-8e7a9961a0ef" aria-activedescendant="ca3c4431-3ebf-46c0-9510-a64a32eae108-C.US.0000110896">
<span unselectable="on" class="k-input">
<!---->
<!---->2018 ALBERTSONS / Beverage Mixes
</span>
<span unselectable="on" class="k-select">
<span class="k-i-arrow-s k-icon"></span>
</span>
<!---->
</span>
If there is a always present value in dropdown, You can try to find this value by text or something, and then get a parent:
XPath: Get parent node from child node
Other way is to get it by xpath of structure, like: div[3]/.../span etc. It is not good as every change can fail Your test, but if You have no other options, then You might want to try this.
To click on the element with the only attribute-text as 2018 ALBERTSONS / Beverage Mixes you need to induce WebDriverWait for the element to be clickable and you can use the following solution:
(Java) xpath:
new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.xpath("//span[#class='k-dropdown-wrap k-state-default' and #role='listbox']//span[#class='k-input']"))).click();
I found a workaround to locate the element:
First get the xpath of the span tag inside the dropdown using it's text as below:
//text()[contains(.,'2018 ')]
since '2018 ' is common irrespective of adjacent text
Then move up to the parent tag of the dropdown to locate the dropdown frame which can be collapsed on click:
//text()[contains(.,'2018 ')]/parent::/parent::
then simply click on the element which is located.
driver.findElement(By.xpath("//text()[contains(.,'2018 ')]/parent::/parent::")).click();

How to write the XPath using sibling?

How to write the XPath using sibling?
I am trying to get the price value of the delivery fee using the sibling. But I am unable to get the XPath for the price tag below is the HTML code.
<div class="grid_3 d-grid_10">
<label class="m-confirmation-modal-print-detail-capgrey"> Delivery Fee:</label>
</div>
<div class="grid_1 d-grid_2">
<label class="m-confirmation-modal-print-price text-align-right"> $9.00</label>
</div>
You can try this:
//div[contains(., ' Delivery Fee:')]/following-sibling::div/label
Explanation to xPath: //div[contains(., ' Delivery Fee:')] finds a div on the top, then /following-sibling::div will select all next siblings div below our div. In our case only the next div. And finaly /label locates the element with value $9.00.
More information here
You can try this :
//label[contains(text(),' Delivery Fee:')]/parent:div/following-sibling::div/label
But yes #Andrei solution make much sense to use.