Selenium StaleElementReferenceException while using EventFiringWebDriver - java

Dear all I am using Selenium EventFiringWebDriver to record the called web driver methods. I recognised that I often get a "StaleReferenceException" while when I just use the HtmlUnitDriver alone I don't have the issue.
I also recognised that the call i.e. "click()" has been performed in the browser although the "StaleElementReferenceException" got thrown.
Has anybody an idea while the EventFiringWebDriver runs into such problems while using the HtmlUnitDriver or FirefoxDriver standalone not?
Could it be that the WebElements get updated by the origin drivers at runtime while the wrapped WebElements of the EventFiringWebDriver not?
Or should we raise this as a bug for the EventFiringWebDriver implementation?
Example code with EventFiringWebDriver - throws StaleElementReferenceException
HtmlUnitDriver driver = new HtmlUnitDriver();
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
ExtentReports extent = new ExtentReports ("report.html", true);
ExtentTest logger = extent.startTest("test");
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
eventDriver.register(new MyWebDriverEventListener(logger));
try {
WebElement element = null;
eventDriver.get("https://www.google.com");
element = eventDriver.findElement(By.xpath("//input[#type='text']"));
element.sendKeys("Test");
element.submit();
Thread.sleep(2000);
element = eventDriver.findElement(By.xpath("//div[#id='search']//a"));
String title = element.getText();
// HERE the StaleElementReferenceException get thrown ALTHOUGH the "click" event get processed by the browser, it loads already the page
try {
element.click();
} catch(StaleElementReferenceException ex) {
}
Thread.sleep(2000);
Assert.assertEquals(title, eventDriver.getTitle());
logger.log(LogStatus.PASS,"end","Test passed");
} catch(AssertionError error) {
logger.log(LogStatus.FAIL,"end","Test failed:" + error.getMessage());
throw error;
}
finally {
extent.endTest(logger);
extent.flush();
extent.close();
eventDriver.quit();
}
The same code - just using the HtmlUnitDriver directly, works without any problems
HtmlUnitDriver driver = new HtmlUnitDriver();
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
ExtentReports extent = new ExtentReports ("report.html", true);
ExtentTest logger = extent.startTest("test");
try {
WebElement element = null;
driver.get("https://www.google.com");
element = driver.findElement(By.xpath("//input[#type='text']"));
element.sendKeys("Test");
element.submit();
Thread.sleep(2000);
element = driver.findElement(By.xpath("//div[#id='search']//a"));
String title = element.getText();
element.click();
Thread.sleep(2000);
Assert.assertEquals(title, driver.getTitle());
logger.log(LogStatus.PASS,"end","Test passed");
} catch(AssertionError error) {
logger.log(LogStatus.FAIL,"end","Test failed:" + error.getMessage());
throw error;
}
finally {
extent.endTest(logger);
extent.flush();
extent.close();
driver.quit();
}

