Selenium - Find children containing classes - java

I cannot find any regular expression to make it work.
What I need to archive
In a herarchy, I would like to get all the children (including several levels) that contains some specific classes.
For example: I would like the WebElements with classes "black" or "white"
<div class="initial-div">
<div class="red">
<div class="white">Hello</div>
</div>
<div class="black">Goodbye</div>
</div>
It should be able to find both the "Hello" and "Goodbye" divs, as they are both children.
My approach
I am trying to do it using Selenium and searching by the xPath. My expression looks like:
List<WebElement> nodes = initialNode.findElements(By.xpath("//*[#class='black' or #class='white']"));
But I am getting all the time "Invalid Expression exceptions" or no results.
Could someone give me a hand with this?
Thank you in advance!
SOLVED!
It was more complicated that I though, but I finally make it. I share with you, in case that someone is searching for this at any time:
List<WebElement> nodes = initialNode.findElements(By.xpath("*//descendant::div[contains(#class, 'black') or contains(#class, 'white)]"));

List<WebElement> nodes = initialNode.findElements(By.xpath("*//descendant::div[contains(#class, 'black') or contains(#class, 'white)]"));

If the element has two xpath, then you can write two xpaths like below:
xpath1 | xpath2
Eg:
//div[#class="black"] | //div[#class=“white"]
It will choose any one xpath

Related

Select input from form - changes every submit

Add image of the html. I'm not able to copy multiple lines in inspect element for some reason
I'm trying to fill the input of this form
<div class="field" xpath="1"><label class="label">E-Mail *</label> <div class="control is-clearfix"><input type="text" autocomplete="on" class="input"> <!----> <!----> <!----></div> <!----></div>
My current xpath
"/html/body/div[5]/div[#class='animation-content modal-content']/div/section//section[#class='tab-content']/div[2]/div[1]/div/input[#type='text']"
The problem is the xpath I'm using changes every submission so I can only submit once. If someone can provide a xpath that doesn't change every submission I would really appreciate it!
according to the information you provided, you can Try to use this xpath:
//div[contains(#class, "field")]//input[#class="input"]
to find name:
//label[contains(text(), "Name")]/following-sibling::div//input[#class="input"]
email:
//label[contains(text(), "E-Mail")]/following-sibling::div//input[#class="input"]
phone:
//label[contains(text(), "Phone")]/following-sibling::div//input[#class="input"]
The Xpath added by the Roman is satisfactory for your needs it just you need to understand how you can improve your Xpath there are multiple ways to do it ,
I will add some more Xpath so that it will be helpful in the near future.
I personally prefer the below mentioned way of writing the Xpath
//label[contains(text(), "Name")]/following-sibling::div//input[#class="input"]
But there are some other ways to I will add one of the Xpath from my project where you can also learn how we use the parent and following-sibling
//label[contains(text(),'PlantCode*')]//parent::div[#class='rb_Work_FieldContainer']//following-sibling::div[contains(#class,'rb_Work_FieldValueArea rb_Work_FieldValueArea_create ')]//textarea[#class='textarea']
These are some of the ways to find the Xpath, You can also use the extension like chropath in the Chrome to help you out in building the Xpath,

How to get links who follow a pattern within specific section using xpath?

