XPath related questions to locate elements in Webdriver 2 Java - selenium

Dear Selenium Webdriver 2 Experts,
I am new to this framework and need your advice on some XPath related questions on the following webpage XHTML snippet:
<dl class="cN-featDetails">
<dt class="propertytype">Property type</dt>
<!-- line 3 --> <dd id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_ddPropertyType" class="propertytype type-house" title="Property type: House">House</dd>
<!-- line 3a --> <!-- or class="propertytype type-townhouse"--->
.......
<div class="main-wrap">
<div class="s-prodDetails">
<a id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_hypMainThumb" class="photo contain" href="/Property/For-Sale/House/LA/St Gabriel/?adid=2009938763">img id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_imgMainThumb" title="44 Crown Street, St Gabriel" src="http://images.abc.com/img/2012814/2778/2009938763_1_PM.JPG?mod=121010-210000" alt="Main photo of 44 Crown Street, St Gabriel - More Details" style="border-width:0px;" /></a>
<div class="description">
<h4><span id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_lblPrice">Offers Over $900,000 </span></h4>
<h5>SOLD BY WAISE YUSOFZAI</h5><p>CHARACTER FAMILY HOME... Filled with warmth and charm, is a very well maintained family home in a quiet level street. Be...</p>
</div>
<a id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_hypMoreDetails" class="button" href="/Property/For-Sale/House/LA/St Gabriel/?adid=2009938763">More Details</a>
<dl id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_dlAgent" class="agent">
<!-- line 19 --> <dt>Advertiser</dt>
<!-- line 20 --> <dd class="contain">
<!-- line 20a --> <!-- or class="" -->
<img id="ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_imgAgencyLogo" title="Carmen Jones Realty" src="http://images.abc.com/img/Agencys/2778/searchlogo_2778.GIF" style="border-width:0px;" />
</dd>
</dl>
</div>
( a ) How to test whether an element exist or not? e.g. either line 3 or 3a exist but not both. The findElement() method will cause an exception which is what I am trying avoid. Another option is
use findElements() before checking whether its collection list result is empty or not. This approach seems to be a long winded way of doing it. Are there any other simpler way to validate the existence
of an element without the risk of causing an exception? The following statements did not work or cause exception:
WebElement resultsDiv = driver.findElement(By.xpath("/html/body/form/div[3]/div[2]/div[1]/h1/em"));
// If results have been returned, the results are displayed in a drop down.
if (resultsDiv.isDisplayed()) {
break;
}
( b ) Is there a simple way to validate the existence of either of the elements by incorporating boolean operator, and regex, as part of the findElement() or findElements()? This would significantly reduce
the number of finds as well as simplifying the search.
( c ) Is it possible to use XPath syntax when searching Element by TagName. e.g. driver.findElement(By.tagName("/div[#class='report']/result"));
( d ) Is it possible to use regex in XPath search such as driver.findElement(By.xpath("//div[#class='main-wrap']/dl[#class='agent']/dd[#class='' OR #class='contain']")) for line 20 - 20a?
( e ) How to reference the immediate following node? e.g. Assuming the current node is Advertiser on line 19, how to lookup title of which is under , where its class name
can have a value of "contain" or nothing "". There could potentially be multiple tags within on line 18.
I have use XPath on XML document in the past but would like to expand the ability to locate elements within Webdriver 2.
Any assistance would be very greatful.
Thanks a lot,
Jack

considering the answer given by Zarkonnen, i'd add to (a) point
public boolean isElementPresent(By selector)
{
return driver.findElements(selector).size()>0;
}
this method i use for verifying that element is present on the page.
you can also involve isDisplayed() method that can work in pair with isElement is present:
driver.findElement(By locator).isDisplayed()
Returning back to your source:
Suppose we want to verify wheter 'More details' links is present on the page or not.
String cssMoreDetails = "a[id='ctl00_ctl00_Content_Content_SrchResLst_rptResult_ctl04_lstTemplate_hypMoreDetails']";
//and you simply call the described above method
isElementPresent(By.cssSelector(cssMoreDetails));
//if you found xPath then it be
isElementPresent(By.xpath("//*..."));
And always try to verify found web elements' locators in firepath
In that way you can get xPath of the elment automatically. See the screen attached.

(a) & (d): You can use the | XPath operator to specify two alternate paths in the same expression.
(c): You can't use XPath in byTagName, but you can use // to look for any descendant of a given node.
(e): You can use XPath axes like parent and following-sibling to select all nodes relative to a given node.
I hope these pointers are helpful. I also recommend the excellent Selenium Locator Rosetta Stone (PDF) as a reference on how to construct XPaths.

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"

Choose the correct element from the list of objects with the same className

Quick one, i am trying to avoid using xpath and using css selectors due to performance issues xpath can have so i would like to know the right approach of locating for example "A" in the list
<div class="input-search-suggests" xpath="1">
<div class="input-search-suggests-item">A</div>
<div class="input-search-suggests-item">B</div>
<div class="input-search-suggests-item">C</div>
</div>
Currently i am locating A using xpath / span but it would be indeed sufficient locating all elements and then grabbing A from the list that have same class which is "input-search-suggests-item"
#FindBy(xpath = "//span[contains(text(),'A')]")
CSS_SELECTOR does not have support for direct text what xpath has.
What this means is, for the below xpath
xpath = "//span[contains(text(),'A')]"
based on text A you can not write a css selector.
Instead to locate A using css selector, you can do :
div.input-search-suggests > div.input-search-suggests-item
In Selenium something like this :
#FindBy(cssSelector= "div.input-search-suggests > div.input-search-suggests-item")
Even though it will have 3 matching nodes, but findElement will take the first web element.
Also you may wanna look at nth-child(n)
div.input-search-suggests > nth-child(1)
to make use of index to locate A, B, C
Here is the Reference Link

