【问题标题】:How to get selenium to wait for ajax response?如何让硒等待ajax响应?
【发布时间】:2011-02-19 13:57:01
【问题描述】:

如何让 selenium 等待日历小部件之类的加载?现在我只是在将测试用例导出到 junit 程序后做一个Thread.sleep(2500)

【问题讨论】:

    标签: ajax unit-testing selenium


    【解决方案1】:

    在我的情况下,问题似乎是由于 ajax 延迟,但与主页内的内部 iframe 有关。 在 seleminum 中,可以通过以下方式切换到内部框架:

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

    我使用java。之后,我能够找到元素

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

    【讨论】:

      【解决方案2】:

      使用 webdriver aka selenium2,您可以使用前面提到的隐式等待配置 https://www.selenium.dev/documentation/en/webdriver/waits/#implicit-wait

      使用 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"));
      

      或者使用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")
      

      【讨论】:

        【解决方案3】:

        以下是我的获取代码。带我去研究,因为 jQuery.active 不适用于 fetch。这是帮助我代理获取的答案,但它仅适用于 ajax 不能获取 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);
            }
        }
        

        【讨论】:

          【解决方案4】:

          这对我来说就像一个魅力:

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

          【讨论】:

            【解决方案5】:

            这对我有用

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

            【讨论】:

              【解决方案6】:

              如果您正在等待的控件是“Ajax”网页元素,则以下代码将等待它或任何其他 Ajax 网页元素完成加载或执行它需要执行的任何操作,以便您可以更安全地继续跟着你的脚步。

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

              【讨论】:

              • 请尝试在您的代码中添加一些解释,说明您认为此代码如何解决 OP 的问题。
              【解决方案7】:

              如上所述,您可以等待活动连接关闭:

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

              我的观察是这不可靠,因为数据传输发生得非常快。页面上的数据处理和呈现花费了更多时间,甚至jQuery.active == 0 数据可能还没有出现在页面上。

              更明智的做法是使用explicit wait 作为页面上显示的元素,请参阅与此相关的一些答案。

              最好的情况是,如果您的 Web 应用程序有一些自定义加载器或指示正在处理数据。在这种情况下,您可以等待此指示隐藏。

              【讨论】:

                【解决方案8】:

                我写了下一个方法作为我的解决方案(我没有任何负载指示器):

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

                然后我用实际的驱动程序调用了这个方法。 此post 中有更多描述。

                【讨论】:

                • 投反对票。答案应该包含实际答案......它们不是为了吸引您的个人博客的流量。
                • @CoreyGoldberg 我不关心流量,但是好的,我将在此处插入帖子中的代码。这篇文章只是另一种选择,因为其他答案对我不起作用。
                • @CoreyGoldberg 我希望你现在没问题;)
                【解决方案9】:

                这是一个基于 Morten Christiansen 的回答的 groovy 版本。

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

                【讨论】:

                  【解决方案10】:

                  下面的代码 (C#) 确保显示目标元素:

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

                  如果页面支持 jQuery,则可以使用jQuery.active function 确保在所有 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;
                          }
                      }
                  

                  【讨论】:

                    【解决方案11】:

                    如果使用python,你可以使用这个函数,点击按钮并等待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)
                    

                    (信用:基于this stack overflow answer

                    【讨论】:

                    • 不幸的是,有些页面的加载动画破坏了这个技巧,因为 page_source 已更新但您还没有数据。
                    • 它对我有用,我有一个页面,只需单击一个按钮,就有多个 XHR 请求,所以只等待jQuery.active == 0 是行不通的。
                    • 谢谢@nimo,这很好用。我的页面上有一个加载动画,所以我将driver.page_source 行修改为driver.find_element_by_xpath(comparator_xpath).get_attribute('innerHTML')。这样一来,当加载元素处于动画状态时发生的事情就无关紧要了。
                    【解决方案12】:

                    我也有类似的情况,我想等ajax请求让加载面板消失,我检查了请求前后的html,发现ajax加载面板有一个div,dix ajax请求时显示,请求结束后隐藏。我创建了一个函数来等待面板显示,然后等待它被隐藏

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

                    查看this了解更多详情

                    【讨论】:

                      【解决方案13】:

                      对于那些使用 primefaces 的人,只需这样做:

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

                      【讨论】:

                      • waitForCondition void waitForCondition(java.lang.String script, java.lang.String timeout) 重复运行指定的 JavaScript sn-p 直到它评估为“真”。 sn-p 可能有多行,但只会考虑最后一行的结果。请注意,默认情况下,sn-p 将在运行器的测试窗口中运行,而不是在应用程序的窗口中。要获取应用程序的窗口,您可以使用 JavaScript sn-p selenium.browserbot.getCurrentWindow(),然后在其中运行您的 JavaScript
                      • 它仅适用于 Selenium RC。 Selenium WebDriver 不支持功能
                      • WebDriver和IDE一样吗?
                      【解决方案14】:

                      比等待元素更通用的解决方案是等待与服务器的所有连接关闭。这将允许您等待所有 ajax 调用完成,即使它们没有任何回调因此不会影响页面。更多详情请见here

                      使用 C# 和 jQuery,我创建了以下方法来等待所有 AJax 调用完成(如果有人有更直接的方法从 C# 访问 JS 变量,请发表评论):

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

                      【讨论】:

                      • this.selenium.GetEval("(window.jQuery || { active : 0 }).active ") 比输入框直接一点。如果页面上没有 jQuery,它也不会出错。
                      • 变量__selenium的类型是什么?我无法在对象模型中找到它
                      • Mauricio,自从我使用 Selenium 以来已经有一段时间了,所以我不记得细节了,但我猜应该是 IWebDriver 实例。
                      • @AskarKalykov 完全正确。
                      • 很好,试图找出相同的东西,但这里是 PHP:stackoverflow.com/questions/64590437/…
                      【解决方案15】:

                      我会用

                      waitForElementPresent(locator)
                      

                      这将等到元素出现在 DOM 中。

                      如果您需要检查元素是否可见,您可能会更好地使用

                      waitForElementHeight(locator)
                      

                      【讨论】:

                      • 如果 ajax 重定向而 selenium 不知道怎么办?如何使用它?
                      猜你喜欢
                      • 2013-11-24
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-04-24
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多