Testing relative positions of elements - selenium

On the page under test I have the following Support link:
Which is represented with the following HTML:
<div class="ap-version-panel ap-version-support">
<i class="fa fa-external-link"></i>Support
</div>
What I'm trying to is to test that the icon is located before the "Support" text. How can I do that?
I've tried the following - locate the icon element with an XPath that additionally checks that there is "Support" text after and check if the element is present:
expect(element(by.xpath("//text()[. = 'Support']/preceding-sibling::i[contains(#class, 'fa-external-link')]")).isPresent().toBe(true);
This works but it is quite ugly and I don't like that the actual position check is hidden inside the XPath expression which is not really readable and reliable.

I recommend changing the product under test by adding a <span> and move the text "Support" into the <span>.
<div class="ap-version-panel ap-version-support">
<i class="fa fa-external-link"></i><span>Support<span>
</div>
But if you cannot change the product, you can use javascriptExecutor to get childNodes then check order of the nodes:
var aFunctionToCheckOrder = function (arguments) {
var nodes = arguments[0].childNodes;
var result;
// Check nodes[0] = <i>
// Check node [1] is textNode and its value = "Support"
return result;
}
var supportLink = element(by.linkText("Support"));
browser.executeScript(aFunctionToCheckOrder, supportLink).then(...)
As you can see, it is more uglier than your solution. You'd better change your product under test.

Related

How to find xpath of an element which depends upon sibling class

I have below html code
<a class = sidetoolsdivider>
<div class = sideone > Test 1 </div>
<div class = sidetwo> </div>
</a>
<a class = sidetoolsdivider>
<div class = sideone > Test 2 </div>
<div class = sidetwo> </div>
</a>
...............
Here I need to find xpath locator of class sidetwo which has text Test1. There are many such similar classes hence you can differentiate between different only based on element text
The xpath would be something like below:
Since the element depends on the text, can make use of text attribute for the same.
//div[text()='Text1']/following-sibling::div
Or
//div[contains(text(),'Text1')]/following-sibling::div
Or
//div[contains(text(),'Text1')]/following-sibling::div[#class='sidetwo']
Link to refer - Link
This gets you the correct 'a'. Find an 'a' which contains the right div of sideone (note the .//, find a Child which is)
"//a[.//div[ #class='sideone" and text()='Test 1']"
Then just get the side two, complete xPath
"//a[.//div[ #class='sideone" and text()='Test 1']//div[#class='sidetwo']"
Works even if there is more text inside the entire 'a' and stuff gets complex with more elements inside.

Xpath following the for the next thumbnail - optimized solution? (for selenium automated solution)

At demo store we have a list of thumbnail as given below :
<ul class="product_list grid row">
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 1 -->
</div>
</li>
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 2 -->
</div>
</li>
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 3 -->
</div>
</li>
</ul>
I can navigate through thumbnails using the xpath mentioned below:
thumbnail number 1 = //div[#class="product-container"]
thumbnail number 2 = //div[#class="product-container"]/following::div[#class="product-container"][1]
thumbnail number 3 = //div[#class="product-container"]/following::div[#class="product-container"][1]/following::div[#class="product-container"][1]
Through, above xpath are working fine for me but not an optimized solution.
Update 1: The objective is leave the xpath in a closed "form" at web page object library, for using by automated tests.
Get all the thumbnails in a list and then navigate through. Use below CSS selector for the same. Not sure which language you are using. I write this using Java.
List<WebElement> products = driver.findElements(By.cssSelector("div.product-container"));
for(WebElement product : products){
String productName = product.findElement(By.cssSelector(".product-name")).getText();
String productPrice = product.findElement(By.cssSelector(".right-block .price")).getText();
...
}
OR using the xpath mentioned by you.
List<WebElement> allThumbnails = driver.findElements(By.xpath("//div[#class='product-container']"));
for(WebElement thumbnails:allThumbnails){
String productName = product.findElement(By.xpath(".//a[#class='product-name']")).getText();
String productPrice = product.findElement(By.xpath("//div[#class='right-block']//span[#class='price product-price']")).getText();
...
}
UPDATED
As per your comment if it is require to mentioned fix element in your xpath then using indexes would be the right approach.
there are total 7 product present on the page you shared the URL and xpath so you can write it like-
thumbnail number 1 = //ul[#class='product_list grid row']/li[1]
thumbnail number 2 = //ul[#class='product_list grid row']/li[2]
...
thumbnail number 7 = //ul[#class='product_list grid row']/li[7]
If all the products share the same class you can just have Selenium pick up all matching elements, as opposed to explicitly stating one after the other like you're currently trying.
You've not said what language you're using, but here's what it'd look like in C#:
// Setup your web driver...
var thumbnails = driver.FindElements(By.XPath("//div[#class=\"product-container\"]"));
foreach (var thumbnail in thumbnails)
{
// Do work
}
Or
for(int i = 0; i < thumbnails.Count; i++)
{
// Do work by index
}
Note the plural FindElements, it returns a collection of all matching elements. Selenium does a fairly good job of matching methods across languages so that should point you in the right direction at least.

