Get <p> content using Selenium webdriver - Java - java

<div id="address" class="guideFieldDescription short" style="null;display:none">
<p>
Enter home address for the contact person. Leave blank if you don't have one. Home Address and Mailing Address must be located in the United States. No international addresses will be accepted. If the home addresses and mailing addresses are different, you must fill out both sections.<br>
</p>
I'm trying to get the tag content but I'm getting either null or empty using the script below
WebElement WebElement = driver.findElement(By.xpath("//*[#id='address']"));
List<WebElement> paras = WebElement.findElements(By.tagName("p"));
System.out.println("Size = " + paras.size() + " " + paras.toString() );
for (WebElement para : paras) {
System.out.println(para.getText());}
I get the size is = 1, but the getText() return empty.

Selenium getText() can't get text from elements with display: none, which includes the div and its children p paragraphs. If the elements were visible (weren't set to display: none;), your code would be working.
Instead of using getText(), you can use a JavascriptExecutor to get the innerText of the element if it's invisible. See this possible duplicate question: Using selenium, can I get the text of an invisible element?
Here is a function which gets the inner text
/**
* Get the innerText from an element.
* #param driver the WebDriver
* #param element the element to get innerText from
* #return the element's innerText
*/
public static String getInnerText(WebDriver driver, WebElement element) {
JavascriptExecutor executor = (JavascriptExecutor) driver;
return (String) executor.executeScript("return arguments[0].innerText", element);
}
This function is used in the below code sample.
To see the difference between getText() and getting innerText for visible and invisible elements, here is a complete working example program (NOTE the step in the middle to debug and add the display: none manually):
import io.github.bonigarcia.wdm.WebDriverManager;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
class DemonstrateGetTextVsGetInnerTextForDisplayNoneElements {
public static void main(final String[] args) {
WebDriverManager.chromedriver().setup();
WebDriver driver = new ChromeDriver();
// Let's go to a page that mirrors the use case of a div container,
// with children paragraphs.
// The difference: this page doesn't have display: none set on the container.
driver.get("https://www.google.com/intl/en/about/");
final WebElement container = driver.findElement(By.className("home-hero-copy"));
final List<WebElement> paragraphs = container.findElements(By.tagName("p"));
System.out.println("getText() works as normal for *VISIBLE* containers and paragraphs.");
System.out.println("CONTAINER: " + container.getText());
System.out.println(
"LIST OF PARAGRAPHS, Size = " + paragraphs.size() + " " + paragraphs.toString());
for (final WebElement paragraph : paragraphs) {
System.out.println("PARAGRAPH: " + paragraph.getText());
}
System.out.println("SET THE JAVA DEBUGGER TO PAUSE RIGHT HERE, "
+ "GO INTO THE BROWSER AND INJECT \"display: none;\" "
+ "as a style on the div.home-hero-copy element to make"
+ "the div and its child paragraphs invisible. "
+ "You can do this by using the developer tools elements panel.");
System.out.println("If you've made the container invisible, "
+ "you should notice that in the following block of printouts "
+ "that we've still got references to the WebElements (they aren't stale) "
+ "but when we try to getText() while they are invisible from 'display: none;', "
+ "we won't get any text back.");
System.out.println("CONTAINER: " + container.getText());
System.out.println(
"LIST OF PARAGRAPHS, Size = " + paragraphs.size() + " " + paragraphs.toString());
for (final WebElement paragraph : paragraphs) {
System.out.println("PARAGRAPH: " + paragraph.getText());
}
System.out.println("Now, let's try getting the text via 'innerText' with a Javascript Executor");
System.out.println("CONTAINER: " + getInnerText(driver, container));
System.out.println(
"LIST OF PARAGRAPHS, Size = " + paragraphs.size() + " " + paragraphs.toString());
for (final WebElement paragraph : paragraphs) {
System.out.println("PARAGRAPH: " + getInnerText(driver, paragraph));
}
System.out.println("As you can see, getting inner text works when the element is invisible!");
driver.quit();
}
/**
* Get the innerText from an element.
* #param driver the WebDriver
* #param element the element to get innerText from
* #return the element's innerText
*/
public static String getInnerText(WebDriver driver, WebElement element) {
JavascriptExecutor executor = (JavascriptExecutor) driver;
return (String) executor.executeScript("return arguments[0].innerText", element);
}
}
The output of this program should look like this
Connected to the target VM, address: '127.0.0.1:62943', transport: 'socket'
Starting ChromeDriver 71.0.3578.33 (269aa0e3f0db08097f0fe231c7e6be200b6939f7) on port 15369
Only local connections are allowed.
Nov 13, 2018 11:07:46 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
getText() works as normal for *VISIBLE* containers and paragraphs.
CONTAINER: Our mission is to organize the world’s information and make it universally accessible and useful.
LIST OF PARAGRAPHS, Size = 1 [[[[[ChromeDriver: chrome on MAC (bbb9840f94250510047ac8e04b055d88)] -> class name: home-hero-copy]] -> tag name: p]]
PARAGRAPH: Our mission is to organize the world’s information and make it universally accessible and useful.
SET THE JAVA DEBUGGER TO PAUSE RIGHT HERE, GO INTO THE BROWSER AND INJECT "display: none;" as a style on the div.home-hero-copy element to makethe div and its child paragraphs invisible. You can do this by using the developer tools elements panel.
If you've made the container invisible, you should notice that in the following block of printouts that we've still got references to the WebElements (they aren't stale) but when we try to getText() while they are invisible from 'display: none;', we won't get any text back.
CONTAINER:
LIST OF PARAGRAPHS, Size = 1 [[[[[ChromeDriver: chrome on MAC (bbb9840f94250510047ac8e04b055d88)] -> class name: home-hero-copy]] -> tag name: p]]
PARAGRAPH:
Now, let's try getting the text via 'innerText' with a Javascript Executor
CONTAINER: Our mission is to organize the world’s information and make it universally accessible and useful.
LIST OF PARAGRAPHS, Size = 1 [[[[[ChromeDriver: chrome on MAC (bbb9840f94250510047ac8e04b055d88)] -> class name: home-hero-copy]] -> tag name: p]]
PARAGRAPH: Our mission is to organize the world’s information and make it universally accessible and useful.
As you can see, getting inner text works when the element is invisible!
Disconnected from the target VM, address: '127.0.0.1:62943', transport: 'socket'
Process finished with exit code 0
And just in case Google ever changes their page, here is what the div and its children look like:
<div class="home-hero-copy center">
<p>Our mission is to <span class="color-hero">organize</span> the world’s <span class="color-hero">information</span> and make it <span class="color-hero">universally accessible</span> and <span class="color-hero">useful</span>.</p>
</div>

Related

Searching and adding product in cart of bigbasket using selenium

I was trying to search and add product in the cart using selenium but was not able to successfully do it
driver.get("https://www.bigbasket.com/cl/fruits-vegetables/?nc=nb");
List<WebElement> product = driver.findElements(By.xpath("//div[#qa=\'product\']"));
System.out.println("prdoduct=" + product.size());
for(int i=0;i<product.size();i++)
{
String name = product.get(i).getText();
System.out.println("NAME is" + name);
String xp= "(//button[#qa=\'add\'])" + "["+i+ "]";
System.out.println("xp="+xp);
if(name.contains("Cauliflower"))
{
System.out.println("xp" +xp);
driver.findElement(By.xpath(xp)).click();
}
}
In this previous product is getting selected but when I was debugging it was on the cauliflower but still the previous product is getting selected
there is a chance that some time the element get overlapped by another element. At this time, the normal selenium click will try to click on the overlapped element. so it is better to use js click, it will click the exact element even if it is overlapped
WebElement element= driver.findElement(By.xpath(xp));
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].click();", element);
There is also a chance for issue in your xpath. Since list starts at 0 , you may need to change i in to i+1 in xp for getting current selection add button.do try this xpath too
String xp= "(//button[#qa=\'add\'])" + "["+(i+1)+ "]";
Induce WebDriverWait() and wait for visibilityOfAllElementsLocatedBy() and use following css selector and xpath.
driver.get("https://www.bigbasket.com/cl/fruits-vegetables/?nc=nb");
List<WebElement> product =new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("div[qa='product_name']>a")));
System.out.println("prdoduct=" + product.size());
for(int i=0;i<product.size();i++)
{
String name = product.get(i).getText();
System.out.println("NAME is" + name);
if(name.contains("Cauliflower"))
{
driver.findElement(By.xpath("//div[#qa='product_name']//a[text()='" + name + "']/following::button[1]")).click();
}
}

