How to get selenium to wait for ajax response?

AjaxUnit TestingSelenium

Ajax Problem Overview


How can I get selenium to wait for something like a calendar widget to load? Right now I am just doing a Thread.sleep(2500) after exporting the testcase to a junit program.

Ajax Solutions


Solution 1 - Ajax

I would use

waitForElementPresent(locator)

This will wait until the element is present in the DOM.

If you need to check the element is visible, you may be better using

waitForElementHeight(locator)

Solution 2 - Ajax

A more general solution than waiting for an element would be to wait for all the connections to the server to close. This will allow you to wait for all ajax calls to finish, even if they don't have any callback and thus don't affect the page. More details can be found here.

Using C# and jQuery, I have created the following method to wait for all AJax calls to complete (if anyone have more direct ways of accessing JS variables from C#, please comment):

internal void WaitForAjax(int timeOut = 15)
{
    var value = "";
    RepeatUntil(
        () => value = GetJavascriptValue("jQuery.active"), 
        () => value == "0", 
        "Ajax calls did not complete before timeout"
    );
}

internal void RepeatUntil(Action repeat, Func<bool> until, string errorMessage, int timeout = 15)
{
    var end = DateTime.Now + TimeSpan.FromSeconds(timeout);
    var complete = false;

    while (DateTime.Now < end)
    {
        repeat();
        try
        {
            if (until())
            {
                complete = true;
                break;
            }
        }
        catch (Exception)
        { }
        Thread.Sleep(500);
    }
    if (!complete)
        throw new TimeoutException(errorMessage);
}

internal string GetJavascriptValue(string variableName)
{
    var id = Guid.NewGuid().ToString();
    _selenium.RunScript(String.Format(@"window.$('body').append(""<input type='text' value='""+{0}+""' id='{1}'/>"");", variableName, id));
    return _selenium.GetValue(id);
}

Solution 3 - Ajax

If using python, you may use this function, which clicks the button and waits for the DOM change:

def click_n_wait(driver, button, timeout=5):
    source = driver.page_source
    button.click()
    def compare_source(driver):
        try:
            return source != driver.page_source
        except WebDriverException:
            pass
    WebDriverWait(driver, timeout).until(compare_source)

(CREDIT: based on this stack overflow answer)

Solution 4 - Ajax

With webdriver aka selenium2 you can use implicit wait configuration as mentionned on https://www.selenium.dev/documentation/en/webdriver/waits/#implicit-wait

Using Java:

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));

Or using python:

from selenium import webdriver

ff = webdriver.Firefox()
ff.implicitly_wait(10) # seconds
ff.get("http://somedomain/url_that_delays_loading")
myDynamicElement = ff.find_element_by_id("myDynamicElement")

Solution 5 - Ajax

This work for me

public  void waitForAjax(WebDriver driver) {
    new WebDriverWait(driver, 180).until(new ExpectedCondition<Boolean>(){
        public Boolean apply(WebDriver driver) {
            JavascriptExecutor js = (JavascriptExecutor) driver;
            return (Boolean) js.executeScript("return jQuery.active == 0");
        }
    });
}

Solution 6 - Ajax

I wrote next method as my solution (I hadn't any load indicator):

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();");
}

Then I jsut called this method with actual driver. More description in this post.

Solution 7 - Ajax

The code (C#) bellow ensures that the target element is displayed:

        internal static bool ElementIsDisplayed()
        {
          IWebDriver driver = new ChromeDriver();
          driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
          WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
          By locator = By.CssSelector("input[value='csharp']:first-child");
          IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
          {
            return d.FindElement(locator);
          });
          return myDynamicElement.Displayed;
        }

If the page supports jQuery it can be used the jQuery.active function to ensure that the target element is retrieved after all the ajax calls are finished:

 public static bool ElementIsDisplayed()
    {
        IWebDriver driver = new ChromeDriver();
        driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
        WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        By locator = By.CssSelector("input[value='csharp']:first-child");
        return wait.Until(d => ElementIsDisplayed(d, locator));
    }

    public static bool ElementIsDisplayed(IWebDriver driver, By by)
    {
        try
        {
            if (driver.FindElement(by).Displayed)
            {
                //jQuery is supported.
                if ((bool)((IJavaScriptExecutor)driver).ExecuteScript("return window.$ != undefined"))
                {
                    return (bool)((IJavaScriptExecutor)driver).ExecuteScript("return $.active == 0");
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return false;
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

Solution 8 - Ajax

This works like a charm for me :

public void waitForAjax() {
	
	try {
		WebDriverWait driverWait = new WebDriverWait(driver, 10);
	
		ExpectedCondition<Boolean> expectation;   
		expectation = new ExpectedCondition<Boolean>() {
			
			public Boolean apply(WebDriver driverjs) {
				
				JavascriptExecutor js = (JavascriptExecutor) driverjs;
				return js.executeScript("return((window.jQuery != null) && (jQuery.active === 0))").equals("true");
			}
		};
		driverWait.until(expectation);
	}		
	catch (TimeoutException exTimeout) {

       // fail code
	}
	catch (WebDriverException exWebDriverException) {
       
       // fail code
	}
	return this;
}

Solution 9 - Ajax

I had a similar situation, i wanted to wait for ajax requests so that the loading panel would have disappeared, I have inspected the html before and after the requests, found that there is a div for the ajax loading panel, the dix is displayed during the ajax request, and hidden after the request ends. I have created a function to wait for the panel to be displayed, then wait for it to be hidden

public void WaitForModalPanel()
{
    string element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer'  and not(contains(@style,'display: none'))]";
    WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 2, 0));
    wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(element_xpath)));
    element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and contains(@style,'DISPLAY: none')]";
    wait.Until(ExpectedConditions.ElementExists(By.XPath(element_xpath)));
}

