Selenium : Handling Loading screens obscuring the web elements. (Java) - java

I'm writing an automated test case for a web page. Here's my scenario.
I have to click and type on various web elements in an html form. But, sometimes while typing on a text field, an ajax loading image appears , fogging all elements i want to interact with. So, I'm using web-driver wait before clicking on the actual elements like below,
WebdriverWait innerwait=new WebDriverWait(driver,30);
innerwait.until(ExpectedConditions.elementToBeClickable(By.xpath(fieldID)));
driver.findelement(By.xpath(fieldID)).click();
But the wait function returns the element even if it is fogged by another image and is not clickable. But the click() throws an exception as
Element is not clickable at point (586.5, 278).
Other element would receive the click: <div>Loading image</div>
Do I have to check every time if the loading image appeared before interacting with any elements?.(I can't predict when the loading image will appear and fog all elements.)
Is there any efficient way to handle this?
Currently I'm using the following function to wait till the loading image disappears,
public void wait_for_ajax_loading() throws Exception
{
try{
Thread.sleep(2000);
if(selenium.isElementPresent("id=loadingPanel"))
while(selenium.isElementPresent("id=loadingPanel")&&selenium.isVisible("id=loadingPanel"))//wait till the loading screen disappears
{
Thread.sleep(2000);
System.out.println("Loading....");
}}
catch(Exception e){
Logger.logPrint("Exception in wait_for_ajax_loading() "+e);
Logger.failedReport(report, e);
driver.quit();
System.exit(0);
}
}
But I don't know exactly when to call the above function, calling it at a wrong time will fail. Is there any efficient way to check if an element is actually clickable? or the loading image is present?
Thanks..

Given the circumstances that you describe, you are forced to verify one of two conditions:
Is the element that you want to click clickable?
Is the reason that blocks the clicks still present?
Normally, if the WebDriver is able to find the element and it is visible, then it is clickable too. Knowing the posible reasons that might block it, I would rather choose to verify those reasons. Besides, it would be more expressive in the test code: you clearly see what you are waiting for, what you are checking before clicking the element, instead of checking the "clickability" with no visible reason for it. I think it gives one (who reads the test) a better understanding of what is (or could be) actually going on.
Try using this method to check that the loading image is not present:
// suppose this is your WebDriver instance
WebDriver yourDriver = new RemoteWebDriver(your_hub_url, your_desired_capabilities);
......
// elementId would be 'loadingPanel'
boolean isElementNotDisplayed(final String elementId, final int timeoutInSeconds) {
try {
ExpectedCondition condition = new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(final WebDriver webDriver) {
WebElement element = webDriver.findElement(By.id(elementId));
return !element.isDisplayed();
}
};
Wait w = new WebDriverWait(yourDriver, timeoutInSeconds);
w.until(condition);
} catch (Exception ex) {
// if it gets here it is because the element is still displayed after timeoutInSeconds
// insert code most suitable for you
}
return true;
}
Perhaps you will have to adjust it a bit to your code (e.g. finding the element once when the page loads and only checking if it is displayed).
If you are not sure when exactly the loading image comes up (though I suppose you do), you should call this method before every click on elements that would become "unclickable" due to the loading image. If the loading image is present, the method will return true as soon as it disappears; if it doesn't disappear within 'timeoutInSeconds' amount of time, the method will do what you choose (e.g. throw an exception with specific message).
You could wrap it together like:
void clickSkippingLoadingPanel(final WebElement elementToClick) {
if (isElementNotDisplayed('loadingPanel', 10)) {
elementToClick.click();
}
}
Hope it helps.

Related

How can I wait for a page loader, which appears and disappears based on API call, to disappear before performing action on element

