Locating child nodes of WebElements in selenium - java

I am using selenium to test my web application and I can successfully find tags using By.xpath. However now and then I need to find child nodes within that node.
Example:
<div id="a">
<div>
<span />
<input />
</div>
</div>
I can do:
WebElement divA = driver.findElement( By.xpath( "//div[#id='a']" ) )
But now I need to find the input, so I could do:
driver.findElement( By.xpath( "//div[#id='a']//input" ) )
However, at that point in code I only have divA, not its xpath anymore... I would like to do something like this:
WebElement input = driver.findElement( divA, By.xpath( "//input" ) );
But such a function does not exist.
Can I do this anyhow?
BTW: Sometimes I need to find a <div> that has a certain decendent node. How can I ask in xpath for "the <div> that contains a <span> with the text 'hello world'"?

According to JavaDocs, you can do this:
WebElement input = divA.findElement(By.xpath(".//input"));
How can I ask in xpath for "the div-tag that contains a span with the
text 'hello world'"?
WebElement elem = driver.findElement(By.xpath("//div[span[text()='hello world']]"));
The XPath spec is a suprisingly good read on this.

If you have to wait there is a method presenceOfNestedElementLocatedBy that takes the "parent" element and a locator, e.g. a By.xpath:
WebElement subNode = new WebDriverWait(driver,10).until(
ExpectedConditions.presenceOfNestedElementLocatedBy(
divA, By.xpath(".//div/span")
)
);

For Finding All the ChildNodes you can use the below Snippet
List<WebElement> childs = MyCurrentWebElement.findElements(By.xpath("./child::*"));
for (WebElement e : childs)
{
System.out.println(e.getTagName());
}
Note that this will give all the Child Nodes at same level ->
Like if you have structure like this :
<Html>
<body>
<div> ---suppose this is current WebElement
<a>
<a>
<img>
<a>
<img>
<a>
It will give me tag names of 3 anchor tags here only . If you want all the child Elements recursively , you can replace the above code with
MyCurrentWebElement.findElements(By.xpath(".//*"));
Hope That Helps !!

I also found myself in a similar position a couple of weeks ago. You can also do this by creating a custom ElementLocatorFactory (or simply passing in divA into the DefaultElementLocatorFactory) to see if it's a child of the first div - you would then call the appropriate PageFactory initElements method.
In this case if you did the following:
PageFactory.initElements(new DefaultElementLocatorFactory(divA), pageObjectInstance));
// The Page Object instance would then need a WebElement
// annotated with something like the xpath above or #FindBy(tagName = "input")

The toString() method of Selenium's By-Class produces something like
"By.xpath: //XpathFoo"
So you could take a substring starting at the colon with something like this:
String selector = divA.toString().substring(s.indexOf(":") + 2);
With this, you could find your element inside your other element with this:
WebElement input = driver.findElement( By.xpath( selector + "//input" ) );
Advantage: You have to search only once on the actual SUT, so it could give you a bonus in performance.
Disadvantage: Ugly... if you want to search for the parent element with css selectory and use xpath for it's childs, you have to check for types before you concatenate...
In this case, Slanec's solution (using findElement on a WebElement) is much better.

Related

how to add the second css selector xpath where the same element appears more than once

