Comment obtenir du selenium pour attendre une réponse ajax?

Comment puis-je obtenir du selenium pour attendre quelque chose comme un widget de calendrier à charger? En ce moment, je ne fais qu’un Thread.sleep(2500) après avoir exporté le test dans un programme junit.

j’utiliserais

 waitForElementPresent(locator) 

Cela attendra que l’élément soit présent dans le DOM.

Si vous devez vérifier que l’élément est visible, vous feriez peut-être mieux

 waitForElementHeight(locator) 

Une solution plus générale que d’attendre un élément serait d’attendre que toutes les connexions au serveur se ferment. Cela vous permettra d’attendre la fin de tous les appels ajax, même s’ils n’ont aucun rappel et n’affectent donc pas la page. Plus de détails peuvent être trouvés ici .

En utilisant C # et jQuery, j’ai créé la méthode suivante pour attendre que tous les appels AJax soient terminés (si vous avez des moyens plus directs d’accéder aux variables JS à partir de C #, veuillez commenter):

 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, ssortingng 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 vous utilisez python, vous pouvez utiliser cette fonction, qui clique sur le bouton et attend le changement 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) 

(CREDIT: basé sur cette réponse de débordement de stack )

Avec webdriver aka selenium2, vous pouvez utiliser une configuration d’attente implicite telle que mentionnée sur http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits

En utilisant 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")); 

Ou en utilisant 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") 

J’ai écrit la méthode suivante comme solution (je n’avais pas d’indicateur de charge):

 public static void waitForAjax(WebDriver driver, Ssortingng 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();"); } 

Ensuite, j’ai appelé cette méthode avec le pilote réel. Plus de description dans cet article .

J’ai eu une situation similaire, je voulais attendre les requêtes ajax pour que le panneau de chargement ait disparu, j’ai inspecté le HTML avant et après les requêtes, trouvé qu’il y avait un div pour le panneau de chargement ajax, le dix est affiché pendant la requête ajax, et masquée après la fin de la requête. J’ai créé une fonction pour attendre l’affichage du panneau, puis attendez qu’il soit caché


public void WaitForModalPanel() { ssortingng 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))); }

Vérifiez ceci pour plus de détails

Le code (C #) ci-dessous garantit que l’élément cible est affiché:

  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 page prend en charge jQuery, vous pouvez utiliser la fonction jQuery.active pour vous assurer que l’élément cible est récupéré une fois tous les appels ajax terminés:

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

Voici une version groovy basée sur la réponse 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, Ssortingng 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) } 

Comme mentionné ci-dessus, vous pouvez attendre que les connexions actives soient fermées:

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

Mon observation est que ce n’est pas fiable car le transfert de données se produit très rapidement. Beaucoup plus de temps est consacré au traitement des données et au rendu sur la page et même jQuery.active == 0 données peuvent ne pas être encore sur la page.

Plus sage est d’utiliser une attente explicite pour que l’élément soit affiché sur la page, voir certaines des réponses à ce sujet.

La meilleure situation est que votre application Web ait un chargeur personnalisé ou une indication du traitement des données. Dans ce cas, vous pouvez simplement attendre que cette indication se cache.

Si le contrôle que vous attendez est un élément Web “Ajax”, le code suivant l’attendra, ou tout autre élément Web Ajax pour terminer le chargement ou effectuer tout ce qu’il doit faire afin que vous puissiez continuer vos étapes en toute sécurité .

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

Ce travail pour moi

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

Cela fonctionne comme un charme pour moi:

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

Pour ceux qui utilisent des prime, il suffit de faire:

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