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

может ли кто-нибудь позволить мне, как я могу заставить 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 ответов


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

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

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

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

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


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

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

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

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

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


Это рабочая версия 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:

во-первых, общая функция "wait" (используйте 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-фреймворк. Ты можешь!--7-->скачать исходный код там.

идея состоит в том, чтобы сигнализировать, что страница готова по 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 написанной Алистером Скотт!--6-->.

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

Если вы напишете этот код, вы будете перемещаться, и selenium будет ждать 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-адресу.

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

Селен Документация (Nodejs)


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

после некоторых поисков я нашел пост на Obay козел испытания, который имеет решение для этой проблемы. Код 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 и этот метод будет вызываться три раза, или вы можете увеличить время и добавить нулевую проверку для 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);
}

if $.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);
        }
    }

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

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