how to add the second css selector or xpath where the same element appears more than once.
WebElement element = driver.findElement(By.cssSelector("span.mat-content"));
I need to add the same css selector again but for the 2nd element
how do it do that.
This is for the first one which I have added as seen above:
HTML:
<span class="mat-content ng-tns-c143-2587"> = first one
HTML which I need to add -
<span class="mat-content ng-tns-c143-2589"> = How do I add the css selector using this html tag.
WebElement element2 = driver.findElement(By.cssSelector("span.mat-content")); = In this place how do I add it for the second element
If this span.mat-content represent multiple web element in HTMLDOM.
You can use findElements to grab them all.
List<WebElement> elements = driver.findElements(By.cssSelector("span.mat-content"));
Now elements is a list in Java-Selenium bindings.
You could do, elements.get(1) and this shall represent the second web element.
or You can iterate the entire list like this:
for (WebElement element : elements){
element.getText(); //Note that each `element` is a web web element.
}
If you do not wish to have the above way. You can try xpath indexing.
(//span[contains(#class,'span.mat-content')])[1]
should represent the first element.
and
(//span[contains(#class,'span.mat-content')])[2]
should represent the second element and so on..
[3], [4], .... [n]
just replace css with xpath. xpath indexing is not preferred choice.
List<WebElement> elements= driver.findElements(By.cssSelector("span.mat-
content"));
WebElement element2 = elements.get(1);
Should work

Selenium Automation : Identifying unique set of webelements using selenium in JAVA

I have an HTML like this:
"<div **id="xx-abc-list-1"**>
<button ng-reflect-class-base="icon component" **id="xx-abc-list-button-2"**><span class="mat-button-wrapper">
</div>
<div **id="xx-abc-list-2"**>
<button ng-reflect-class-base="icon component" **id="xx-abc-list-button-2"**><span class="mat-button-wrapper">
</div>"
Now, i wanted to get the list of all the elements ending with id = xx-abc-list- only so i used the below code.
#FindBy(xpath = "//*[contains(#id,'xx-abc-list-')]");
List<WebElement>;
But this gives me 4 count considering other elements (xx-abc-list-button-) as well.
What should i modify in my above code to get just 2 values that i need?
#FindBy(xpath = "//*[contains(#id,'xx-abc-list-')]");
The * in your XPath above indicates any element (it's a wildcard). In your case, you want only DIVs so you can change that to
#FindBy(xpath = "//div[contains(#id,'xx-abc-list-')]");
and it should get you what you want.
In a more complicated scenario where you want two but not the four and all are DIVs, you can still use a "not contains()" to exclude the IDs that contain "-button-".
#FindBy(xpath = "//*[contains(#id,'xx-abc-list-')][not(contains(#id,'-button-'))]")
How to use not contains() in xpath?

Clicking an element having same class name as others

I am trying to locate and click an element which is having same className as other elements. I am unable to differentiate that element from other to click that element. Here is the HTML code of that element:
<a href="/category/men/N-fh7rea" class="accord-header">
Men
</a>
In this code, classname is same as others elements and text "Men" is also same. So made an Xpath of this:
//a[#class='accord-header' AND contains(text(),'Men') ]
Tweak the xpath a bit and use :
//a[#class='accord-header' and #href='/category/men/N-fh7rea']
You can get more granular and use :
//a[#class='accord-header' and #href='/category/men/N-fh7rea' and contains(.,'Men')]
You can alos use :
//a[#class='accord-header' and #href='/category/men/N-fh7rea'][normalize-space()='Men']
if you can't find any difference you can always count whith one, from the same objects interest you. If its for example 3rd element, save all of them as a list using findElements, and then get third element from it.
List<WebElement> elems = driver.findElements(By.xpath("//a[#class='accord-header' and #href='/category/men/N-fh7rea' and contains(.,'Men')]"));
WebElement elementThatYouLookedFor = elems.get(2);
If You need to click all of elements of this kind just use foreach loop:
List<WebElement> elems = driver.findElements(By.xpath("//a[#class='accord-header' and #href='/category/men/N-fh7rea' and contains(.,'Men')]"));
for(WebElement we : elems){
we.click(); //or any other operation
}

Selenium: Extract Text of a div with cssSelector in Java

I am writing a JUnit test for a webpage, using Selenium, and I am trying to verify that the expected text exists within a page. The code of the webpage I am testing looks like this:
<div id="recipient_div_3" class="label_spacer">
<label class="nodisplay" for="Recipient_nickname"> recipient field: reqd info </label>
<span id="Recipient_nickname_div_2" class="required-field"> *</span>
Recipient:
</div>
I want to compare what is expected with what is on the page, so I want to use
Assert.assertTrue(). I know that to get everything from the div, I can do
String element = driver.findElement(By.cssSelector("div[id='recipient_div_3']")).getText().replaceAll("\n", " ");
but this will return "reqd info * Recipient:"
Is there any way to just get the text from the div ("Recipient") using cssSelector, without the other tags?
You can't do this with a CSS selector, because CSS selectors don't have a fine-grained enough approach to express "the text node contained in the DIV but not its other contents". You can do that with an XPath locator, though:
driver.findElement(By.xpath("//div[#id='recipient_div_3']/text()")).getText()
That XPath expression will identify just the single text node that is a direct child of the DIV, rather than all the text contained within it and its child nodes.
I am not sure if it is possible with one css locator, but you can get text from div, then get text from div's child nodes and subtract them. Something like that (code wasn't checked):
String temp = "";
List<WebElement> tempElements = driver.findElements(By.cssSelector("div[id='recipient_div_3'] *"));
for (WebElement tempElement : tempElements) {
temp =+ " " + tempElement.getText();
}
String element = driver.findElement(By.cssSelector("div[id='recipient_div_3']")).getText().replaceAll("\n", " ").replace(temp, "");
This is for case when you try to avoid using xpath. Xpath allows to do it:
//div[#id='recipient_div_3']/text()
You could also get the text content of an element and remove the tags with regexp. Also notice: you should use the reluctant quntifier
https://docs.oracle.com/javase/tutorial/essential/regex/quant.html
String getTextContentWithoutTags(WebElement element) {
return element.getText().replaceAll("<[^>]*?/>", "").trim();
}

WebDriver Specific tags

I'm using WebDriver and I have a WebElement that has a few root tags, each of these div tags has multiple div tags within them. Is there a way to get just the root tags?
Eg.
WebElement grabbed from the "someid" field below contains the following data within. I'd like to grab the root div tags from this element (example below has three) By default it grabs the root, and any div tags within, is there a way to just grab those three div tags? There is not specific names or id to grab them, but if I can get the root div tags, I can loop through them easier to locate the data I need in each. Thanks for any direction, or let me know if I need to get more information. Thanks
<div id="someid">
<div><more html that has div tags></div>
<div><more html that has div tags></div>
<div><more html that has div tags></div>
</div>
WebElement weMain = driver.findElement(By.id("someid"));
// Looking for a way to just grab the root divs to my WebElement
List<WebElement> weSomeElements = weMain.findElements(By.tagName("div"));
Thanks for any direction
Try:
driver.findElements(By.xPath("//div[#id='someid']/div")
I think that should return the 3 divs.
try this
List<WebElement> weSomeElements =
driver.findElements(By.CssSelector("div[id='someid'] div"));
Then you can do something like this:
WebElement element = weSomeElement.select(n).findElements(by.tagName("div"));
var elementText = element.Text();

Categories

Resources