Селен подождите, пока документ не будет готов


может ли кто-нибудь позволить мне, как я могу заставить selenium ждать до тех пор, пока страница не загрузится полностью? Я хочу что-то общее, я знаю, что могу настроить WebDriverWait и вызвать что-то вроде "Найти", чтобы заставить его ждать, но я не иду так далеко. Мне просто нужно проверить, что страница загружается успешно и перейти к следующей странице для тестирования.

Я нашел что-то в .net, но не смог заставить его работать на java ...

IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

какие мысли у кого?

25 105

25 ответов:

попробуйте этот код:

  driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);

приведенный выше код будет подождать 10 секунд для загрузки страницы. Если загрузка страницы превышает время, она будет бросать TimeoutException. Вы ловите исключение и выполняете свои потребности. Я не уверен, что он завершает загрузку страницы после выброса исключения. я еще не пробовал этот код. Хочу просто попробовать.

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

дополнительные информация.

предлагаемое решение только ждет DOM readyState сигнал complete. Но селен по умолчанию пытается ждать тех (и немного больше) на странице загружается через driver.get() и element.click() методы. Они уже блокируют, они ждут полной загрузки страницы, и они должны работать нормально.

проблема, очевидно, заключается в перенаправлении через AJAX-запросы и запущенные скрипты - они не могут быть пойманы Selenium, он не ждет их нужно закончить. Кроме того, вы не можете надежно поймать их через readyState - Он ждет немного, что может быть полезно, но он будет сигнализировать complete задолго до загрузки всего содержимого AJAX.

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

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

смотрите мои два ответа относительно этого для получения дополнительной информации:

Это рабочая версия Java пример :

void waitForLoad(WebDriver driver) {
    new WebDriverWait(driver, 30).until((ExpectedCondition<Boolean>) wd ->
            ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
}

пример для c#:

public static void WaitForLoad(this IWebDriver driver, int timeoutSec = 15)
{        
    WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec ));
    wait.Until(wd => wd.ExecuteScript("return document.readyState") == "complete");           
}

вот моя попытка полностью общего решения, в Python:

во-первых, общая функция "ожидания" (используйте WebDriverWait, если хотите, я нахожу их уродливыми):

def wait_for(condition_function):
    start_time = time.time()
    while time.time() < start_time + 3:
        if condition_function():
            return True
        else:
            time.sleep(0.1)
    raise Exception('Timeout waiting for {}'.format(condition_function.__name__))

далее, решение основано на том, что selenium записывает (внутренний) id-номер для всех элементов на странице, включая верхний уровень <html> элемент. Когда страница обновляется или загружается, она получает новый html-элемент с новым идентификатором.

Итак, предполагая, что вы хотите нажать на ссылку с текст "моя ссылка", например:

old_page = browser.find_element_by_tag_name('html')

browser.find_element_by_link_text('my link').click()

def page_has_loaded():
    new_page = browser.find_element_by_tag_name('html')
    return new_page.id != old_page.id

wait_for(page_has_loaded)

для более Pythonic, многоразовый, общий помощник, вы можете сделать менеджер контекста:

from contextlib import contextmanager

@contextmanager
def wait_for_page_load(browser):
    old_page = browser.find_element_by_tag_name('html')

    yield

    def page_has_loaded():
        new_page = browser.find_element_by_tag_name('html')
        return new_page.id != old_page.id

    wait_for(page_has_loaded)

и тогда вы можете использовать его практически на любом взаимодействии Селена:

with wait_for_page_load(browser):
    browser.find_element_by_link_text('my link').click()

Я думаю, что это пуленепробиваемый! А ты как думаешь?

дополнительная информация в сообщение в блоге об этом здесь

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

Javascript:

return (document.readyState == 'complete' && jQuery.active == 0)

полный метод C#:

private void WaitUntilDocumentIsReady(TimeSpan timeout)
{
    var javaScriptExecutor = WebDriver as IJavaScriptExecutor;
    var wait = new WebDriverWait(WebDriver, timeout);            

    // Check if document is ready
    Func<IWebDriver, bool> readyCondition = webDriver => javaScriptExecutor
        .ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)");
    wait.Until(readyCondition);
}
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));

для C# NUnit вам нужно преобразовать WebDriver в JSExecuter, а затем выполнить сценарий, чтобы проверить, есть ли документ.состояние готов или нет. Проверьте ниже код для справки:

 public static void WaitForLoad(IWebDriver driver)
    {
        IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
        int timeoutSec = 15;
        WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec));
        wait.Until(wd => js.ExecuteScript("return document.readyState").ToString() == "complete");
    }

Это будет ждать, пока условие не будет выполнено или тайм-аут.

для начальной загрузки страницы я заметил, что" максимизация " окна браузера практически ждет, пока загрузка страницы не будет завершена (включая источники)

заменить:

AppDriver.Navigate().GoToUrl(url);

С:

public void OpenURL(IWebDriver AppDriver, string Url)
            {
                try
                {
                    AppDriver.Navigate().GoToUrl(Url);
                    AppDriver.Manage().Window.Maximize();
                    AppDriver.SwitchTo().ActiveElement();
                }
                catch (Exception e)
                {
                    Console.WriteLine("ERR: {0}; {1}", e.TargetSite, e.Message);
                    throw;
                }
            }

чем использовать:

OpenURL(myDriver, myUrl);

Это позволит загрузить страницу, дождаться завершения, развернуть и сосредоточиться на нем. Я не знаю, почему это так, но это работает.

Если вы хотите дождаться загрузки страницы после нажатия кнопки Далее или любой другой навигации по странице триггер другой, чем " Navigate ()", ответ Бена Дайера (в этом потоке) будет делать работу.

посмотреть гобелен web-фреймворк. Вы можете скачать исходный код там.

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

<html>
<head>
</head>
<body data-page-initialized="false">
    <p>Write you page here</p>

    <script>
    $(document).ready(function () {
        $(document.body).attr('data-page-initialized', 'true');
    });
    </script>  
</body>
</html>

а затем создать расширение Selenium webdriver (в соответствии с рамками гобелен)

public static void WaitForPageToLoad(this IWebDriver driver, int timeout = 15000)
{
    //wait a bit for the page to start loading
    Thread.Sleep(100);

    //// In a limited number of cases, a "page" is an container error page or raw HTML content
    // that does not include the body element and data-page-initialized element. In those cases,
    // there will never be page initialization in the Tapestry sense and we return immediately.
    if (!driver.ElementIsDisplayed("/html/body[@data-page-initialized]"))
    {                
        return;
    }

    Stopwatch stopwatch = Stopwatch.StartNew();

    int sleepTime = 20;

    while(true)
    {
        if (driver.ElementIsDisplayed("/html/body[@data-page-initialized='true']"))
        {
            return;
        }

        if (stopwatch.ElapsedMilliseconds > 30000)
        {
            throw new Exception("Page did not finish initializing after 30 seconds.");
        }

        Thread.Sleep(sleepTime);
        sleepTime *= 2; // geometric row of sleep time
    }          
}

использовать расширение ElementIsDisplayed написано Алистером Скотт.

public static bool ElementIsDisplayed(this IWebDriver driver, string xpath)
{
    try
    {
        return driver.FindElement(By.XPath(xpath)).Displayed;
    }
    catch(NoSuchElementException)
    {
        return false;
    }
}

и, наконец, создать тест:

driver.Url = this.GetAbsoluteUrl("/Account/Login");            
driver.WaitForPageToLoad();

ответ Бена Драйера не компилировался на моей машине ("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait").

рабочая версия Java 8:

Predicate<WebDriver> pageLoaded = wd -> ((JavascriptExecutor) wd).executeScript(
        "return document.readyState").equals("complete");
new FluentWait<WebDriver>(driver).until(pageLoaded);

версия Java 7:

Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver input) {
            return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
        }

};
new FluentWait<WebDriver>(driver).until(pageLoaded);

в Nodejs вы можете получить его через обещания...

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

driver.get('www.sidanmor.com').then(()=> {
    // here the page is fully loaded!!!
    // do your stuff...
}).catch(console.log.bind(console));

Если вы пишете этот код, вы будете перемещаться, и селен будет ждать 3 секунды...

driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...

из Selenium документации:

этого.get (url ) → Thenable

планирует команду для перехода к заданному URL.

возвращает обещание, которое будет разрешено когда документ имеет загр.

Selenium Documentation (Nodejs)

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

после некоторых поисков я нашел пост на Обай испытательный козел, который имеет решение для этой проблемы. Код c# для этого решения-это что-то вроде это:

 IWebElement page = null;
 ...
 public void WaitForPageLoad()
 {
    if (page != null)
    {
       var waitForCurrentPageToStale = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
       waitForCurrentPageToStale.Until(ExpectedConditions.StalenessOf(page));
    }

    var waitForDocumentReady = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    waitForDocumentReady.Until((wdriver) => (driver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));

    page = driver.FindElement(By.TagName("html"));

}

