Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selenium WaitDriver doesn't wait for element to be clickable

When I click on a button it displays me a form with another buttons and I want to click on one of them. Here is a video with this (really short one), please watch http://screencast.com/t/zyliSemW1s1

So I click on button "Buy Tickets" simply like that:

button.Click();

And then I wait for the next button to be clickable.

I use the next code:

WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(15));
IWebElement element = wait.Until(ExpectedConditions.ElementToBeClickable(myButton));

After that I click on button that I waited for:

element.Click();

And I get error: Element is not clickable at that point.

As I know, the method ExpectedConditions.ElementToBeClickable() waits for 2 conditions: element is visible and element is enabled.

When I use Thread.Sleep(3000) before clicking on the second button the code works and button is clickable.

I saw similar issue and the solution was to wait for handlers of this button:Selenium Wait doesn't wait for Element to be Clickable

But what to do if I don't know what handles it? I think it handles by jQuery and I use the next code to wait till it stops executing:

var ajaxIsComplete = (bool)
    ((IJavaScriptExecutor)Driver).ExecuteScript("return jQuery.active == 0");

If it returns "false" I wait and check again.

But it still doesn't work.

So for now my flow goes like that:

  1. I click on button "Buy Tickets"
  2. I wait till jQuery stops executing
  3. I wait till element is clickable using ExpectedConditions.ElementToBeClickable() method
  4. I click on the element and it returns me an error that it is not clickable.

Please guys tell me what is wrong in my flow and how to manage it correct.

Update: I'm adding HTML code of buttons:

I click to this one:

<button class="btn btn-warning play-now" name="button" type="submit">Buy Tickets</button>

And wait for this one:

<img alt="Credit Card" class="merchant" src="https://numgames-production.s3.amazonaws.com/uploads/merchant/image/21/CC_Offline.png?AWSAccessKeyId=AKIAJ2Q64HPERGHAJJUA&amp;Expires=1470984765&amp;Signature=Qj%2BFSQ3ElctkY6KTMfzp%2FedPjPo%3D">
like image 255
Denis Koreyba Avatar asked Aug 12 '15 07:08

Denis Koreyba


People also ask

What would you use if you are waiting for an element to be clickable in Selenium?

#1) elementToBeClickable() – The expected condition waits for an element to be clickable i.e. it should be present/displayed/visible on the screen as well as enabled. wait. until(ExpectedConditions.

Why is Selenium element not clickable?

This is because the element is present in DOM, but its position is not fixed on the page. Adding the explicit wait. The webdriver can wait till the expected condition - visibilityOf(webdriver shall wait for an element in DOM to be visible). Adding the explicit wait.

Which is the correct syntax to wait for an element to be clickable?

var wait = new WebDriverWait(driver, TimeSpan. FromMinutes(1)); var clickableElement = wait. Until(ExpectedConditions.

How do you get Selenium to wait until the element is present?

We can wait until an element is present in Selenium webdriver. This can be done with the help of synchronization concept. We have an explicit wait condition where we can pause or wait for an element before proceeding to the next step. The explicit wait waits for a specific amount of time before throwing an exception.


2 Answers

Denis,

As mentioned in comments to OP, here are a few little extension methods that may help your quest:

public static void WaitForAjax(this IWebDriver driver, int timeoutSecs = 10, bool throwException = false)
{
    for (var i = 0; i < (timeoutSecs*10); i++)
    {
        var javaScriptExecutor = driver as IJavaScriptExecutor;
        var ajaxIsComplete = javaScriptExecutor != null && (bool)javaScriptExecutor.ExecuteScript("return jQuery.active == 0");
        if (ajaxIsComplete) return;
        Thread.Sleep(100);
    }
    if (throwException)
    {
        throw new Exception("WebDriver timed out waiting for AJAX call to complete");
    }
}

public static bool ElementExists(this IWebDriver driver, By condition)
{
    return ElementExists(driver, condition, new TimeSpan());
}

public static bool ElementExists(this IWebDriver driver, By condition, TimeSpan timeSpan)
{
    bool isElementPresent = false;

    if (timeSpan == default(TimeSpan))
    {
        timeSpan = TimeSpan.FromMilliseconds(15000);
    }

    var driverWait = new WebDriverWait(driver, (TimeSpan)timeSpan);
    driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
    isElementPresent = driverWait.Until(x => x.FindElements(condition).Any());

    return isElementPresent;
}

public static IWebElement FindElementAfterWait(this IWebDriver driver, By condition, int fromSeconds = 90)
{
    bool isElementPresent = false;
    IWebElement singleElement = null;

    var driverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(fromSeconds));
    driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));

    try
    {
        isElementPresent = driverWait.Until(ExpectedConditions.ElementExists(condition)) != null;

        if (isElementPresent)
        {
            singleElement = driver.FindElement(condition);
        }
    }
    catch
    {
        // log any errors
    }

    return singleElement;
}

usages:

bool elementExists = _driver.ElementExists(By.Id("submitButton"));

var submitButton = _driver.FindElementAfterWait(By.Id("submitButton"));
submitButton.Click();
_driver.WaitForAjax();

// then do other code stuff...

Hopefully, a combo of these may get you out of the fix.

like image 146
jim tollan Avatar answered Nov 02 '22 23:11

jim tollan


This is a typical problem of working with any asynchronous (AJAX) page.

You don't need to use any "magic" methods like sleeps, jquery.active and expected conditions.

Web pages are usually built that way that user would see when operation ends - like some message appears, or some button become enabled. I believe in your case something like this also happens after you click "Buy Tickets" - you need to notice this and wait for this in your code.

That wait needs to be performed using Explicit wait for your specific case. This is the only reliable way to manage asynchronous pages (including elements not yet clickable).

You can see more detailed overview in my blog post - Approaches to handling AJAX in Web Automation (Selenium)

like image 37
MrHant Avatar answered Nov 03 '22 00:11

MrHant