59

I'm using Selenium 2 WebDriver to test an UI which uses AJAX.

Is there a way to make the driver to wait for a bit that the Ajax request will complete.

Basically I have this :

d.FindElement(By.XPath("//div[8]/div[3]/div/button")).Click();
// This click trigger an ajax request which will fill the below ID with content.
// So I need to make it wait for a bit.

Assert.IsNotEmpty(d.FindElement(By.Id("Hobbies")).Text);
jcollum
  • 43,623
  • 55
  • 191
  • 321
Omu
  • 69,856
  • 92
  • 277
  • 407

10 Answers10

82

If you're using jQuery for your ajax requests, you can wait until the jQuery.active property is zero. Other libraries might have similar options.

public void WaitForAjax()
{
    while (true) // Handle timeout somewhere
    {
        var ajaxIsComplete = (bool)(driver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
        if (ajaxIsComplete)
            break;
        Thread.Sleep(100);
    }
}
Morten Christiansen
  • 19,002
  • 22
  • 69
  • 94
46

You could also use the Selenium explicit wait here. Then you don't need to handle timeout yourself

public void WaitForAjax()
{
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
    wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}
Mila
  • 469
  • 4
  • 3
18
var wait = new WebDriverWait(d, TimeSpan.FromSeconds(5));
var element = wait.Until(driver => driver.FindElement(By.Id("Hobbies")));
Hakan Hastekin
  • 553
  • 3
  • 7
  • 1
    are you sure this is the way, cuz it didn't helped – Omu Jun 01 '11 at 14:12
  • Does the ajax call take longer than 5 secs? Above code would throw an exception if it cannot find the element in 5 secs. Also, is the element hobbies visible when the ajax call is complete? WebDriver would only find elements that are visible to end user. – Hakan Hastekin Jun 01 '11 at 14:22
  • 1
    @hhastekin the ajax call is instant, it looks like the wait is not needed at all, cuz the visible elements are ok, but doing .Text for style="display:none;" elements gives empty – Omu Jun 02 '11 at 06:53
  • @hhastekin so it's not possible to get the innerText of div that is style="display:none;" ? – Omu Jun 02 '11 at 07:04
  • 2
    @Omu, WebDriver is designed so that it only displays what real user sees. If user cannot see it then webdriver cannot see it as well. – Hakan Hastekin Jun 02 '11 at 10:05
  • 2
    @hhastekin well it sees the value attribute of input type:hidden – Omu Jun 02 '11 at 10:57
  • @Omu checkout [http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions](http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions), you might find your answer there. – Hakan Hastekin Jun 02 '11 at 11:38
8

Java solution based on Morten Christiansens answer

    public void WaitForAjax() throws InterruptedException
    {

        while (true)
        {

            Boolean ajaxIsComplete = (Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0");
            if (ajaxIsComplete){
                break;
            }
            Thread.sleep(100);
        }
    }


sherif
  • 2,282
  • 19
  • 21
3

Just a little improvement by adding a timeout parameter:

internal static void WaitForAllAjaxCalls(this ISelenium selenium, IWebDriver driver, int timeout = 40)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        while (true)
        {
            if (sw.Elapsed.Seconds > timeout) throw new Exception("Timeout");
            var ajaxIsComplete = (bool)driver.ExecuteScript("return jQuery.active == 0");
            if (ajaxIsComplete)
                break;
            Thread.Sleep(100);
        }            
    }
c0deNinja
  • 3,956
  • 1
  • 29
  • 45
2

Just small improvement:

//Wait for Ajax call to complete
  public void WaitForAjax1() throws InterruptedException
    {

        while (true)
        {
            if ((Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")){
                break;
            }
            Thread.sleep(100);
        }
    }
Andrei Suvorkov
  • 5,559
  • 5
  • 22
  • 48
Pavan T
  • 716
  • 9
  • 12
1

Here is my code:

public static void WaitForCommission (WebDriver driver) throws Exception {
    for (int second = 0;; second++) {
        if (second >= 30) fail("timeout");
        try { 
            if (IsElementActive(By.id("transferPurposeDDL"), driver)) 
                break; 
            } catch (Exception e) {}
        Thread.sleep(1000);
    }
}

private static boolean IsElementActive(By id, WebDriver driver) {
    WebElement we =  driver.findElement(id);        
    if(we.isEnabled())
        return true;
    return false;
}

This code is really work.

Anton
  • 19
  • 1
1

If you are using Graphene you can use this:

Graphene.waitModel().until((Predicate<WebDriver>) input -> (Boolean) ((JavascriptExecutor) input).executeScript("return jQuery.active == 0"));
Markus Heberling
  • 835
  • 13
  • 18
1

The “XML Http Request” is the protocol used to send Ajax requests to the server and so the presence of such a request indicates an Ajax based operation in progress.

There are a number of browser plugins that allow you to monitor XML Http Requests sent by the browser. I personally use the Firebug plugin for Firefox which is a very useful tool. Once installed Firebug displays a Bug-like icon at the bottom right corner of the browser window. Clicking on the bug-like icon launches Firebug as shown in the image above. Select the “Net” and then “XHR” to launch the XHR console where all XML HTTP Requests sent by the browser will be displayed.

Avoid using thread.sleep() as much as possible. Here is a piece of code that accepts wait time as input and runs a stop watch for the specified time.

You may set the input time in seconds to 30 to start with.

protected void WaitForAjaxToComplete(int timeoutSecs)
        {

            var stopWatch = new Stopwatch();

            try
            {
                while (stopWatch.Elapsed.TotalSeconds < timeoutSecs)
                {

                    var ajaxIsComplete = (bool)(WebDriver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
                    if (ajaxIsComplete)
                    {
                        break;
                    }

                }
            }
            //Exception Handling
            catch (Exception ex)
            {
                stopWatch.Stop();
                throw ex;
            }
            stopWatch.Stop();

        }
1

If you use Coypu you can Check if an element exists after an AJAX call and then you can click it:

private void WaitUntilExistsThenClick(string selectorId)
{
    var searchBoxExists = new State(() => browser.FindId(selectorId).Exists());
    if (browser.FindState(searchBoxExists) == searchBoxExists)
    {                
        browser.FindId(selectorId).Click();
    }
}       
zx485
  • 28,498
  • 28
  • 50
  • 59
OlegGuy
  • 41
  • 7