Check this for more details

Solution 10 - Ajax

As mentioned above you can wait for active connections to get closed:

private static void WaitForReady() {
    WebDriverWait wait = new WebDriverWait(webDriver, waitForElement);
    wait.Until(driver => (bool)((IJavaScriptExecutor)driver).ExecuteScript("return jQuery.active == 0"));
}

My observation is this is not reliable as data transfer happens very quickly. Much more time is consumed on data processing and rendering on the page and even jQuery.active == 0 data might not be yet on the page.

Much wiser is to use an explicit wait for element to be shown on the page, see some of the answers related to this.

The best situation is if your web application have some custom loader or indication that data is being processed. In this case you can just wait for this indication to hide.

Solution 11 - Ajax

Here's a groovy version based on Morten Christiansen's answer.

void waitForAjaxCallsToComplete() {
    repeatUntil(
            { return getJavaScriptFunction(driver, "return (window.jQuery || {active : false}).active") },
            "Ajax calls did not complete before timeout."
    )
}

static void repeatUntil(Closure runUntilTrue, String errorMessage, int pollFrequencyMS = 250, int timeOutSeconds = 10) {
    def today = new Date()
    def end = today.time + timeOutSeconds
    def complete = false;

    while (today.time < end) {
        if (runUntilTrue()) {
            complete = true;
            break;
        }

        sleep(pollFrequencyMS);
    }
    if (!complete)
        throw new TimeoutException(errorMessage);
}

static String getJavaScriptFunction(WebDriver driver, String jsFunction) {
    def jsDriver = driver as JavascriptExecutor
    jsDriver.executeScript(jsFunction)
}

Solution 12 - Ajax

If the control you are waiting for is an "Ajax" web element, the following code will wait for it, or any other Ajax web element to finish loading or performing whatever it needs to do so that you can more-safely continue with your steps.

	public static void waitForAjaxToFinish() {

	WebDriverWait wait = new WebDriverWait(driver, 10);

	wait.until(new ExpectedCondition<Boolean>() { 
		public Boolean apply(WebDriver wdriver) { 
			return ((JavascriptExecutor) driver).executeScript(
					"return jQuery.active == 0").equals(true);
		}
	});	
	
}

Solution 13 - Ajax

Below is my code for fetch. Took me while researching because jQuery.active doesn't work with fetch. Here is the answer helped me proxy fetch, but its only for ajax not fetch mock for selenium

public static void customPatchXMLHttpRequest(WebDriver driver) {
	try {
		if (driver instanceof JavascriptExecutor) {
			JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
			Object numberOfAjaxConnections = jsDriver.executeScript("return window.openHTTPs");
			if (numberOfAjaxConnections instanceof Long) {
				return;
			}
			String script = "  (function() {" + "var oldFetch = fetch;"
					+ "window.openHTTPs = 0; console.log('starting xhttps');" + "fetch = function(input,init ){	"
					+ "window.openHTTPs++; "

					+ "return oldFetch(input,init).then( function (response) {"
					+ "      if (response.status >= 200 && response.status < 300) {"
					+ "          window.openHTTPs--;  console.log('Call completed. Remaining active calls: '+ window.openHTTPs); return response;"
					+ "      } else {"
					+ "          window.openHTTPs--; console.log('Call fails. Remaining active calls: ' + window.openHTTPs);  return response;"
					+ "      };})" + "};" + "var oldOpen = XMLHttpRequest.prototype.open;"
					+ "XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {"
					+ "window.openHTTPs++; console.log('xml ajax called');"
					+ "this.addEventListener('readystatechange', function() {" + "if(this.readyState == 4) {"
					+ "window.openHTTPs--; console.log('xml ajax complete');" + "}" + "}, false);"
					+ "oldOpen.call(this, method, url, async, user, pass);" + "}" +

					"})();";
			jsDriver.executeScript(script);
		} else {
			System.out.println("Web driver: " + driver + " cannot execute javascript");
		}
	} catch (Exception e) {
		System.out.println(e);
	}
}

Solution 14 - Ajax

In my case the issue seemed due to ajax delays but was related to internal iframes inside the main page. In seleminum it is possible to switch to internal frames with:

	driver.switchTo().frame("body");
	driver.switchTo().frame("bodytab");

I use java. After that I was able to locate the element

	driver.findElement(By.id("e_46")).click();

Solution 15 - Ajax

For those who is using primefaces, just do:

selenium.waitForCondition("selenium.browserbot.getCurrentWindow().$.active==0", defaultWaitingPeriod);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZombiesView Question on Stackoverflow
Solution 1 - AjaxNeil AitkenView Answer on Stackoverflow
Solution 2 - AjaxMorten ChristiansenView Answer on Stackoverflow
Solution 3 - AjaxNimoView Answer on Stackoverflow
Solution 4 - AjaxtoutptView Answer on Stackoverflow
Solution 5 - AjaxArtem KislicynView Answer on Stackoverflow
Solution 6 - AjaxHraboschView Answer on Stackoverflow
Solution 7 - AjaxGeorge KargakisView Answer on Stackoverflow
Solution 8 - AjaxneonidianView Answer on Stackoverflow
Solution 9 - AjaxAmir ShenoudaView Answer on Stackoverflow
Solution 10 - AjaxllatinovView Answer on Stackoverflow
Solution 11 - AjaxCiremView Answer on Stackoverflow
Solution 12 - AjaxBill HilemanView Answer on Stackoverflow
Solution 13 - AjaxBatBoldView Answer on Stackoverflow
Solution 14 - AjaxRoberto PetrilliView Answer on Stackoverflow
Solution 15 - AjaxSamuel TianView Answer on Stackoverflow