Here is what I'm trying to do:
I have some links on a webpage with this pattern:
/html/body/div[4]/div/div/section/div[2]/div[3]/div/div1/div1/div/div[2]/a
/html/body/div[4]/div/div/section/div[2]/div[3]/div/div1/div[2]/div/div[2]/a
/html/body/div[4]/div/div/section/div[2]/div[3]/div/div1/div[3]/div/div[2]/a
/html/body/div[4]/div/div/section/div[2]/div[3]/div/div1/div[4]/div/div[2]/a
However, I also have other links within the same web page that have a similar path but not exactly following the same pattern:
/html/body/div[4]/div/div/section/div[2]/div[3]/div/div[3]/div/div1/div1/div/div[2]/a
How can I get just the links which follow the first pattern displayed and ignore the other ones?
Ps: I'm using Selenium Webdriver and Java and this is the update question with the html for the links
<div class="col-sm-6 half-tile">
<div class="outside-caro">
<div class="grey-overlay">
<div class="inside-caro" style="background-image:url(' https://resources/images/metabolism.jpg'")>
</div>
</div>
<div class="tile-content">
<h4 class="module title-long-card">Healthy Weight Loss</h4>
<p class="module line-clamp">This online eLearning programme is designed to help you make smart decisions when it comes to dieting and to be aware of the pitfalls.</p>
<a class="more-button" href="/application/res-courses/overview?id=23">Learn More<i style="font-size: 10px;padding-left: 5px; "class="fa fa-chevron-right" aria-hidden="true"></i></a>
</div>
</div>
</div>
Thanks very much.
I'm not quite following what you're really hoping for from your description, but I can make some guesses.
The quick answer is, just always give the full path.
But there are ways to make things a little easier to code. There are a couple ways you can create a pointer on the page and to only look for things beyond that point. The most straightforward is using simple string concatenation:
String pointer = "/html/body/div[4]/div/div/section/div[2]/div[3]/div/div[1]";
WebElement tag1 = driver.findElement(By.xpath(pointer + "/div[1]/div/div[2]/a"));
WebElement tag2 = driver.findElement(By.xpath(pointer + "/div[2]/div/div[2]/a"));
The other is to declare that pointer as a WebElement, and then use it as a base for all future findElements:
WebElement pointer = driver.findElement(By.xpath("/html/body/div[4]/div/div/section/div[2]/div[3]/div/div[1]"));
WebElement tag1 = pointer.findElement(By.xpath("./div[1]/div/div[2]/a"));
WebElement tag2 = pointer.findElement(By.xpath("./div[2]/div/div[2]/a"));
Note the dot at the beginning of the xpath to say "Use this node as your starting point".
Now, what I think you're really trying to accomplish to to make a list of all the anchors, not just pick them one by one. As in "get all the link that match one pattern but not a different but similar pattern". For that, you could just do a variation of either of the two above methods. For instance:
WebElement pointer = driver.findElement(By.xpath("/html/body/div[4]/div/div/section/div[2]/div[3]/div/div[1]"));
List<WebElement> tags = pointer.findElement(By.xpath("./div/div/div[2]/a"));
This will pull in all the links that match the pattern into a List. There are a couple things to take note:
The first element is just div, not div[1] and div[2]. since that seems to be the only thing changing in the pattern.
Most likely, the language you will use to script this is 0-indexed. So div[1] is tags.get(0).
I have a solution to problem and i hope it will help you.
You just have to identify a single parent for all 4 links that you mentioned above. And i feel you can use this locator as parent node /html/body/div[4]/div/div/section/div[2]/div[3]/div/div[1]/div[1].
Please find my code..
System.setProperty("webdriver.chrome.driver","Drivers/chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://www.abodeqa.com/2015/08/26/finding-child-elements-in-webdriver-using-findelements/");
Thread.sleep(3000);
WebElement parent = driver.findElement(By.xpath("//section[#class='secondary clearfix']"));
List<WebElement>childernNodes = parent.findElements(By.xpath("./aside//a"));
System.out.println("Total: "+childernNodes.size());
for(WebElement value: childernNodes){
System.out.println(value.getAttribute("href"));
}
WebElement pointer = driver.findElement(By.xpath("/html/body/div[4]/div/div/section/div[2]/div[3]/div/div[1]"));
// Generic path to simulate the change in the xpath for the elements of the following pattern:
// WebElement tag1 = pointer.findElement(By.xpath("./div[1]/div/div[2]/a"));
// WebElement tag2 = pointer.findElement(By.xpath("./div[2]/div/div[2]/a"));
List<WebElement> linksList = pointer.findElements(By.xpath("./div/div/div[2]/a"));
for (WebElement link : linksList) {
System.out.println(link.getAttribute("href"));
}

presenceOfAllElementsLocatedBy could not find the element