After studying the stack trace of the stale exception I recognised that the issue comes not directly from the EventFiringWebDriver. It gets thrown by my listener implementation of the WebDriverEventListener while I try to get the tag name of the element after the click has been performed.
For me it looks like that the design of the WebDriverEventListener is not optimal. With other words you may not able to use the passed WebElement in the "afterXXX" methods, otherwise you may risk a stale exception. Instead you should use the "beforeXXX" methods in order to retrieve the details of the elements.
Stacktrace of my StaleElementReferenceException
at org.openqa.selenium.htmlunit.HtmlUnitDriver.assertElementNotStale(HtmlUnitDriver.java:963)
at org.openqa.selenium.htmlunit.HtmlUnitWebElement.assertElementNotStale(HtmlUnitWebElement.java:734)
at org.openqa.selenium.htmlunit.HtmlUnitWebElement.getTagName(HtmlUnitWebElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement$1.invoke(EventFiringWebDriver.java:332)
at com.sun.proxy.$Proxy18.getTagName(Unknown Source)
at ch.megloff.test.SimpleExtentReportWebDriverEventListener.afterClickOn(SimpleExtentReportWebDriverEventListener.java:111)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.openqa.selenium.support.events.EventFiringWebDriver$1.invoke(EventFiringWebDriver.java:81)
at com.sun.proxy.$Proxy16.afterClickOn(Unknown Source)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement.click(EventFiringWebDriver.java:346)
..s
Java Code snippet of "getTagName()" of the underlying HtmlUnit Implementation
public String getTagName() {
assertElementNotStale();
return element.getNodeName();
}
My "Error-prone" listener implementation for this "afterClickOn" Method - the "getTagName()" should not be called after a click has been performed
public class MyWebDriverEventListener extends AbstractWebDriverEventListener {
...
#Override
public void afterClickOn(WebElement element, WebDriver driver) {
// bad implementation, click has been already performed
// so you may risk to have a stale exception in case the
// browser switched already to the other page (DOM got changed)
logEvent("Clicked on tag: " + element.getTagName() + " with href: " + element.getAttribute("href"));
}
}

Related

About Automated testing, huge number of steps and perfomance issues [duplicate]

I am trying to check if web page is loaded completed or not (i.e. checking that all the control is loaded) in selenium.
I tried below code:
new WebDriverWait(firefoxDriver, pageLoadTimeout).until(
webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
but even if page is loading above code does not wait.
I know that I can check for particular element to check if its visible/clickable etc but I am looking for some generic solution
As you mentioned if there is any generic function to check if the page has completely loaded through Selenium the answer is No.
First let us have a look at your code trial which is as follows :
new WebDriverWait(firefoxDriver, pageLoadTimeout).until(webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
The parameter pageLoadTimeout in the above line of code doesn't really reseambles to actual pageLoadTimeout().
Here you can find a detailed discussion of pageLoadTimeout in Selenium not working
Now as your usecase relates to page being completely loaded you can use the pageLoadStrategy() set to normal [ the supported values being none, eager or normal ] using either through an instance of DesiredCapabilities Class or ChromeOptions Class as follows :
Using DesiredCapabilities Class :
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
public class myDemo
{
public static void main(String[] args) throws Exception
{
System.setProperty("webdriver.gecko.driver", "C:\\Utility\\BrowserDrivers\\geckodriver.exe");
DesiredCapabilities dcap = new DesiredCapabilities();
dcap.setCapability("pageLoadStrategy", "normal");
FirefoxOptions opt = new FirefoxOptions();
opt.merge(dcap);
WebDriver driver = new FirefoxDriver(opt);
driver.get("https://www.google.com/");
System.out.println(driver.getTitle());
driver.quit();
}
}
Using ChromeOptions Class :
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.PageLoadStrategy;
public class myDemo
{
public static void main(String[] args) throws Exception
{
System.setProperty("webdriver.gecko.driver", "C:\\Utility\\BrowserDrivers\\geckodriver.exe");
FirefoxOptions opt = new FirefoxOptions();
opt.setPageLoadStrategy(PageLoadStrategy.NORMAL);
WebDriver driver = new FirefoxDriver(opt);
driver.get("https://www.google.com/");
System.out.println(driver.getTitle());
driver.quit();
}
}
You can find a detailed discussion in Page load strategy for Chrome driver (Updated till Selenium v3.12.0)
Now setting PageLoadStrategy to NORMAL and your code trial both ensures that the Browser Client have (i.e. the Web Browser) have attained 'document.readyState' equal to "complete". Once this condition is fulfilled Selenium performs the next line of code.
You can find a detailed discussion in Selenium IE WebDriver only works while debugging
But the Browser Client attaining 'document.readyState' equal to "complete" still doesn't guarantees that all the JavaScript and Ajax Calls have completed.
To wait for the all the JavaScript and Ajax Calls to complete you can write a function as follows :
public void WaitForAjax2Complete() throws InterruptedException
{
while (true)
{
if ((Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")){
break;
}
Thread.sleep(100);
}
}
You can find a detailed discussion in Wait for ajax request to complete - selenium webdriver
Now, the above two approaches through PageLoadStrategy and "return jQuery.active == 0" looks to be waiting for indefinite events. So for a definite wait you can induce WebDriverWait inconjunction with ExpectedConditions set to titleContains() method which will ensure that the Page Title (i.e. the Web Page) is visible and assume the all the elements are also visible as follows :
driver.get("https://www.google.com/");
new WebDriverWait(driver, 10).until(ExpectedConditions.titleContains("partial_title_of_application_under_test"));
System.out.println(driver.getTitle());
driver.quit();
Now, at times it is possible though the Page Title will match your Application Title still the desired element you want to interact haven't completed loading. So a more granular approach would be to induce WebDriverWait inconjunction with ExpectedConditions set to visibilityOfElementLocated() method which will make your program wait for the desired element to be visible as follows :
driver.get("https://www.google.com/");
WebElement ele = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("xpath_of_the_desired_element")));
System.out.println(ele.getText());
driver.quit();
References
You can find a couple of relevant detailed discussions in:
Selenium IE WebDriver only works while debugging
Selenium how to manage wait for page load?
I use selenium too and I had the same problem, to fix that I just wait also for the jQuery to load.
So if you have the same issue try this also
((Long) ((JavascriptExecutor) browser).executeScript("return jQuery.active") == 0);
You can wrap both function in a method and check until both page and jQuery is loaded
Implement this, Its working for many of us including me. It includes Web Page wait on JavaScript, Angular, JQuery if its there.
If your Application is containing Javascript & JQuery you can write code for only those,
By define it in single method and you can Call it anywhere:
// Wait for jQuery to load
{
ExpectedCondition<Boolean> jQueryLoad = driver -> ((Long) ((JavascriptExecutor) driver).executeScript("return jQuery.active") == 0);
boolean jqueryReady = (Boolean) js.executeScript("return jQuery.active==0");
if (!jqueryReady) {
// System.out.println("JQuery is NOT Ready!");
wait.until(jQueryLoad);
}
wait.until(jQueryLoad);
}
// Wait for ANGULAR to load
{
String angularReadyScript = "return angular.element(document).injector().get('$http').pendingRequests.length === 0";
ExpectedCondition<Boolean> angularLoad = driver -> Boolean.valueOf(((JavascriptExecutor) driver).executeScript(angularReadyScript).toString());
boolean angularReady = Boolean.valueOf(js.executeScript(angularReadyScript).toString());
if (!angularReady) {
// System.out.println("ANGULAR is NOT Ready!");
wait.until(angularLoad);
}
}
// Wait for Javascript to load
{
ExpectedCondition<Boolean> jsLoad = driver -> ((JavascriptExecutor) driver).executeScript("return document.readyState").toString()
.equals("complete");
boolean jsReady = (Boolean) js.executeScript("return document.readyState").toString().equals("complete");
// Wait Javascript until it is Ready!
if (!jsReady) {
// System.out.println("JS in NOT Ready!");
wait.until(jsLoad);
}
}
Click here for Reference Link
Let me know if you stuck anywhere by implementing.
It overcomes the use of Thread or Explicit Wait.
public static void waitForPageToLoad(long timeOutInSeconds) {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
};
try {
System.out.println("Waiting for page to load...");
WebDriverWait wait = new WebDriverWait(Driver.getDriver(), timeOutInSeconds);
wait.until(expectation);
} catch (Throwable error) {
System.out.println(
"Timeout waiting for Page Load Request to complete after " + timeOutInSeconds + " seconds");
}
}
Try this method
This works for me well with dynamically rendered websites:
Wait for complete page to load
WebDriverWait wait = new WebDriverWait(driver, 50);
wait.until((ExpectedCondition<Boolean>) wd -> ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
Make another implicit wait with a dummy condition which would always fail
try {
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(),'" + "This text will always fail :)" + "')]"))); // condition you are certain won't be true
}
catch (TimeoutException te) {
}
Finally, instead of getting the html source - which would in most of one page applications would give you a different result , pull the outerhtml of the first html tag
String script = "return document.getElementsByTagName(\"html\")[0].outerHTML;";
content = ((JavascriptExecutor) driver).executeScript(script).toString();
There is a easy way to do it. When you first request the state via javascript, it tells you that the page is complete, but after that it enters the state loading. The first complete state was the initial page!
So my proposal is to check for a complete state after a loading state. Check this code in PHP, easily translatable to another language.
$prevStatus = '';
$checkStatus = function ($driver) use (&$prevStatus){
$status = $driver->executeScript("return document.readyState");
if ($prevStatus=='' && $status=='loading'){
//save the previous status and continue waiting
$prevStatus = $status;
return false;
}
if ($prevStatus=='loading' && $status=='complete'){
//loading -> complete, stop waiting, it is finish!
return true;
}
//continue waiting
return false;
};
$this->driver->wait(20, 150)->until($checkStatus);
Checking for a element to be present also works well, but you need to make sure that this element is only present in the destination page.
Something like this should work (please excuse the python in a java answer):
idle = driver.execute_async_script("""
window.requestIdleCallback(() => {
arguments[0](true)
})
""")
This should block until the event loop is idle which means all assets should be loaded.

