According to a number of articles I have seen, the use of Thread.sleep() seems to be frowned upon. I have used this frequently in my test classes, where it was necessary to wait for something to load. I have tried using this method to tell me when load is complete, but that did not help. It is not a reliable method.:
try {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
} catch (Exception e) {
System.err.println(thisClass + " Exception caught: " + e.getMessage());
}
What alternatives methods can I use?
This solution worked:
Calling function:
String className = "gwt-Button form-control btn back-button ripple-container";
String textToFind = "back-button-icon";
String htmlElement = "button";
boolean isReady = Common.FluentWait(driver, 60, className, textToFind, htmlElement);
That function:
public static boolean FluentWait(WebDriver driver, int timeOut, String className, String textToFind,
String htmlElement) {
// Waiting timeOut seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Common.myPrint(thisClass + " FluentWait. ");
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(timeOut, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS).ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
Common.myPrint(thisClass + " run returnWebElement. ");
return returnWebElement(className, textToFind, htmlElement, driver);
}
});
if (foo != null) {
return true;
}
return false;
}
and the function I called inside that:
public static WebElement returnWebElement(String className, String textToFind, String htmlElement,
WebDriver driver) {
List<WebElement> elements = Common.findElementsUsingHtmlXpathClass(driver, htmlElement, className);
Common.myPrint(thisClass + " elements count: " + elements.size());
String text = "";
for (WebElement element : elements) {
// select an element
if (element.isDisplayed()) {
text = element.getAttribute("innerHTML");
if (text != "") {
text = text.trim();
if (text.contains(textToFind)) {
Common.myPrint(thisClass + " innerHTML: " + text);
Common.myPrint(thisClass + " returning element found. ");
return element;
}
}
}
}
Common.myPrint(thisClass + " element not found. ");
return null;
}
You can use FluentWait
Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition. Furthermore, the user may configure the wait to ignore specific types of exceptions whilst waiting, such as NoSuchElementExceptions when searching for an element on the page.
/*
code snippet will Wait 30 seconds for
an element to be present on the page and check for its
presence once every 5 seconds.
*/
Wait wait = new FluentWait(driver)
.withTimeout(30, SECONDS)
.pollingEvery(5, SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function() {
public WebElement apply(WebDriver driver) {
return driver.findElement(By.id("foo"));
}
});
another wait also available
I have found another way although it is very similar to Thread.sleep(). I was looking for another wait as the implicit wait and explicit wait with Selenium is not waiting long enough for me.
The alternative I used:
TimeUnit.SECONDS.sleep(int timeUnit)
This delivered the same functionality as Thread.sleep(). I hope this helps.
Your code will just attempt to wait for the page to load, not wait for specific elements, etc. to load. This will work fine for static HTML pages but once you start adding dynamic sections to the page (using AJAX, etc.) , it will not accomplish what you want.
You can use WebDriverWait. See the docs for more info.
Simple example,
// create an instance that can be reused
WebDriverWait wait = new WebDriverWait(driver, 10);
// wait for an element to be clickable and store the return
WebElement button = wait.until(ExpectedConditions.elementToBeClickable(By.id("someId"));
// click the returned button
button.click();
There are a bunch of standard conditions provided by ExpectedConditions. See the docs for more info.
Related
I used explicit waits and I have the warning:
org.openqa.selenium.WebDriverException:
Element is not clickable at point (36, 72). Other element would receive
the click: ...
Command duration or timeout: 393 milliseconds
If I use Thread.sleep(2000) I don't receive any warnings.
#Test(dataProvider = "menuData")
public void Main(String btnMenu, String TitleResultPage, String Text) throws InterruptedException {
WebDriverWait wait = new WebDriverWait(driver, 10);
driver.findElement(By.id("navigationPageButton")).click();
try {
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector(btnMenu)));
} catch (Exception e) {
System.out.println("Oh");
}
driver.findElement(By.cssSelector(btnMenu)).click();
Assert.assertEquals(driver.findElement(By.cssSelector(TitleResultPage)).getText(), Text);
}
WebDriverException: Element is not clickable at point (x, y)
This is a typical org.openqa.selenium.WebDriverException which extends java.lang.RuntimeException.
The fields of this exception are :
BASE_SUPPORT_URL : protected static final java.lang.String BASE_SUPPORT_URL
DRIVER_INFO : public static final java.lang.String DRIVER_INFO
SESSION_ID : public static final java.lang.String SESSION_ID
About your individual usecase, the error tells it all :
WebDriverException: Element is not clickable at point (x, y). Other element would receive the click
It is clear from your code block that you have defined the wait as WebDriverWait wait = new WebDriverWait(driver, 10); but you are calling the click() method on the element before the ExplicitWait comes into play as in until(ExpectedConditions.elementToBeClickable).
Solution
The error Element is not clickable at point (x, y) can arise from different factors. You can address them by either of the following procedures:
1. Element not getting clicked due to JavaScript or AJAX calls present
Try to use Actions Class:
WebElement element = driver.findElement(By.id("navigationPageButton"));
Actions actions = new Actions(driver);
actions.moveToElement(element).click().build().perform();
2. Element not getting clicked as it is not within Viewport
Try to use JavascriptExecutor to bring the element within the Viewport:
WebElement myelement = driver.findElement(By.id("navigationPageButton"));
JavascriptExecutor jse2 = (JavascriptExecutor)driver;
jse2.executeScript("arguments[0].scrollIntoView()", myelement);
3. The page is getting refreshed before the element gets clickable.
In this case induce ExplicitWait i.e WebDriverWait as mentioned in point 4.
4. Element is present in the DOM but not clickable.
In this case induce ExplicitWait with ExpectedConditions set to elementToBeClickable for the element to be clickable:
WebDriverWait wait2 = new WebDriverWait(driver, 10);
wait2.until(ExpectedConditions.elementToBeClickable(By.id("navigationPageButton")));
5. Element is present but having temporary Overlay.
In this case, induce ExplicitWait with ExpectedConditions set to invisibilityOfElementLocated for the Overlay to be invisible.
WebDriverWait wait3 = new WebDriverWait(driver, 10);
wait3.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("ele_to_inv")));
6. Element is present but having permanent Overlay.
Use JavascriptExecutor to send the click directly on the element.
WebElement ele = driver.findElement(By.xpath("element_xpath"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", ele);
In case you need to use it with Javascript
We can use arguments[0].click() to simulate click operation.
var element = element(by.linkText('webdriverjs'));
browser.executeScript("arguments[0].click()",element);
I ran into this error while trying to click some element (or its overlay, I didn't care), and the other answers didn't work for me. I fixed it by using the elementFromPoint DOM API to find the element that Selenium wanted me to click on instead:
element_i_care_about = something()
loc = element_i_care_about.location
element_to_click = driver.execute_script(
"return document.elementFromPoint(arguments[0], arguments[1]);",
loc['x'],
loc['y'])
element_to_click.click()
I've also had situations where an element was moving, for example because an element above it on the page was doing an animated expand or collapse. In that case, this Expected Condition class helped. You give it the elements that are animated, not the ones you want to click. This version only works for jQuery animations.
class elements_not_to_be_animated(object):
def __init__(self, locator):
self.locator = locator
def __call__(self, driver):
try:
elements = EC._find_elements(driver, self.locator)
# :animated is an artificial jQuery selector for things that are
# currently animated by jQuery.
return driver.execute_script(
'return !jQuery(arguments[0]).filter(":animated").length;',
elements)
except StaleElementReferenceException:
return False
You can try
WebElement navigationPageButton = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(By.id("navigationPageButton")));
navigationPageButton.click();
Scrolling the page to the near by point mentioned in the exception did the trick for me. Below is code snippet:
$wd_host = 'http://localhost:4444/wd/hub';
$capabilities =
[
\WebDriverCapabilityType::BROWSER_NAME => 'chrome',
\WebDriverCapabilityType::PROXY => [
'proxyType' => 'manual',
'httpProxy' => PROXY_DOMAIN.':'.PROXY_PORT,
'sslProxy' => PROXY_DOMAIN.':'.PROXY_PORT,
'noProxy' => PROXY_EXCEPTION // to run locally
],
];
$webDriver = \RemoteWebDriver::create($wd_host, $capabilities, 250000, 250000);
...........
...........
// Wait for 3 seconds
$webDriver->wait(3);
// Scrolls the page vertically by 70 pixels
$webDriver->executeScript("window.scrollTo(0, 70);");
NOTE: I use Facebook php webdriver
If element is not clickable and overlay issue is ocuring we use arguments[0].click().
WebElement ele = driver.findElement(By.xpath("//div[#class='input-group-btn']/input"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", ele);
The best solution is to override the click functionality:
public void _click(WebElement element){
boolean flag = false;
while(true) {
try{
element.click();
flag=true;
}
catch (Exception e){
flag = false;
}
if(flag)
{
try{
element.click();
}
catch (Exception e){
System.out.printf("Element: " +element+ " has beed clicked, Selenium exception triggered: " + e.getMessage());
}
break;
}
}
}
In C#, I had problem with checking RadioButton,
and this worked for me:
driver.ExecuteJavaScript("arguments[0].checked=true", radio);
Can try with below code
WebDriverWait wait = new WebDriverWait(driver, 30);
Pass other element would receive the click:<a class="navbar-brand" href="#"></a>
boolean invisiable = wait.until(ExpectedConditions
.invisibilityOfElementLocated(By.xpath("//div[#class='navbar-brand']")));
Pass clickable button id as shown below
if (invisiable) {
WebElement ele = driver.findElement(By.xpath("//div[#id='button']");
ele.click();
}
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.
Given a site, AJAX components on the page and I need to wait till the whole page is fully loaded.
Here is my wait method using JavascriptExecutor checking document.readyState:
public void waitForLoading2() {
WebDriverWait wait = new WebDriverWait(driver, timeOut);
if(!driver.findElements(By.xpath("//*[#id='wait'][contains(#style, 'display: block')]")).isEmpty()) {
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[#id='wait'][contains(#style, 'display: none')]")));
}
ExpectedCondition<Boolean> expectation = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equalsIgnoreCase("complete");
}
};
wait.until(expectation);
}
Sometimes it's failing with the following Error msg:
org.openqa.selenium.JavascriptException: JavaScript error (WARNING:
The server did not provide any stacktrace information)
What did I miss here? My assumption is that document.readyState is common and always can be checked.
Thanks
There are more complex options, like this one
public static void waitForAjax(WebDriver driver, String action) {
driver.manage().timeouts().setScriptTimeout(5, TimeUnit.SECONDS);
((JavascriptExecutor) driver).executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"var xhr = new XMLHttpRequest();" +
"xhr.open('POST', '/" + action + "', true);" +
"xhr.onreadystatechange = function() {" +
" if (xhr.readyState == 4) {" +
" callback(xhr.responseText);" +
" }" +
"};" +
"xhr.send();");
}
in order
to wait till the whole page is fully loaded
But the following did the trick for me - I check if there are ongoing AJAX calls and wait till those are done:
JavascriptExecutor js = (JavascriptExecutor) driverjs;
js.executeScript("return((window.jQuery != null) && (jQuery.active === 0))").equals("true");
Checking document.readyState will not help you with AJAX calls. Your best bet is to find an element in the area being loaded by AJAX and wait until it is visible. Then you'll know that the page is loaded. If there are multiple/separate areas loaded by AJAX calls, then you will want to pick an element from each area.
If you use jQuery to send AJAX request, you can get the value of jQuery.active. it's equivalent to all AJAX requests complete when jQuery.active=0.
Use executeScript("return jQuery.active==0"). For detail please read this artical
I'm trying synchronization with selenium webdriver and something is not working with implicitlyWait().
The way I understand implicitlyWait(..) is that the code is waiting until the element is available for a max of time.
The code below crash with the error:
org.openqa.selenium.InvalidElementStateException: invalid element state: Element is not currently interactable and may not be manipulated
The System.out ist printing: -->> false true false (isDiplayed(), isEnabled(), is Selected())
private static WebDriver driver;
public static void main(String[] args) throws InterruptedException {
setupWebDriverChrome();
//Thread.sleep(1000);
final String cssSelectorFromAirport = "div.od-airportselector.airportselector_root input[tabindex='11']";
final By cssSelector = By.cssSelector(cssSelectorFromAirport);
WebElement fromAirportElement = driver.findElement(cssSelector);
System.out.println("-->> " + fromAirportElement.isDisplayed() + " " + fromAirportElement.isEnabled() + " " + fromAirportElement.isSelected());
fromAirportElement.clear();
fromAirportElement.sendKeys("MUC");
}
private static void setupWebDriverChrome() {
System.setProperty("webdriver.chrome.driver", "C:\\...\\chromedriver.exe");
setupLocation();
}
private static void setupLocation() {
driver.manage().timeouts().implicitlyWait(1000, TimeUnit.MILLISECONDS);
driver.get("https://www.opodo.de/");
}
I tried that also with the Geckodriver with the same result.
I have also increased the wait time but same result.
The only way to make it works, is to use Thread.sleep() (Commented above)
EDIT
Pls. note that I do not see any duplication with Selenium implicitwait not working.
You have to wait that your element is clickable. Try adding this:
WebElement element = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(cssSelectorFromAirport)));
So:
setupWebDriverChrome();
//Thread.sleep(1000);
final String cssSelectorFromAirport = "div.od-airportselector.airportselector_root input[tabindex='11']";
WebElement element = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(cssSelectorFromAirport)));
/*final By cssSelector = By.cssSelector(cssSelectorFromAirport);
WebElement fromAirportElement = driver.findElement(cssSelector);
System.out.println("-->> " + fromAirportElement.isDisplayed() + " " + fromAirportElement.isEnabled() + " " + fromAirportElement.isSelected());*/
element.clear();
element.sendKeys("MUC");
EDIT
From the documentation :
An implicit wait is to tell WebDriver to poll the DOM for a certain
amount of time when trying to find an element or elements if they are
not immediately available. The default setting is 0. Once set, the
implicit wait is set for the life of the WebDriver object instance.
This means, in your example, that selenium found the element but it isn't yet "clickable".
You can see this also in your test. If you take a look to:
System.out.println("-->> " + fromAirportElement.isDisplayed() + " " + fromAirportElement.isEnabled() + " " + fromAirportElement.isSelected() );
When it fails, the output is:
-->> false true false
While when it works:
-->> true true false
I am trying to automate functional testing of a web application using Selenium and Java. In my application there are several menus. When clicked on a particular menu, a drop down of sub menus appear
click to view screenshot of menu
I use below code to click sub menu
driver.findElement(By.xpath("id=menu")).click();
driver.findElement(By.xpath("id=sub_menu_a")).click();
but the issue is that it throws a 'ElementNotVisibleException' at the second line. The same happens even if I use implicit wait
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
explicit wait
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("id=sub_menu_a")));
and fluent wait.
Wait<WebDriver> fluentWait=new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(2, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class,ElementNotVisibleException.class);
WebElement element=fluentWait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver){
driver.findElement(By.xpath("id=menu"));
return driver.findElement(By.xpath("id=sub_menu_a"));
}
});
element.click();
but no luck. But the code works fine if add sleep time using
Thread.sleep(sleeptime);
before and after the first line of code. But it is not a permanent solution since the page load time may vary depend on the network speed and the data in the page. Is there any other solution?
Try this
WebElement menu=driver.findElement(By.xpath("id=menu"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", menu);
WebElement subMenu=driver.findElement(By.xpath("id=sub_menu_a"));
executor.executeScript("arguments[0].click();", subMenu);
Hope this work
Try to use Actions class and see if it works or not ...
driver.findElement(By.xpath("id=menu")).click();
WebElement subMenu=driver.findElement(By.xpath("id=sub_menu_a"));
Actions myaction = new Actions(driver);
myaction.moveToElement(subMenu);
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.elementToBeClickable(subMenu));
myaction.click().perform();
Fluent waits should work fine.
Try using something like this:
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).ignoring(NoSuchElementException.class);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("id=sub_menu_a")));
but I would go for css Selectors they are perfect for html pages.
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).ignoring(NoSuchElementException.class);
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#sub_menu_a")));
Or if your sub_menu_a is a child of menu I would go for
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).ignoring(NoSuchElementException.class);
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#menu #sub_menu_a")));
Could you try
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("id=sub_menu_a")));
Also it would be better if you can give html to find the right xpath as I think better XPATH will yield the result of click on the submenu.
Long time ago I have the similar issue (don't remember the exact case so indeed your HTML page snipped would be helpful) so I was forced to use Thread.sleep()
To avoid long waits will propose something like this method:
static void waitAndClick(WebDriver driver, By by, int attempts, int sleep) throws InterruptedException {
for (int i = 0; i < attempts; i++) {
WebElement element = null;
try {
element = driver.findElement(by);
} catch (NoSuchElementException e) {
// Do nothing
}
if (element == null) {
int time = sleep * (i + 1);
Thread.sleep(time);
} else {
element.click();
break;
}
}
throw new NoSuchElementException("Error");
}
It's not a 100% complete solution but just an idea.