As the title states, I am trying to perform an action (i.e., sendkeys or click etc.) on an element but the loader (which has ID) is making the action inconsistent.
I have tried quite a few different methods, now I am onto a gimmicky method of sorts with using thread sleep which works semi-consistently, but still gets false-positives to move forward once in a while which results in test failure.
The loader (id="spinner") disappears from the page after an API call completes (sending a signal to script that it is good to go forward), but upon the start of the next call, the loader comes back, and then disappears upon completion for (currently) 3 API calls. I would like to devise a way for this to work regardless of how many calls are implemented.
public void Loader()
{
WebDriverWait wait=new WebDriverWait(driver,50);
WebElement CPloader = driver.findElement(By.id("spinner"));
boolean test = wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("spinner")));
if(test==false) {
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("spinner")));
wait.until(ExpectedConditions.invisibilityOf(CPloader));
threadsleep(2000);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("spinner")));
wait.until(ExpectedConditions.invisibilityOf(CPloader));
threadsleep(2000);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("spinner")));
wait.until(ExpectedConditions.invisibilityOf(CPloader));
}
}
In the test script code, I am usually putting loader method, threadsleep, loader method in an attempt to get it to hold off for long enough but I know this isn't proper... example below:
Util.Loader();
Util.threadsleep(2000);
Util.Loader();
//click element or send keys etc
I am imagining this to be a loop of sorts with an invisibility wait but can't seem to figure it out.
Any pointers in the right direction would be of great help.
I would approach this by waiting for the loader to appear then disappear... and do this in a loop. Once the loader stops appearing, the wait will throw a TimeoutException. Catch that with a try-catch and break out of the loop. You may need to tweak the 10s wait up or down depending on how much time generally passes between loading spinners.
public static void Loader()
{
By loader = By.id("spinner");
WebDriverWait wait = new WebDriverWait(driver, 10);
while (true)
{
try
{
wait.until(ExpectedConditions.visibilityOfElementLocated(loader));
wait.until(ExpectedConditions.invisibilityOfElementLocated(loader));
}
catch (Exception e)
{
break;
}
}
}
AS you mentioned the loader (which has ID) is making the action inconsistently so instead of invoking invisibilityOfElementLocated() method for the loader you can invoke elementToBeClickable() method for the desired element for which you intent to invoke click() or sendKeys() as follows :
Using cssSelector and click :
new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.cssSelector("cssSelector_of_element_to_interact"))).click();
Using xpath and sendKeys :
new WebDriverWait(driver, 30).until(ExpectedConditions.elementToBeClickable(By.xpath("xpath_of_element_to_interact']"))).sendKeys("Test");

How to Deal with ElementNotFound Exception in Selenium WebDriver

Overview : I have already prepared the automation script in Selenium Web driver script in Java which will login into a website and make the selections automatically and once the selections are completed it will run the report.
What I want : I am facing issue while optimizing my automation script.
Brief Explanation : Actually I am familiar with different kinds of wait we are using in Selenium but all those wait i.e implicit,explicit or fluent wait didn't able to help me out in making the code more optimized.Currently I am using Thread.sleep() method everywhere in order to run the script completely without any fail but I know this should not be best practice to be get followed because sometime Elements loads fast and sometime slow because of that either my Script execution took long time or failed based on Element availability.I created one separate method for Webdriver wait and Which I can call for various webelements whenever I needed in my main script but this approach also sometime works or sometime not even though I am passing 800 Second as Time period to wait but if I use Thread.sleep(5000) it will work without any issue not sure why ??
What I want to be have a separate method for wait which can be called in main script whenever required and I want my script to be worked flawless the moment webeelment visible just like what we human do when we interacting with any web.
Note : I have tried ExpectedCondition methods like elementtobeclickable, visibilityOfElementLocated,presenceofElementLocated all of them sometime these work but sometime won't.
Separate Method of wait I have created
public static WebElement waiting(WebDriver driver,String path){
WebDriverWait wait = new WebDriverWait(driver,800);
WebElement element=wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(path)));
return element;
}
Piece of main code where I am calling this Method.
if(nam.equals("Some name"))
{
WebElement e=driver.findElement(By.xpath("1st Webelement path"));
e.click();
System.out.println("Value clicked under First Drop Down is:"+e);
Listing.waiting(driver,"2nd WebElement xpath").click();
//Thread.sleep(5000);
//driver.findElement(By.xpath("2nd WebElement xpath")).click();
System.out.println("Second Dropdown clicked");
}
When I commenting the Thread.sleep() then it will throw the ElementNotFound exception even though I have used 800 Seconds in Webdriver wait method but the moment I removed the comment from Thread.sleep() method it will work.
Kindly Help me in getting the reusable and useful wait method which I can call several times in my main code.
Thanks in Advance !!
This usually works for me (not FluentWait though):
WebDriverWait wait = new WebDriverWait(driver, TIMEOUT);
ExpectedCondition elementIsDisplayed = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver arg0) {
try {
webElement.isDisplayed();
return true;
}
catch (NoSuchElementException e ) {
return false;
}
catch (StaleElementReferenceException f) {
return false;
}
}
};
wait.until(elementIsDisplayed);
Of course, setup TIMEOUT with the amount of time you want to wait for the element to be found (that is in seconds).