Protractor Conditional Selector

I am using Selenium Protractor and want to select all elements from the following list except one that contains text "Cat" and then perform some operations on the remaining ones.
<div class="mainDiv">
<div class="childDiv">Dog</div>
<div class="childDiv">Goat</div>
<div class="childDiv">Bird</div>
<div class="childDiv">Cat</div>
<div class="childDiv">Zebra</div>
</div>
Is there a selector by cssContainingText (or some other) in which I can give a condition to select all elements except the one containing text "Cat"?
You can create a List selecting all the elements except one that contains text Cat using the following Locator Strategy:
xpath:
//div[#class='mainDiv']//div[#class='childDiv'][not(contains(.,'Cat'))]
When using Selenium and css-selectors:
The :contains pseudo-class isn't in the CSS Spec and is not supported by either Firefox or Chrome (even outside WebDriver). You can find a detailed discussion in selenium.common.exceptions.InvalidSelectorException with “span:contains('string')”
However, if the elements always appears within the DOM Tree in a specific order, e.g. Cat always at the forth child, you can also use:
cssSelector:
div.mainDiv div.childDiv:not(:nth-child(4))
Reference
You can find a couple of relevant discussions in:
While fetching all links,Ignore logout link from the loop and continue navigation in selenium java
How to write a CSS Selector selecting elements NOT having a certain attribute?

Selenium XPath find element where second text child element contains certain text (use contains on array item)

The page contains a multi-select dropdown (similar to the one below)
The html code looks like the below:
<div class="button-and-dropdown-div>
<button class="Multi-Select-Button">multi-select button</button>
<div class="dropdown-containing-options>
<label class="dropdown-item">
<input class="checkbox">
"
Name
"
</label>
<label class="dropdown-item">
<input class="checkbox">
"
Address
"
</label>
</div>
After testing in firefox developer tools, I was finally able to figure out the xPath needed in order to get the text for a certain label ...
The below XPath statement will return the the text "Phone"
$x("(//label[#class='dropdown-item'])[4]/text()[2]")
The label contains multiple text items (although it looks like there is just one text object when looking at the UI) in the label element. There are actually two text elements within each label element. The first is always empty, the second contains the actual text (as shown in the below image when observing the element through the Firefox developer tool's console window):
Question:
How do I modify the XPath shown above in order to use in Selenium's FindElement?
Driver.FindElement(By.XPath("?"));
I know how to use the contains tool, but apparently not with more complex XPath statements. I was pretty sure one of the below would work but they did not (develop tool complain of a syntax error):
$x("(//label[#class='dropdown-item' and text()[2][contains(., 'Name')]]")
$x("(//label[#class='dropdown-item' and contains(text()[2], 'Name')]")
I am using the 'contains' in order to avoid white-space conflicts.
Additional for learning purposes (good for XPath debugging):
just in case anyone comes across this who is new to XPath, I wanted to show what the data structure of these label objects looked like. You can explore the data structure of objects within your webpage by using the Firefox Console window within the developer tools (F12). As you can see, the label element contains three sub-items; text which is empty, then the inpput checkbox, then some more text which has the actual text in it (not ideal). In the picture below, you can see the part of the webpage that corresponds to the label data structure.
If you are looking to find the element that contains "Name" given the HTML above, you can use
//label[#class='dropdown-item'][contains(.,'Name')]
So finally got it to work. The Firefox developer environment was correct when it stated there was a syntax problem with the XPath strings.
The following XPath string finally returned the desired result:
$x("//label[#class='dropdown-item' and contains(text()[2], 'Name')]")

Handle elements that have changing ids all the time through Selenium Webdriver

I am running the script to automate test cases and having this unique problem.
I have detected and used IDs of the elements for click etc. purpose. However, all of a sudden these ids have changed and the script works no more.
Another weird thing is those IDs are same as in script when inspected in Chrome but different in Firefox driver browser.
Firebug for test driver: -
<p class="description" onclick="selectElementTextListForIE(this,'tile29', 'tile19');selectElementTextList(this,'tile29', '')" id="tile29_span_0_0">
Platinum
</p>
Chrome inspector for same element: -
<p class="description" onclick="selectElementTextListForIE(this,'tile20', 'tile19');selectElementTextList(this,'tile20', '')" id="tile20_span_0_0">
Platinum
</p>
Also, what could be the best strategy for detecting such elements whose IDs are generated on run.
I even tried using XPATH but that too contains id's reference
eg. #id="tile276_input
Any help will be appreciated.
Thanks.
Abhishek
You can utilize CSS for this. For your element, looks like its:
<* id="tile276_input" />
What you need to do is find out what is changing about it. I assume it's the number inbetween. If it is, then your selector would look something like:
By.cssSelector("*[id^='tile'][id$='input']")
This will look for anything that has an ID that "starts with tile" and "ends with input. In our case, "tile276_input" matches that.
See this article if you want more information
You also can try contains and starts-with() for such things
driver.findElement(By.xpath("//*[contains(#id,'title')]"))
or
driver.findElement(By.xpath("//* [start-with(#id,'title')]"))
WebElement element = driver.getElement(By.cssSelector("[id^='title']);
Or
WebElement element = driver.getElement(By.cssSelector("id:contains('title')"));
You Can use this element to do desired actions.