How to select between e-ticket, mobile ticket and selfprint ticket to click on it

here is my element:
<div id="ctl00_MasterContent_FareOptionsWebPart_FareOptionsFares_ctl123_FarePoint_Outbound28_1" class="FareOptionsFarePoint Outbound Single F28 J1 fakecheck filtered fakechecked" **data-fulfilment="ToD Kiosk SelfPrint MobileTicket eTicket"** radid="28_1" fare="28" data-selectable="1">£35.00</div>
I can't use any other tags because all are dynamic. Only I can use to locate is data-fulfilment.
1 - If e-ticket available then it should be a click.
2 - If Mobile Ticket available then it should be a click.
Below is my locator:
//eticket
private By _cel = By.xpath("//*[#data-fulfilment='ToD Kiosk SelfPrint MobileTicket eTicket']");
//mobile ticket if e-ticket not available
private By _celM = By.xpath("//*[#data-fulfilment='ToD Kiosk SelfPrint MobileTicket']");
my code is below:
WebElement element = driver.findElement(_table3);
List <WebElement> row = element.findElements(_row);
System.out.println("Up to now I find " + getTextFromElement(_row) + " element on page.");
System.out.println("No of Rows: " + row.size());
List <WebElement> cel = element.findElements(_cel);
System.out.println("Up to now I find " + getTextFromElement(_cel) + " element on page.");
System.out.println("No of Cell: " + cel.size());
String SelfPrint = " SelfPrint ";
if (cel.contains(SelfPrint)) {
clickOnElement(_cel);
}
else
{
clickOnElement(_celM);
}
My question is How I can select between e-ticket, mobile ticket and self-print ticket available or not? please guide me with coding as well.
Thank you
If only one or the other option will be available (your _cel element or your _celm element), you could just create a single By selector that would work for both, and click on it.
Based on your example XPaths, this would work in both scenarios (provided there are no other elements that contain "ToD Kiosk SelfPrint MobileTicket "data-fulfillment attribute).
private by _cel = By.XPath("//*[starts-with(#Data-Fulfilment, 'ToD Kiosk SelfPrint MobileTicket')]");
See Xpath Cheatsheet
Since both elements have the Data-Fulfilment attribute start with "ToD Kiosk SelfPrint MobileTicket", this would work for both. So long as only one is on the page at the time. Simply clicking with this selector should work.