` Я запускаю этот метод непосредственно после водителя.направлять.gotourl, так что он получает ссылку на страницу как можно скорее. Получайте удовольствие от этого!

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

public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        driver.get("https://www.crowdanalytix.com/#home");
        WebElement webElement = getWebElement(driver, "homekkkkkkkkkkkk");
        int i = 1;
        while (webElement == null && i < 4) {
            webElement = getWebElement(driver, "homessssssssssss");
            System.out.println("calling");
            i++;
        }
        System.out.println(webElement.getTagName());
        System.out.println("End");
        driver.close();
    }

    public static WebElement getWebElement(WebDriver driver, String id) {
        WebElement myDynamicElement = null;
        try {
            myDynamicElement = (new WebDriverWait(driver, 10))
                    .until(ExpectedConditions.presenceOfElementLocated(By
                            .id(id)));
            return myDynamicElement;
        } catch (TimeoutException ex) {
            return null;
        }
    }

Я выполнил код javascript, чтобы проверить, готов ли документ. Сэкономил мне много времени на отладке тестов selenium для сайтов, которые имеют рендеринг на стороне клиента.

public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
    Thread.sleep(100)
    def ready = isDOMReady(driver);
    if (ready) {
        break;
    }
}

}

public static boolean isDOMReady(WebDriver driver){
    return driver.executeScript("return document.readyState");
}

я попробовал этот код и он работает для меня. Я вызываю эту функцию каждый раз, когда я перехожу на другую страницу

public static void waitForPageToBeReady() 
{
    JavascriptExecutor js = (JavascriptExecutor)driver;

    //This loop will rotate for 100 times to check If page Is ready after every 1 second.
    //You can replace your if you wants to Increase or decrease wait time.
    for (int i=0; i<400; i++)
    { 
        try 
        {
            Thread.sleep(1000);
        }catch (InterruptedException e) {} 
        //To check page ready state.

        if (js.executeScript("return document.readyState").toString().equals("complete"))
        { 
            break; 
        }   
      }
 }
public boolean waitForElement(String zoneName, String element, int index, int timeout) {
        WebDriverWait wait = new WebDriverWait(appiumDriver, timeout/1000);
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(element)));
        return true;
    }

Как Рубанов писал для C#, я пишу его для Java, и это:

    public void waitForPageLoaded() {
    ExpectedCondition<Boolean> expectation = new
            ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver driver) {
                    return (((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete")&&((Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")));
                }
            };
    try {
        Thread.sleep(100);
        WebDriverWait waitForLoad = new WebDriverWait(driver, 30);
        waitForLoad.until(expectation);
    } catch (Throwable error) {
        Assert.fail("Timeout waiting for Page Load Request to complete.");
    }
}

в Java это будет как ниже :-

  private static boolean isloadComplete(WebDriver driver)
    {
        return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
                || ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
    }

нормально, когда Селена откроет новую страницу с кнопкой или отправить или получить методами, он будет ждать, взять свои слова обратно, когда страница загружается, но на проблеме это когда на странице есть используем XHR-вызов (Аякс) он никогда не стой на XHR-запрос для загрузки, так что создавая новый способ контролировать XHR и ждать, для них это будет благо. (простите мой плохой английский :D)

public boolean waitForJSandJQueryToLoad() {
    WebDriverWait wait = new WebDriverWait(webDriver, 30);
    // wait for jQuery to load
    ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() {
      @Override
      public Boolean apply(WebDriver driver) {
        try {
            Long r = (Long)((JavascriptExecutor)driver).executeScript("return $.active");
            return r == 0;
        } catch (Exception e) {
            LOG.info("no jquery present");
            return true;
        }
      }
    };

    // wait for Javascript to load
    ExpectedCondition<Boolean> jsLoad = new ExpectedCondition<Boolean>() {
      @Override
      public Boolean apply(WebDriver driver) {
        return ((JavascriptExecutor)driver).executeScript("return document.readyState")
        .toString().equals("complete");
      }
    };

  return wait.until(jQueryLoad) && wait.until(jsLoad);
}

если $.active == 0, поэтому нет активного вызова xhrs (который работает только с jQuery). для вызова javascript ajax вам необходимо создать переменная в вашем проекте и имитировать его.

следующий код, вероятно, должен работать:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));
public void waitForPageToLoad()
  {
(new WebDriverWait(driver, DEFAULT_WAIT_TIME)).until(new ExpectedCondition<Boolean>() {
      public Boolean apply(WebDriver d) {
        return (((org.openqa.selenium.JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete"));
      }
    });//Here DEFAULT_WAIT_TIME is a integer correspond to wait time in seconds

вот что-то похожее, в Ruby:

wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { @driver.execute_script('return document.readyState').eql?('complete') }

вы можете иметь поток сна, пока страница не будет перезагружена. Это не лучшее решение, потому что вы должны иметь оценку того, сколько времени занимает страница для загрузки.

driver.get(homeUrl); 
Thread.sleep(5000);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(userName);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(passWord);
driver.findElement(By.xpath("Your_Xpath_here")).click();

Я проверил загрузку страницы завершена, работа в Selenium 3.14.0

    public static void UntilPageLoadComplete(IWebDriver driver, long timeoutInSeconds)
    {
        Until(driver, (d) =>
        {
            Boolean isPageLoaded = (Boolean)((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete");
            if (!isPageLoaded) Console.WriteLine("Document is loading");
            return isPageLoaded;
        }, timeoutInSeconds);
    }

    public static void Until(IWebDriver driver, Func<IWebDriver, Boolean> waitCondition, long timeoutInSeconds)
    {
        WebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
        webDriverWait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
        try
        {
            webDriverWait.Until(waitCondition);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

вы используете угловые? Если это так, возможно, что webdriver не распознает, что асинхронные вызовы завершены.

Я рекомендую посмотреть на Paul Hammants ngWebDriver. Метод waitForAngularRequestsToFinish () может пригодиться.