FindElement in Selenium without using Xpath - java

I want do find the element without using xpath, but I have another ul´s elements and "class=title" on my page. So, I cannot use Type or "class=title" either.
<div id="pai">
<div class="def">
<div class="abc" >
<p class=title>Title</p>
<ul>
<li></li>
I´m thinking about something like below, but I don´t know if it is the best practice:
WebElement um = driver.findElements(By.className("abc"));
WebElement dois = um.findElements(By.className("title"));
tamUl = dois.findElements(By.tagName("ul")).size();

In case when you can't or not willing to use Xpath or CSS selectors, you want to get list of all needed elements present on the page. Then you choose (either empirically, by debugging or using conditionals) the index of the specific element to use. Following might be the case you want to try:
List <WebElement> elements = driver.findElements(By.className("title"));
if (elements.size() > 0) {
elements.get(INDEX_OF_YOUR_ELEMENT).findElements(By.tagName("ul")).size();
}
Pay attention to the difference between findElement() and findElements() methods. While findElement() returns only first element if multiple occurrences are found, at the same time it throws NoSuchElementException if nothing is found, where findElements() only returns empty list.

Related

How to create a list of the autocomplete <li> elements when parent <ul> element contains the attribute style="display: none;" through Java Selenium

I'm using Java selenium to create a automation project. Now in our web app, there is address input box and has autocomplete feature. The problem is after input a partial address, we need click one of the option from the list. Here is the html:
Now I have tried to get the list. But failed:
WebElement autoCompelet = driver.findElement(By.xpath("/html/body/ul[1]"));
List<WebElement> options = autoCompelet.findElements(By.tagName("li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1);
}
I can print out , but list is empty.
I've also tried to use wait method like:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/html/body/ul[1]/li[1]")));
Unfortunately, none of them works. Is anyone has any idea on this? Any input is appreciated. Thank you guys.
By.className("li") is invalid,the value should be the class name,need to use By.tagName("li") instead.
Also you can change your xpath to get ul by class and then get li directly
WebElement autoCompelet = driver.findElement(By.xpath("//ul[#class='af_list']"));
List<WebElement> options = autoCompelet.findElements(By.tagName("li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1);
}
You can try as below.
//wait is added in order to check the presence of autocomplete suggestion
WebDriverWait wait=new WebDriverWait(driver,20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("af_list")));
List<WebElement> options = driver.findElements(By.xpath("//ul[#class='af_list']/li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1.getText());
}
Note: I have assumed af_list class name is used only in the autocomplete.If it is used in other section as well, then please share the full html detail
You can try something like this , after having all the <li> elements in a list, you can easily get the text of each web element by using getText() method which is available in selenium Java binding.
List<WebElement> options = driver.findElements(By.cssSelector("ul.af_list li"));
System.out.println(options.size());
logger.debug(options.size());
for(WebElement options1 : options)
{
logger.debug(options1.getText());
}
Note that it is always a good practice to use css selector over xpath.
You may want to introduce webDriverWait for your scenario. That would explicitly wait for all <li> elements to be present/available in list.
Hope this will help.
As per the HTML you have shared, the desired elements i.e. the <li> tags are within a <ul> tag which is having the attribute style="display: none;". So to access the <li> tags you can use the following solution:
WebElement autoCompelet = driver.findElement(By.xpath("//ul[#class='af_list']"));
((JavascriptExecutor)driver).executeScript("arguments[0].removeAttribute('style')", autoCompelet);
List<WebElement> options = autoCompelet.findElements(By.xpath("./li[#class='af_item']"));
for (WebElement option1 : options) {
System.out.println(option1.getText());
}
I have a similar feature in our web application. In this case, you can use sendKeys() method with Keys.DOWN to literally go to that element and then Keys.ENTER to select it. Normally once you sendKeys() part of the text, the others start populating below, so you basically type to an extent until which just the first option in the populated dropdown texts is the one which you want. Then do sendKeys(Keys.DOWN) followed by sendKeys(Keys.ENTER)

Selenium not picking up found elements on xpath

Found list of elements either with xpath or CSS (same results in browser console)
int alltips = driver.findElements(By.xpath("//div[#class='column medium-12']//div/ul/li")).size();
int alltips1 = driver.findElements(By.cssSelector("ul.feed-tips#Grid > li.feed-item")).size();
System.out.println(alltips);
System.out.println(alltips1);
As a result of both printing got same result (that there are 21 of 'li' containers exist on a page)
But, when put ran that in selenium webdriver, I got same result for both and it is zero.
Added screenshot from console
What did I do wrong?
Here is a part of HTML:
<div class="column medium-12">
<h1>Free Tips</h1>
<p>Here you'll always find the latest tips posted by our international community of sports betting tipsters. If you're ever in need of inspiration for a bet, this is the place to be! </p>
<div class="row">
<ul class="feed-tips" id="Grid" data-sport="" data-country="" data-
league="">
And below HTML looks like as on screenshot:
findElements does not throw an error if no elements are found, so it is possible that the elements are not found at the time of calling this method.
You can wait for the ul element to be visible before calling findElements by using a WebDriverWait like this
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(#Grid)));
This will wait up to 10 seconds before throwing a timeout. After that, call your findElements method. At this point you know that the parent ul is visible
int alltips = driver.findElements(By.xpath("//ul[#id='Grid']/li")).size();

Get Web Elements that are not named, by their proximity to known elements

I am a beginner test automator and I've been using direct XPATH references so far.
Now, I have the task of counting list items in a list that has no name.
I can copy the xpath using Chrome inspect but that path is very brittle. Any addition of element above it will make the path invalid. Looking for a more intelligent way to find the list.
I am using Selenium-JUnit-GeckoDriver.
Here is an example of my raw data:
...
<div class="entry-content" itemprop="text"><h3></h3>
<p> </p>
<p>texttexttexttexttexttexttexttext</p>
<p> </p>
<h4 class="p1"><span class="s1">List ONE</span></h4>
<hr>
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<h4 class="p1"><strong><span class="s1">List TWO</span></strong></h4>
<hr>
<ul>
<li>d</li>
<li>e</li>
</ul>
...
In my existing (and inadequate code), I do something like:
// Find the text input element by xPath (from chrome inspect)
WebElement element = driver.findElement(By.xpath("//*[#id=\"main\"]/div/div/main/article/div[3]/div[1]/ul[1]"));
List<WebElement> listItems = element.findElements(By.tagName("li"));
... and then I do my test on the list items.
How can I automate something like a method, where I give it a search string ("List ONE") and it returns all list items of the preceding list? (but not the list items of "List Two")?
Just because I'm more familiar with it, I would use a CSS Selector here. Assuming that the order of the lists doesn't change, and the lists of interest are inside that div, the CSS selector for the first list would be:
div.entry-content > ul:nth-of-type(1)
If you really want to stick with XPath, I was able to select the second list using the following:
/div[contains(#class, "entry-content")]//span[contains(text(), 'List TWO')]//parent::*/parent::h4/following-sibling::ul[1]
here we find the div that contains the lists, the heading that contains our target list, then the very first ul list element that follows the selected h4.

Locating child nodes of WebElements in selenium

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.

Finding HTML elements to use in Selenium WebDriver

Using Selenium WebDriver in a java class where I try to find that specific element and then automatically add a needed amount of that element in the input field.
I have an HTML table with each row specifying a type of element and an input field used to add X to the amount of element in the specific row.
<tr>
<td class="non-sortable-table">
<input class="required text" type="text" value="0" name="plate_order{pageFlow.plateorders[0].numberOfPlates}" tabindex="25">
</td>
<td class="non-sortable-table">
<span>20% - White plates</span>
</td>
...
</tr>
I have tried the following in my Java code in order to get that element, but with no luck:
WebElement element = (WebElement) js.executeScript("return ${\"document.getElementsByName('plate_order{pageFlow.plateorders[0].numberOfPlates}')\"");
WebElement element = driver.findElement(By.ByName('plate_order{pageFlow.plateorders[0].numberOfPlates}'))
how could i retreive that element in order to edit its input?
Is it possible when parts of the name of the element is a reference to a controller, i.e. pageFlow?
what if i wanted to retrieve the following element identified by 20% ....
I have tried to get that one using xpath and cssSelector with no luck.
Any suggestions?
To return an element from a JavaScript result, you can use the following syntax (i used jQuery as simplification):
RenderedWebElement webElement = (RenderedWebElement) ((JavascriptExecutor) webDriver).executeScript("return jQuery('input[name^=plate_order]').get(0);");
You can also pass an element which was previosly selected in Selenium:
((JavascriptExecutor) webDriver).executeScript("return doSomethingWithElement(arguments[0]);", webElement);
It looks to me like you might want to use a starts-with type operator in your XPath or CSS.
Something like:
XPath: //input[starts-with(#name, 'plate_order')]
CSS: input[name^='plate_order']
Similar things should work for the span, too (if you know the text):
XPath: //span[starts-with(., '20%')]
CSS/Sizzle*: span:contains('20%')
If you don't know the text, then something this xpath might work.
XPath: //input[starts-with(#name, 'plate_order]/../../td/span
* The contains function is not pure CSS. It's an addition provided by the Sizzle library. This may or may not work, depending on whether Selenium is using a built-in CSS library or injecting Sizzle.

Categories

Resources