I am using Selenium and Java to write a test, when I use the code below:
List<WebElement> elements = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy
(By.xpath("//div[.//span[text()='Map']]//*")));
for (WebElement e : elements) {
System.out.println("=>" + e.getTagName() + "<=");
}
it shows all the web elements in that <div> tag.
Result:
=>span<=
=>div<=
=>div<=
=>path<=
=>path<=
=>span<=
As you see, some of the elements tag-name is path but when I use the code below it says that I could not find the element.
List<WebElement> elements = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy
(By.xpath("//div[.//span[text()='Map']]//path")));
It not easy to find the real issue with out knowing your HTML structure.
While I think there is a issue in your xpath
Try below xpath
//div//span[text()='Map']//path
Hope it will help you :)
By.xpath("//div[.//span[text()='Map']]//* will return all the decedents of span[text()='Map'] in the html hierarchy.
For example, this html structure will produce the same results you have
<div>
<span>Map</span>
<div></div>
<div>
<path></path>
<path></path>
</div>
<span></span>
</div>
As you can see, <path> is not <span> direct child, so By.xpath("//div[.//span[text()='Map']]//path is not a valid xpath.
The issue was related to some web elements that Selenium cannot navigate, the web element that I was trying to catch was inside a svg web element which is not detectable by Selenium have a look here this is exactly what was happening to me.

Get xpath of div having multiple occurence in page

I am having a weird requirement. I am having a div class named "expandThisSection". And I have
<div id=​"requiredAccessoriesContentPlaceHolder" class=​"expandThisSection">​No required accessories are available for this product.​</div>​
<div id=​"requiredAccessoriesContentPlaceHolderMeasurement" class=​"expandThisSection">​ ​</div>​
<div id=​"optionalAccessoriesContentPlaceHolder" class=​"expandThisSection">​No optional accessories are available for this product.​</div>​
<div id=​"optionalAccessoriesContentPlaceHolderMeasurement" class=​"expandThisSection">​ ​</div>​
<div class=​"expandThisSection">​
<div style=​"width:​95%">​mytext</div>​
​<ul class=​"movePos">​…​</ul>​
<div><b>test</b>​</div>
​<div>​<b>abc</b> Get this text</div>
​<div id=​"vendLogo">​…​</div>
</div>
<div class="expandThisSection">
<table>...</table>
</div>
I want the content of the div that has style of 95% width.That is value I want is "mytext". But I am not able to find out xpath for the same.
Also I want xpath for finding the div content just above div with id=​"vendLogo". That is I want "Get this text".
NOTE: ITS ASSURED THAT THIS Bold tag WILL CONTAIN "abc"
How to do it? I am using selenium with Java
Got the first one. Not able to get second one.
Code:
List<WebElement> currentSkuDescription = driver.findElements(By.xpath("//div[#class='expandThisSection']/div"));
for(WebElement currentItem: currentSkuDescription) {
WebElement descriptionBlock = currentItem.findElement(By.xpath("//div[contains(#style, 'width:95%')]"));
description= descriptionBlock.getText();
}
Try dropping the #class='expandThisSection' because the div you want does not have that class attribute (it's parent does). Also, an exact match may be possible.
By.xpath("//div[#style='width:95%']
By.xpath("//div[contains(#style, 'width:95%')]
Either of those should work. If you want to make sure you are grabbing the right one, relative to the parent, use XPath axes...
//div[contains(#class, 'expandThisSection')]/child::div[contains(#style, 'width:95%')]
If you to search for the div by a specific text (in this case 'mytext') this you can use this xpath:
"//div[text()[contains(.,'mytext')]]/ancestor::div[#class='expandThisSection']"
Or if you want by the styling, the you can use this xpath:
"//div[#style='width:95%']/ancestor::div[#class='expandThisSection']"
Note that the second xpath will only work where the inline styling is done on the tag.
For the second query, try this xpath:
"//div[#id='vendLogo']/preceding-sibling::div[1]"
To get the specific text you require, you can do the following:
String text = driver.findElement(By.xpath("//div[#id='vendLogo']/preceding-sibling::div[1]")).getText();
text.replace(div.findElement(By.tagName("b")).getText(), "");

Handle elements that have changing ids

I am running the script to automate test cases and found that id's keep on changing. Below is my HTML code
Firebug for test drive:
<button class="G0036HC-b-a G0036HC-b-o G0036HC-b-g" id="gwt-uid-470" tabindex="0" aria-labelledby="gwt-uid-470" role="button" type="button"><div class="G0036HC-b-j">Click to continue</div></button>
Inspector:
<button class="G0036HC-b-a G0036HC-b-o G0036HC-b-g" id="gwt-uid-320" tabindex="0" aria-labelledby="gwt-uid-320" role="button" type="button"><div class="G0036HC-b-j">Click to continue</div></button>
Only id's changes.
Any Help would be appreciated.
You can get the element by xpath and check that id attribute starts with gwt-uid-:
//button[starts-with(#id, "gwt-uid-")]
Besides, the answer #alecxe suggested I would also suggest you to try with text based xpath search. I often faced issue with wait so also suggest to use explicit wait this.
// //div[.='Click to continue']/..
By byXpath = By.xpath("//div[.='Click to continue']");
WebElement myDynamicElement = (new WebDriverWait(driver, 10)).until(ExpectedConditions.presenceOfElementLocated(byXpath));
// myDynamicElement.click();
Considering the following observations, the below code should work fine.
All the buttons on the page are prefixed with gwt-uid- with changing number suffix
If the buttons on the page have a unique text then use
By.xpath("//button/div[text()='Click to continue']")
If there are multiple buttons with same text then use indexes. eg. two close buttons with same text then use,
By.xpath("(//button/div[text()='Close'])[2]") # for the 2nd occurence, mind that selenium is not zero indexed
use following...
//input[#id=’’]/following::input[1]
You can use preceding in the same way to identify the child node

Categories

Resources