Как заставить селен ждать ответа ajax? - PullRequest
57 голосов
/ 14 мая 2010

Как я могу заставить селен ждать что-то вроде виджета календаря для загрузки? Прямо сейчас я просто делаю Thread.sleep(2500) после экспорта тестового примера в программу junit.

Ответы [ 13 ]

41 голосов
/ 14 мая 2010

Я бы использовал

waitForElementPresent(locator)

Это будет ждать, пока элемент присутствует в DOM.

Если вам нужно проверить, видим ли элемент, лучше использовать

waitForElementHeight(locator)
23 голосов
/ 15 февраля 2011

Более общим решением, чем ожидание элемента, было бы ожидание закрытия всех соединений с сервером.Это позволит вам дождаться завершения всех вызовов ajax, даже если они не имеют обратного вызова и, следовательно, не влияют на страницу.Более подробную информацию можно найти здесь .

Используя C # и jQuery, я создал следующий метод для ожидания завершения всех вызовов AJax (если у кого-то есть более прямые способы доступа к переменным JS изC #, пожалуйста, прокомментируйте):

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);
}
11 голосов
/ 30 апреля 2014

Если вы используете 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)

(КРЕДИТ: на основе ответ о переполнении стека )

6 голосов
/ 15 октября 2013

С webdriver aka selenium2 вы можете использовать неявную конфигурацию ожидания, как упомянуто на http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits

Использование 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 голосов
/ 23 июля 2015

Я написал следующий метод в качестве решения (у меня не было индикатора загрузки):

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

Тогда я только что вызвал этот метод с фактическим драйвером. Больше описания в этом сообщении .

2 голосов
/ 11 октября 2017

Эта работа для меня

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");
        }
    });
}
1 голос
/ 10 февраля 2017

Для меня это работает как шарм:

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;
}
1 голос
/ 17 марта 2016

Вот отличная версия, основанная на ответе Мортена Кристиансена.

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)
}
1 голос
/ 01 февраля 2016

Код (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 , чтобы обеспечить получение целевого элемента после завершения всех вызовов 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;
        }
    }
1 голос
/ 21 марта 2015

Как уже упоминалось выше, вы можете дождаться закрытия активных соединений:

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

По моим наблюдениям, это ненадежно, поскольку передача данных происходит очень быстро. Гораздо больше времени уходит на обработку и рендеринг данных на странице, и даже jQuery.active == 0 данные могут быть еще не на странице.

Гораздо разумнее использовать явное ожидание для отображения элемента на странице, см. Некоторые ответы, связанные с этим.

В лучшем случае, если в вашем веб-приложении есть какой-либо пользовательский загрузчик или указатель того, что данные обрабатываются. В этом случае вы можете просто подождать, пока этот индикатор не будет скрыт.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...