¿Cómo obtener selenium para esperar la respuesta ajax?

¿Cómo puedo obtener selenium para esperar a que se cargue algo como un widget de calendario? Ahora mismo estoy haciendo un Thread.sleep(2500) después de exportar el testcase a un progtwig junit.

yo usaría

 waitForElementPresent(locator) 

Esto esperará hasta que el elemento esté presente en el DOM.

Si necesita verificar que el elemento esté visible, puede que sea mejor usarlo

 waitForElementHeight(locator) 

Una solución más general que esperar un elemento sería esperar a que se cierren todas las conexiones al servidor. Esto le permitirá esperar a que finalicen todas las llamadas ajax, incluso si no tienen ninguna callback y, por lo tanto, no afectan a la página. Más detalles se pueden encontrar aquí .

Usando C # y jQuery, he creado el siguiente método para esperar a que se completen todas las llamadas de AJax (si alguien tiene formas más directas de acceder a las variables de JS desde C #, por favor comenten):

 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 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("""");", variableName, id)); return _selenium.GetValue(id); } 

Si usa Python, puede usar esta función, que hace clic en el botón y espera el cambio de DOM:

 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) 

(CRÉDITO: basado en esta respuesta de desbordamiento de stack )

Con webdriver también conocido como selenium2, puede usar la configuración de espera implícita mencionada en http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits

Usando 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")); 

O usando 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") 

Escribí el siguiente método como mi solución (no tenía ningún indicador de carga):

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

Luego llamé a este método con el controlador real. Más descripción en esta publicación .

Tuve una situación similar, quería esperar las solicitudes ajax para que el panel de carga desapareciera, inspeccioné el html antes y después de las solicitudes, descubrí que hay un div para el panel de carga de ajax, el dix se muestra durante la solicitud de AJAX, y se oculta después de que finaliza la solicitud. Creé una función para esperar a que se muestre el panel, luego espero a que se oculte


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

Mira esto para más detalles

El código (C #) a continuación asegura que el elemento objective se muestra:

  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((d) => { return d.FindElement(locator); }); return myDynamicElement.Displayed; } 

Si la página admite jQuery, se puede usar la función jQuery.active para garantizar que el elemento objective se recupera después de que se hayan terminado todas las llamadas ajax:

  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; } } 

Aquí hay una versión maravillosa basada en la respuesta de Morten Christiansen.

 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) } 

Como se mencionó anteriormente, puede esperar a que se cierren las conexiones activas:

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

Mi observación es que esto no es confiable ya que la transferencia de datos ocurre muy rápidamente. Se consume mucho más tiempo en el procesamiento y procesamiento de datos en la página e incluso los datos de jQuery.active == 0 pueden no estar todavía en la página.

Mucho más sabio es usar un elemento de esperar explícito para mostrarse en la página, ver algunas de las respuestas relacionadas con esto.

La mejor situación es si su aplicación web tiene algún cargador personalizado o indica que los datos están siendo procesados. En este caso, puede esperar a que se oculte esta indicación.

Si el control que está esperando es un elemento web “Ajax”, el siguiente código lo esperará, o cualquier otro elemento web Ajax para terminar de cargar o realizar lo que sea necesario para que pueda continuar sus pasos de manera más segura. .

  public static void waitForAjaxToFinish() { WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(new ExpectedCondition() { public Boolean apply(WebDriver wdriver) { return ((JavascriptExecutor) driver).executeScript( "return jQuery.active == 0").equals(true); } }); } 

Este trabajo para mí

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

Esto funciona como un encanto para mí:

 public void waitForAjax() { try { WebDriverWait driverWait = new WebDriverWait(driver, 10); ExpectedCondition expectation; expectation = new ExpectedCondition() { 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; } 

Para aquellos que están usando Primefaces, simplemente hazlo:

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