"driver cannot be resolved" in Java program

I'm new to the programming world so I wouldn't know how to fix this.
`#Test
public void LoginEmail() {
driver.findElement(By.id("email_button")).sendKeys("xxxxxxxx#gmail.com");`
At driver.findElement , driver is underlined red. When I hover over it these are my options.
Couldn't copy the options, so I took a screenshot:
you should initialize it first :
try {
WebDriver driver = new AndroidDriver();
// And now use this to visit Google
driver.get("http://www.google.com");
// Find the text input element by its name
WebElement element = driver.findElement(By.name("q"));
// Enter something to search for
element.sendKeys("Cheese!");
// Now submit the form. WebDriver will find the form for us from the element
element.submit();
// Check the title of the page
System.out.println("Page title is: " + driver.getTitle());
driver.quit();
} catch (Exception e) {
e.printStackTrace();
}
AndroidDriver driver = new AndroidDriver(new URL("localhost:4723/wd/hub"), cap);
This should be a class or local accessibility. Declare as a class object i.e in this text fixture or in the parent class which is inherited by this class or initialize locally i.e. in the test method.

Can't find an element by name using chrome driver in selenium?

I have my simple selenium program that validate if the value search box in the google is equal to hello world but i got this error:
Exception in thread "main"
org.openqa.selenium.NoSuchElementException: no such element: Unable to
locate element: {"method":"name","selector":"q"}....
Here's my complete code
public class SimpleSelenium {
WebDriver driver = null;
public static void main(String args[]) {
SimpleSelenium ss = new SimpleSelenium();
ss.openBrowserInChrome();
ss.getPage();
ss.listenForHelloWorld();
ss.quitPage();
}
private void openBrowserInChrome(){
System.setProperty("webdriver.chrome.driver", "C:/chromedriver.exe");
driver = new ChromeDriver();
}
private void quitPage() {
driver.quit();
}
private void getPage() {
driver.get("http://www.google.com");
}
private void listenForHelloWorld() {
WebElement searchField = driver.findElement(By.name("q"));
int count = 1;
while (count++ < 20) {
if (searchField.getAttribute("value").equalsIgnoreCase("hello world")) {
break;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Do you wait until the page is ready and element displayed?
I've often got this error when the page is still loading. You could add something like
(MochaJS example, pretty much the same API for JAVA tests)
test.it('should check the field existence', function (done) {
let field_by = By.id(ID_OF_THE_FIELD);
driver.wait(until.elementLocated(field_by,
driver.wait(until.elementIsVisible(driver.findElement(field_by)), TIME_TO_WAIT_MS);
done();
});
You wait until the element is visible. If it failed, the timeout of TIME_TO_WAIT_MS will be raised.
The google search bar will never have "hello world" in it because you haven't typed it in?
Also the search field value doesn't seem to update when you type in a search (if you inspect the element using the Console).
If your just learning I would just write a test like this and the click the search button, then confirm the "hello world" text in the search results:
WebElement searchField = driver.findElement(By.name("q"))
searchField.sendKeys("Hello World")
//Add code to click search button
//Add code to assert results on next page
Also I would completely change your listenForHelloWorld() method and use the built in WebDriver ExpectedConditions:
new WebDriverWait(driver, 10)
.until(ExpectedConditions.textToBePresentInElement(searchField, "Hello World"))

Selenium unable to find element in collapsable div

I was trying to automate the Make My Trip site using Selenium. These are the steps I took:
Search for MakeMyTrip in Google -> Done
Open makemytrip and change country to US -> Done
Click on feedback -> Done
Trying to fill feedback form -> Error
It's saying, unable to find the element.
I have tried the following:
1. Tried finding the element by id
2. Tried finding the element by xpath
//div[#class='feedback-form-container']//form[#id='feedbackForm']//input[#id='field_name_NAME']"
Code:
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseURL = "http://www.google.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void makeMyTriptest() throws Exception {
System.out.println("Entered this loop");
driver.get(baseURL + "/?gws_rd=ssl");
driver.findElement(By.id("lst-ib")).sendKeys("makemytrip");
System.out.println("send keys successful");
driver.findElement(By.linkText("Flights - MakeMyTrip")).click();
driver.findElement(By.id("country_links")).click();
driver.findElement(By.xpath("//*[#id='country_dropdown']//p//a[#href='http://us.makemytrip.com/']")).click();
driver.findElement(By.xpath("//div[#id='webklipper-publisher-widget-container-content-expand-collapse']")).click();
//entering feedback details
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
//driver.findElement(By.id("field_name_NAME")).sendKeys("SubbaRao");
driver.findElement(By.xpath("//div[#class='feedback-form-container']//form[#id='feedbackForm']//input[#id='field_name_NAME']")).sendKeys("SubbaRao");
//driver.findElement(By.id("field_email_EMAIL")).sendKeys("test#test.com");
}
The Feedback form is located inside an iframe. You have to switch into it's context:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
//WebElement iFrame = driver.findElement(By.xpath("//*[id='print_ticket_overlayiframe']"));
driver.switchTo().defaultContent();
driver.switchTo().frame("webklipper-publisher-widget-container-frame");
//driver.findElement(By.id("field_name_NAME")).sendKeys("SubbaRao");
driver.findElement(By.xpath("//*[#id='field_name_NAME']")).sendKeys("SubbaRao");
now while you are in the iframe, search for the input
Works for me.

How to perform drag drop in selenium grid

I was installed selenium grid successfully but not able to perform drag drop operation in selenium grid. if i run the code in testng it is working as expected, bu for grid it fail i learning selenium grid so unable to fix this issue,
jar: selenium-server-standalone-2.45.0.jar
Selenium JAR: All jar from "selenium-2.45.0" (latest)
CODE:
public class DragdropElements {WebDriver driver;
String nodeURL;
#BeforeTest
public void draganddrop() throws MalformedURLException
{
// driver = new FirefoxDriver();
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Jdk14Logger");
nodeURL = "http://localhost:4444/wd/hub";
DesiredCapabilities cap = new DesiredCapabilities();
cap.setBrowserName("firefox");
/// cap.setVersion("31.0");
//cap.setPlatform(Platform.VISTA);
driver = new RemoteWebDriver(new URL(nodeURL),cap);
driver.get("http://only-testing-blog.blogspot.in/2014/09/drag-and-drop.html");
}
#Test
public void DragdropElements1() throws InterruptedException
{
WebElement DragFrom = driver.findElement(By.xpath("//*[#id='dragdiv']"));
WebElement DragTo = driver.findElement(By.xpath("//*[#id='dropdiv']"));
Actions builder = new Actions(driver);
Action dragAndDrop3 = builder.dragAndDrop(DragFrom, DragTo).build();
dragAndDrop3.perform();
String Texttocompare = driver.findElement(By.xpath("//*[#id='Blog1']/div[1]/div/div/div/div[1]/h3")).getText();
System.out.println(""+Texttocompare);
Assert.assertEquals(Texttocompare, "Drag and Drop");
}
}
Exception:
java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebDriver cannot be cast to org.openqa.selenium.interactions.HasInputDevices
at org.openqa.selenium.interactions.Actions.(Actions.java:41)
at qa.DragdropElements.DragdropElements1(DragdropElements.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
Try this and it might work
Actions actionsBuilder = new Actions(driver);
builder.keyDown(Keys.CONTROL)
.click(elementName)
.keyUp(Keys.CONTROL);
Action action = actionsBuilder.build();
action.perform();
Not sure why drag and drop isn't working for you. If you're running multi threads in your nodes try running with just tone thread. Also it's worth mentioning your node OS information.

Categories