element is not attached to the page document

While clicking on javascript components Im getting:
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
One way to fix it is to stop script for some short period of time with :
Thread.sleep(200);
I have my implicitlyWait set for 10 seconds, and I think there wasnt such problems with older selenium
But is there any other way to do it globally ?
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
driver.findElement(By.xpath("//div[#id='content']/div/form")).click();
driver.findElement(By.linkText("Order")).click();
But in the middle of this i have to put sleep's to make it work:
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
Thread.sleep(200);
driver.findElement(By.xpath("//div[#id='content']/div/form")).click();
Thread.sleep(200);
driver.findElement(By.linkText("Order")).click();
Your problems are indicator that you need to construct the test cases better and/or lack of understanding of how the website you are automating works. Especially
stop script for some short period of time with : Thread.sleep(200);
is considered a really bad practice. Whatever you do don't mix implicit and explicit waits, things will start going wrong, explicit waits are the recommended solution.
Waiting for the page to be loaded won't work if (as it seems to be your case) the page is being modified by AJAX operations. Instead of waiting for the page to load, wait for the condition you are testing to become true. This way, you give the AJAX operation time to execute and if your there is a problem you will get an error when the time out occurs.
StaleElementReferenceException is caused by the DOM being refreshed
after you found an element. Remember that a WebElement is a reference
to a specific element on the page, if the DOM get's refreshed this
reference is broken and you need to find the element again to be able to
interact with it.
In my example replace getWebDriver() with your drive instance.
The best practice is to first assert & verify that, that particular
element is present or not. if you see function
assertAndVerifyElement() --- it continuously checks for element for 5
secs and then assert it accordingly.
package com.stackoverflow;
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.services.Init;
public class Issue3 extends Init {
#Test
public void solutionOfIssue() throws InterruptedException {
/*
* The best thing is to first assert & verify that, that particular
* element is present or not. if you see function
* assertAndVerifyElement() --- it continuously checks for element for 5
* secs and then assert it accordingly.
*/
assertAndVerifyElement(By.cssSelector("button.btn.btn-default"));
getWebDriver().findElement(By.cssSelector("button.btn.btn-default")).click();
assertAndVerifyElement(By.xpath("//div[#id='content']/div/form"));
getWebDriver().findElement(By.xpath("//div[#id='content']/div/form")).click();
assertAndVerifyElement(By.linkText("Order"));
getWebDriver().findElement(By.linkText("Order")).click();
}
public void assertAndVerifyElement(By element) throws InterruptedException {
boolean isPresent = false;
for (int i = 0; i < 5; i++) {
try {
if (getWebDriver().findElement(element) != null) {
isPresent = true;
break;
}
} catch (Exception e) {
// System.out.println(e.getLocalizedMessage());
Thread.sleep(1000);
}
}
Assert.assertTrue(isPresent, "\"" + element + "\" is not present.");
}
}
Hope this will work for you. :)
In order to avoid that, you should locate the element again. After you aquired the element, the reference of its Java object might become stale. Do something like:
WebElement element = WebDriver.findElement(By by) again.
EDIT: For your example try this
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
//30s timeout, use timeout not sleep
WebDriverWait wait = new WebDriverWait(driver, 30);
By xpath = By.xpath("//div[#id='content']/div/form")
//wait for element to be clickable, then click
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(xpath));
element.click();
Error "element is not attached to the page document" display when the element is not exist in current page anymore. It happen because you got element for page 1, and used it in page 1 (or the page was refresh after getting the element, HTML was change by AJAX)

Webdriver, detect DOM changing and wait for it