No such element Exception. class name with double__

I am trying to get the text under the tag. That is Arduus, Gstaad, Switzerland. I tried with the classname and also with xpath.
driver.findElement(By.className("chalet-details__title"));
String chaletTitle = driver.findElement(By.xpath("//div/h1[#class='chalet-details_title']")).getText();
But its giving NoSuchElementException. The classname has double underscore(__) .Is it because of that not working? Can anyone help me with this?
<div class="col-md-12 col-sm-12 col-xs-12">
<h1 class="chalet-details__title">
<span class="chalet-details__title-property">Arduus</span>,
<a class="chalet-details__title-resort" href="/ski- resorts/switzerland/gstaad">Gstaad</a>,
<a class="chalet-details__title-country" href="/ski- resorts/switzerland">Switzerland</a>
</h1>
<div class="chalet-details__rating">
<div class="chalet-details__wrapper">
<span class="chalet-details__star" style="width: 108px;"></span>
<span class="chalet-details__mask"></span>
</div>
</div>
</div>
You can try something like:
driver.findElement(by.cssSelector("h1.chalet-details__title"))
I just tried this and it worked fine with the provided html, if this still fails for you, can you verify that there aren't any iframes on the page.
Both your locators are OK, but you might need to wait for your element to be present in DOM:
WebDriverWait wait= new WebDriverWait(driver,20 );
String chaletTitle = wait.until(ExpectedConditions.presenceOfElementLocated(By.className("chalet-details__title"))).getText();
Another reason for NoSuchElementException: your element could be located inside an iframe, so you need to switch to that frame before handling target element:
driver.switchTo().frame("iframeNameOrId");
If it has no attributes as id or name:
WebElement someFrame = driver.findElement(By.xpath("//iframe[#someAttr='someValue']"));
driver.switchTo().frame(someFrame);
To get all the text under h1 tag find all elements using locator as follows:
List<WebElement> items = driver.findElements(By.cssSelector("h1.chalet-details__title > *")); //it will find all elements immediately under the h1 tag
Now, iterate over the elements.
Complete code:
List<WebElement> items = driver.findElements(By.cssSelector("h1.chalet-details__title > *"));
for(WebElement item : items){
System.out.println(item.getText());
}

Finding a parent element in webdriver.io