How to display all the values in auto drop down list

How to display all the values in auto drop down list using selenium web driver when it is developed using <div> tag.
My code
driver.get("https://www.yatra.com/");// URl of yatra
String origin ="(//input[#name='flight_origin_city'])[1]";// text box for entering origin
driver.findElement(By.xpath(origin)).sendKeys("Bangalore");
String destination = "(//input[#name='flight_destination_city'])[1]";// text for entering destination
driver.findElement(By.xpath(destination)).sendKeys("M");
List<WebElement> lists= driver.findElements(By.xpath("(//div[#class='viewport'])[2]")); // this is the div where all the values are present.
for (int i=0; i<lists.size();++i){
System.out.println(lists.get(i).getText());// getting the text for all the values.
}
The xpath "(//div[#class='viewport'])[2]" gives you a single element, not list. This is also not where the options are stored, its the parent of the container where they are stored. Try
List<WebElement> lists= driver.findElements(By.xpath("(//div[#class='overview'])[3]//li"));
Your case is a bit difficult, but that's what makes it worth exploring. Here's a step wise dissection of the problem:
Desktop notification dialog
First of all, when I tried to run your test in Chrome, the "Do you want to allow Desktop notifications?" dialog interfered with the test (it popped up some time after the second sendKeys which is why the text box lost its focus and one of the entries got accepted which made it impossible to find the other suggestions).
I worked around that using this tip: Disable Chrome notifications (Selenium)
Finding the right container
You got pretty far already by identifying that you can locate the container of the suggestions as By.xpath("(//div[#class='viewport'])[2]"). From there though, you need to look more specifically for the right child elements. Also as this is dynamic content, it is advisable to use an explicit wait statement to make sure the test doesn't fail prematurely in case the element takes longer to load.
I changed your test so that it would look the <li> elements which are children of the <div> elements with the viewport class. Those elements have children which format the city, airport and country names accordingly. To get the text of those, you have to drill down a bit more into the DOM structure. Here's the solution for that:
WebElement list = new WebDriverWait(driver, 2000).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(//div[#class='viewport'])[2]")));
List<WebElement> elements = list.findElements(By.tagName("li"));
for (WebElement element : elements) {
String city = element.findElement(By.className("ac_cityname")).getText();
String airport = element.findElement(By.className("ac_airportname")).getText();
String country = element.findElement(By.className("ac_country")).getText();
System.out.println(city + " " + airport + " (" + country + ")");
}
So far, so good. But note that this solution gives you the followiing output:
Mumbai (BOM) Chatrapati Shivaji (India)
Madras (MAA) Chennai (India)
Chennai (MAA) Chennai (India)
Madurai (IXM) (India)
()
()
()
()
()
()
Scrolling
This is because of another speciality of your page: It only renders the text for the elements that are actually in view. So in addition to the code above, apply this trick to make sure all elements are being scrolled to before retrieving their text: Scroll Element into View with Selenium
Long story short: Here's my final version:
driver.get("https://www.yatra.com/");// URl of yatra
String origin ="(//input[#name='flight_origin_city'])[1]";// text box for entering origin
driver.findElement(By.xpath(origin)).sendKeys("Bangalore");
String destination = "(//input[#name='flight_destination_city'])[1]";// text for entering destination
driver.findElement(By.xpath(destination)).sendKeys("M");
WebElement list = new WebDriverWait(driver, 2000).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(//div[#class='viewport'])[2]")));
List<WebElement> elements = list.findElements(By.tagName("li"));
for (WebElement element : elements) {
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
String city = element.findElement(By.className("ac_cityname")).getText();
String airport = element.findElement(By.className("ac_airportname")).getText();
String country = element.findElement(By.className("ac_country")).getText();
System.out.println(city + " " + airport + " (" + country + ")");
}
That should give you the following output when running in Google Chrome:
Mumbai (BOM) Chatrapati Shivaji (India)
Madras (MAA) Chennai (India)
Chennai (MAA) Chennai (India)
Madurai (IXM) Madurai (India)
Mangalore (IXE) Bajpe (India)
Mysore (MYQ) Mysore (India)
Coorg (MYQ) (nearest airport Mysore) (India)
Male (MLE) Male (Maldives)
Melbourne (MEL) Tullamarine (Australia)
Mauritius (MRU) Plaisancet (Mauritius)

How to use Selenium get text from an element not including its sub-elements

HTML
<div id='one'>
<button id='two'>I am a button</button>
<button id='three'>I am a button</button>
I am a div
</div>
Code
driver.findElement(By.id('one')).getText();
I've seen this question pop up a few times in the last maybe year or so and I've wanted to try writing this function... so here you go. It takes the parent element and removes each child's textContent until what remains is the textNode. I've tested this on your HTML and it works.
/**
* Takes a parent element and strips out the textContent of all child elements and returns textNode content only
*
* #param e
* the parent element
* #return the text from the child textNodes
*/
public static String getTextNode(WebElement e)
{
String text = e.getText().trim();
List<WebElement> children = e.findElements(By.xpath("./*"));
for (WebElement child : children)
{
text = text.replaceFirst(child.getText(), "").trim();
}
return text;
}
and you call it
System.out.println(getTextNode(driver.findElement(By.id("one"))));
Warning: the initial solution (deep below) won't workI opened an enhancement request: 2840 against the Selenium WebDrive and another one against the W3C WebDrive specification - the more votes, the sooner they'll get enough attention (one can hope). Until then, the solution suggested by #shivansh in the other answer (execution of a JavaScript via Selenium) remains the only alternative. Here's the Java adaptation of that solution (collects all text nodes, discards all that are whitespace only, separates the remaining by \t):
WebElement e=driver.findElement(By.xpath("//*[#id='one']"));
if(driver instanceof JavascriptExecutor) {
String jswalker=
"var tw = document.createTreeWalker("
+ "arguments[0],"
+ "NodeFilter.SHOW_TEXT,"
+ "{ acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT;} },"
+ "false"
+ ");"
+ "var ret=null;"
+ "while(tw.nextNode()){"
+ "var t=tw.currentNode.wholeText.trim();"
+ "if(t.length>0){" // skip over all-white text values
+ "ret=(ret ? ret+'\t'+t : t);" // if many, tab-separate them
+ "}"
+ "}"
+ "return ret;" // will return null if no non-empty text nodes are found
;
Object val=((JavascriptExecutor) driver).executeScript(jswalker, e);
// ---- Pass the context node here ------------------------------^
String textNodesTabSeparated=(null!=val ? val.toString() : null);
// ----^ --- this is the result you want
}
References:
TreeWalker - supported by all browsers
Selenium Javascript Executor
Initial suggested solution - not working - see enhancement request: 2840
driver.findElement(By.id('one')).find(By.XPath("./text()").getText();
In a single search
driver.findElement(By.XPath("//[#id=one]/text()")).getText();
See XPath spec/Location Paths the child::text() selector.
I use a function like below:
private static final String ALL_DIRECT_TEXT_CONTENT =
"var element = arguments[0], text = '';\n" +
"for (var i = 0; i < element.childNodes.length; ++i) {\n" +
" var node = element.childNodes[i];\n" +
" if (node.nodeType == Node.TEXT_NODE" +
" && node.textContent.trim() != '')\n" +
" text += node.textContent.trim();\n" +
"}\n" +
"return text;";
public String getText(WebDriver driver, WebElement element) {
return (String) ((JavascriptExecutor) driver).executeScript(ALL_DIRECT_TEXT_CONTENT, element);
}
var outerElement = driver.FindElement(By.XPath("a"));
var outerElementTextWithNoSubText = outerElement.Text.Replace(outerElement.FindElement(By.XPath("./*")).Text, "");
Similar solution to the ones given, but instead of JavaScript or setting text to "", I remove elements in the XML and then get the text.
Problem:
Need text from 'root element without children' where children can be x levels deep and the text in the root can be the same as the text in other elements.
The solution treats the webelement as an XML and replaces the children with voids so only the root remains.
The result is then parsed. In my cases this seems to be working.
I only verified this code in a environment with Groovy. No idea if it will work in Java without modifications. Essentially you need to replace the groovy libraries for XML with Java libraries and off you go I guess.
As for the code itself, I have two parameters:
WebElement el
boolean strict
When strict is true, then really only the root is taken into account. If strict is false, then markup tags will be left. I included in this whitelist p, b, i, strong, em, mark, small, del, ins, sub, sup.
The logic is:
Manage whitelisted tags
Get element as string (XML)
Parse to an XML object
Set all child nodes to void
Parse and get text
Up until now this seems to be working out.
You can find the code here: GitHub Code

Unable get options from a Select using Selenium

I am having difficulty selecting options from a dropdown list using Selenium Webdriver. Below is the HTML snippet:
<span id="id14">
<div class="stadium-input-row">
<span class="inputContainer">
<select id="id1f" class="departurePoint stadiumSelect" onchange="var wcall=..........">
<option value="">Please select</option>
<option value="BHX"> Birmingham - (BHX) </option>
<option value="GLA"> Glasgow - (GLA) </option>
<option value="LON"> London - (LON) </option>
<option value="MAN"> Manchester - (MAN) </option>............
The select tag id changes each time the DOM is loaded.
The select tag is greyed out until it is interacted with.
My code
Select oSelect = new Select(driver.findElement(By.xpath("(.//select)[1]"));
oSelect.selectByVisibleText("Birmingham");
Error
org.openqa.selenium.NoSuchElementException: Cannot locate element
with text: Birmingham
In debugging mode, the dropdown does not seem to be activated (clicked) by the driver.
There's a weird (at least it's weird to me) thing going on on that site. The SELECT that you are trying to access is permanently hidden (which means it can't be interacted with using Selenium). Users interact with DIVs, etc. via a fake dropdown (it's not a SELECT) and the result of those selections are stored in the hidden SELECT. There are two ways to accomplish your task.
Deal with what you can see.
This is really a pain on this site. I think it can be ultimately done but I don't want to spend any more time on it myself so I'll show you the door and you'll have to pick up where I left off. The code below will open the departure dropdown. From there, you find the departure airport and click on it. Done. Harder than it sounds...
driver.findElement(By.cssSelector("div.custom-select.departurePoint.airportSelect")).click();
Cheat and use JavascriptExecutor.
NOTE: By doing this you are no longer executing a real user scenario since users can't click on hidden elements or inject Javascript commands into the page. As long as you are OK with this, here's a sample.
This code executes Javascript on the page using the JavascriptExecutor. You pass the function a string you are looking for in the options, e.g. you can pass "EMA" or "East Midlands - (EMA)" or anywhere inbetween. The JS code will grab the hidden SELECT, search through the OPTIONS, and select the first that matches.
Also note: You will not see the UI update with the selection. Once you click SEARCH, it will work. I have tried it myself and it works.
Yet another note: I use Eclipse as my editor so the // #formatter:off you see in the code below keeps Eclipse from wrapping/reformatting the extra long string that contains the JS code. You can leave or remove it as you like. I like it there because I can still read the JS code with it formatted and indented like it is and I don't want Eclipse messing it up.
selectOption("EMA");
public void selectOption(String option)
{
// #formatter:off
String script =
"function selectOption(s) {\r\n" +
" var sel = document.querySelector('select.departurePoint.airportSelect');\r\n" +
" for (var i = 0; i < sel.options.length; i++)\r\n" +
" {\r\n" +
" if (sel.options[i].text.indexOf(s) > -1)\r\n" +
" {\r\n" +
" sel.options[i].selected = true;\r\n" +
" break;\r\n" +
" }\r\n" +
" }\r\n" +
"}\r\n" +
"return selectOption('" + option + "');";
// #formatter:on
((JavascriptExecutor) driver).executeScript(script);
}
what you need to do:
List<WebElement> options = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select")).findElements(By.tagName("option"));
that will create you list of option tags as WebElement objects
or
Select oSelect = new Select(driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
or just take the select as WebElement object
WebElement selectElement = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
Hovering element
Actions action = new Actions(driver);
WebElement hoverElement = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
action.moveToElement(hoverElement);
action.click().build().perform();
As your exception clearly says Cannot locate element with text: Birmingham means it compares with whole visible text option so you should try as below :-
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement select = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(.//select)[1]")));
Select oSelect = new Select(select);
oSelect.selectByVisibleText("Birmingham - (BHX)");
or
oSelect.selectByIndex(1);
or
oSelect.selectByValue("BHX");
Edited :- If unfortunately none of the above solution works you should try using JavascriptExecutor as below :-
((JavascriptExecutor)driver).executeScript("var select = arguments[0]; for(var i = 0; i < select.options.length; i++){ if(select.options[i].text.indexOf(arguments[1]) != -1){ select.options[i].selected = true; } }",
driver.findElement(By.xpath("(.//select)[1]")),
"Birmingham");
Hope it will help you..:)
Select will not work in all the situations. Try this code
List <WebElement> options = driver.findElements(By.xpath("Target Element with Options"));
String element;
for(int i=0;i<options.size();i++)
{
element = options[i].get(i).getAttribute("value");
if(element.equals("BHX")){
options.get(i).click();
}
}

Categories

Resources