I am using Webdriver in Java and I encountered an issue repeatedly that I can't find a proper solution yet.
It is to do with doing actions on a page that will cause this page DOM to change (for example, Javascript lightbox), then my JUnit test is expecting new elements after the DOM change but my test is getting the old DOM element.
To give you an example, I have a scenario as below.
First of all click “Add item” button in the below image and the light box appears:
Then fill in all the item details and click "Add & Close". You will see the screen below:
Notice that now there is an info message Your item ... has been added.
Now I put keywords in the Search text box and hit enter and the info message will be changed to below:
In my JUnit test, the flow is like below:
....
itemDetailsPage.clickAddAndClose();
itemDetailsPage.searchItemBy("Electricity");
assertEquals("Your search for 'electricity' returned 2 results.",
itemDetailsPage.getInfoMsg());
....
Now this test is not very robust, because if the network is slow, most of the times, getInfoMsg() will return the previous info message Your item ... has been added instead of the latest info message, which causes the test to fail. Just a side note that these two info message have share the same html element id.
The solution I am trying to implement here are:
add explicit wait in clickAddAndClose()
So it looks something like:
public void clickAddAndClose() {
...
clickWhenReady(driver, By.id(addAndCloseButtonId));
...
waitForElementByLocator(driver,By.id(itemInfoMsgId),10);
}
The second wait proves to be useless because, itemInfoMsgId already exist when the user added the item from the add item lightbox.
add waitForPageLoaded() method at the end of clickAddAndClose() to try to wait for the page to finish reloading. The generic method for waitForPageLoaded() below:
public void waitForPageLoaded(WebDriver driver) {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState").equals("complete");
}
};
Wait<WebDriver> wait = new WebDriverWait(driver, 30);
try {
wait.until(expectation);
} catch (Throwable error) {
assertFalse("Timeout waiting for Page Load Request to complete.",
true);
}
}
I am expect at the end of clickAddAndClose(), it will see this page is still being updated so it will wait until the info message has been updated. But this does not seem to work either.
That leaves me to the last choice will is to add a thread sleep at the end of clickAddAndClose(). I want to avoid using it.
Is there a generic way of solving this kind of problem? How do I detect that the page DOM is still changing and tell Webdriver to wait until it finishes refreshing?
Waiting for the page to be loaded won't work if (as it seems to be the case) your page is being modified by AJAX operations.
Instead of waiting for the page to load, wait for the condition you are testing to become true. This way, you give the AJAX operation time to execute and if your there is a problem you will get an error when the time out occurs.
I usually use the Python bindings for Selenium and it has been quite a while since I wrote Java code but I believe it would look something like this, with X being replaced with a type appropriate for the itemDetailsPage object:
new FluentWait<X>(itemDetailsPage)
.until(new Function<X, Boolean>() {
public Boolean apply(X itemDetailsPage) {
return "Your search for 'electricity' returned 2 results." == itemDetailsPage.getInfoMsg();
};
});
Seems like you need to wait until ajax has finished its job. In a similar situation I've used a method similar to waitForJQueryProcessing described here. Take a look, it might help.

How can I use Selenium 2 to look for two potential elements?

Super newbie here.
I am testing a page that either returns a list of tweets, or if none are available, a no results page. So I have two possible acceptable outcomes. I am trying to write an assert in Selenium 2 that tests for either element, but if neither appears, return an error.
I am using the page object model and I wrote the following:
Assert.assertTrue((iu.twitterUsername().isDisplayed()) || (iu.noData().isDisplayed()), "Page is not loading")
However, I am getting an element not found on the first part of my OR statement when the no data page is displayed. I thought the point of 'isDisplayed' was to check if the element is there. Why am I getting an element not found error? Obviously its not there, and I want to move onto the second part of my OR statement.
Is there a better way to write this when there are two possible acceptable results?
The WebElement.isDisplayed() method doesn't tell you whether or not an element is there; it tells you whether it is displayed on the page (as opposed to being hidden).
You can use the WebDriver.findElement() method to test whether or not an element has loaded; it will throw a NoSuchElementException if it hasn't. Your method might look like this:
public static boolean isElementLoaded(WebDriver driver, By findBy) {
try {
driver.findElement(findBy));
} catch (NoSuchElementException e) {
return false;
}
return true;
}
You can alter how long WebDriver waits for the element to load with something like the following, which changes it to five seconds:
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Alternatively, you can create a method that will poll for the element until a timeout is reached.
It should be as simple as
WebDriver driver = new FirefoxDriver();
WebElement element = driver.findElement(By.id(id));
Assuming you're doing something before looking for the element I'd suggest using WebDriverWait then throw and catch exception if you don't find the element in the alloted time.
WebDriverWait wait = new WebDriverWait(driver, 10);
try {
wait.until(ExpectedConditions.elementToBeSelected(By.id(id)));
} catch (ElementNotFound e) {};
element = driver.findElement(By.id(id));

Categories