I've seen a couple of solutions in the original webdriver that use getAttribute('xpath') and append to that '/..' but webdriver.io doesn't have an xpath attribute so I haven't been able to use that. Any ideas on how to grab the parent element?
The case I am trying to test is inside of a bootstrap layout and the element that is actually getting the class I am trying to check is one above. It looks like this:
<div class="form-group">
<input class="form-control" type="text" name="username">
<other stuff>
</div>
I am selecting by driver.element("input[name='username'"] but the error class actually hits the div
<div class="form-group error">
<input class="form-control" type="text" name="username">
<other stuff>
</div>
So I need to check if the div itself has an error class, not the input I can find (there are no uniques on the div)
Any help would be greatly appreciated.
Just searched the same and found this by inspecting Webdriver.IO source code - you can use el.$('..') to get the parent element, like this:
$('input[name="username"]').$('..') // returns the parent element
Voila! It's a part of their XPath selectors support.
I ended up solving this by using execute to get the xpath from jQuery, here is the code I used (albeit for a different test I was writing - if every label that has a * is a required field). Hopefully this is helpful to somebody in the future:
var requiredXPaths = browser.execute(function () {
var returner = [];
var $elements = $('.modal.fade.in').find("label:contains('*')");
_.each($elements, el => {
var xpath = '';
var element = el.parentElement;
for (; element && element.nodeType == 1; element = element.parentNode) {
var id = $(element.parentNode).children(element.tagName).index(element) + 1;
id > 1 ? (id = '[' + id + ']') : (id = '');
xpath = '/' + element.tagName.toLowerCase() + id + xpath;
}
returner.push(xpath);
});
return returner;
});
I got the xPath function from another stackoverflow page (How to calculate the XPath position of an element using Javascript?)
WebdriverIO actually lets you use XPath in your selectors, so any valid Xpath selector should work to find the element you want.
Alternatively, you could just use isExisting to check if the element exists on the page using the error class parent:
driver.isExisting('.error input[name="username"]');
If the element doesn't exist, then your error class didn't get added.
I've been talking about this in gitter, when the parent can be selected input[name="username"]
The parent of this element can be selected as //div[input[name="username"]]

How to check if there is a strikethrough on some text in selenium webdriver?

There are many planbox, which are having same class and ids, inside them there are a number of <p> tags and decorated text.
<div class="planbox">
<p class="baseprice">
<span>
<strike> $70 </strike>
</span>
</p>
<p> New discount price is etc. </p>
</div>
<div class="planbox">
<p class="baseprice">
<span> $70 </span>
</p>
</div>
Now, My test case is - if the base price is strikethrough, only then <p> 'New discount price .. </p> will show, otherwise not.
How to check whether a text is strikethrough or not? And even if we get this how will I check that <p> New discount.. </p> should not show if the text is not striked.
As there is no class in <p> tag on which I can check whether it displayed or not.
One solution in my mind was - add one dummy class in <span> tag and using findChildren('span.dummyCLass') it will result all the webelements having dummyClass.
Now I will check whether web-elements have strike tag or not, and this is the place where I got stuck.
Initially, i was thinking of a Jquery solution, but is it possible to do without adding new class and jquery?
You don't need to add a class to any element to accomplish this task. In general, you don't want to edit the HTML. Another issue is... if you can find the element to add a class, then you don't need to add the class to find the element. :)
The way I approach tasks like these is to find the outermost element that contains all the elements that you are interested in. I refer to this as a "container". What you want to do in this case is to find the containers and loop through them looking for the strikethrough price and for the "New discount price..." text.
The containers are DIVs with the planbox class. The strikethrough price is indicated by the STRIKE tag. The "New discount price..." text is in a P tag. With this info we can write some code. This is Java because I don't know what language you want and I'm not familiar with the Galen framework.
// gets the collection of containers using a CSS selector
List<WebElement> containers = driver.findElements(By.cssSelector("div.planbox"));
// loops through the containers
for (WebElement container : containers)
{
// determine if the STRIKE tag exists
boolean strikeExists = !container.findElements(By.tagName("strike")).isEmpty();
// determine if the "New discount price is..." text exists in the 2nd P tag
boolean discountPriceExists = container.findElements(By.tagName("p")).get(1).getText().trim().contains("New discount price is");
// if both are true log a pass, otherwise log a fail
if (strikeExists && discountPriceExists)
{
// log a pass
}
else
{
// log a fail
}
}
I haven't used much of selenium. But you can port this jquery code to selenium,
//if there is a strike element
if($(".baseprice span strike").length > 0){
//next() will select the sibling of the p tag with baseprice class
$("p.baseprice).next() != undefined){
return true
}else{
return false
}
}
you can use Galen for this. There you can verify certain CSS properties.