Вопрос по webdriver, java, selenium-webdriver, selenium – Selenium WebDriver: дождитесь загрузки сложной страницы с JavaScript

85

У меня есть веб-приложение для тестирования с Selenium. При загрузке страницы выполняется много JavaScript.
Этот код JavaScript не так хорошо написан, но я ничего не могу изменить. Так что ожидание появления элемента в DOM сfindElement() метод не вариант.
Я хочу создать универсальную функцию в Java, чтобы ждать загрузки страницы, возможное решение будет:

run a JavaScript script form WebDriver and store the result of document.body.innerHTML in a string variable body. compare the body variable to the previous version of body. if they are the same then set increment a counter notChangedCount otherwise set notChangedCount to zero. wait for a litte time (50 ms for example). if the page has not changed for some time (500 ms for example) so notChangedCount >= 10 then exit the loop otherwise loop to the first step.

Как вы думаете, это правильное решение?

findElement () не ждет - что вы имеете в виду? Rostislav Matl
Я забыл об этом - я привык использовать WebDriverWait и ExpectedCondition, что делает его более гибким. Rostislav Matl
findElement ожидает доступности элемента, но иногда этот элемент доступен до полной инициализации кода javascript, поэтому он не является опцией. psadac

Ваш Ответ

14   ответов
2

Это решение работает для меня из WebDriverDoku:

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

0


Window.setTimeout выполняется только когда браузер не используется.
Поэтому рекурсивный (42 раза) вызов функции займет 100 мс, если в браузере нет активности, и гораздо больше, если браузер занят чем-то другим.

    ExpectedCondition<Boolean> javascriptDone = new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver d) {
            try{//window.setTimeout executes only when browser is idle,
                //introduces needed wait time when javascript is running in browser
                return  ((Boolean) ((JavascriptExecutor) d).executeAsyncScript( 

                        " var callback =arguments[arguments.length - 1]; " +
                        " var count=42; " +
                        " setTimeout( collect, 0);" +
                        " function collect() { " +
                            " if(count-->0) { "+
                                " setTimeout( collect, 0); " +
                            " } "+
                            " else {callback(" +
                            "    true" +                            
                            " );}"+                             
                        " } "
                    ));
            }catch (Exception e) {
                return Boolean.FALSE;
            }
        }
    };
    WebDriverWait w = new WebDriverWait(driver,timeOut);  
    w.until(javascriptDone);
    w=null;

В качестве бонуса счетчик может быть сброшен для document.readyState или jQuery Ajax-вызовов или если запущена какая-либо анимация jQuery (только если ваше приложение использует jQuery для ajax-вызовов ...)
...

" function collect() { " +
                            " if(!((typeof jQuery === 'undefined') || ((jQuery.active === 0) && ($(\":animated\").length === 0))) && (document.readyState === 'complete')){" +
                            "    count=42;" +
                            "    setTimeout( collect, 0); " +
                            " }" +
                            " else if(count-->0) { "+
                                " setTimeout( collect, 0); " +
                            " } "+ 

...

РЕДАКТИРОВАТЬ: я заметил, что executeAsyncScript не работает должным образом, если новая страница загружается, и тест может перестать отвечать неопределенно, лучше использовать вместо этого.

public static ExpectedCondition<Boolean> documentNotActive(final int counter){ 
    return new ExpectedCondition<Boolean>() {
        boolean resetCount=true;
        @Override
        public Boolean apply(WebDriver d) {

            if(resetCount){
                ((JavascriptExecutor) d).executeScript(
                        "   window.mssCount="+counter+";\r\n" + 
                        "   window.mssJSDelay=function mssJSDelay(){\r\n" + 
                        "       if((typeof jQuery != 'undefined') && (jQuery.active !== 0 || $(\":animated\").length !== 0))\r\n" + 
                        "           window.mssCount="+counter+";\r\n" + 
                        "       window.mssCount-->0 &&\r\n" + 
                        "       setTimeout(window.mssJSDelay,window.mssCount+1);\r\n" + 
                        "   }\r\n" + 
                        "   window.mssJSDelay();");
                resetCount=false;
            }

            boolean ready=false;
            try{
                ready=-1==((Long) ((JavascriptExecutor) d).executeScript(
                        "if(typeof window.mssJSDelay!=\"function\"){\r\n" + 
                        "   window.mssCount="+counter+";\r\n" + 
                        "   window.mssJSDelay=function mssJSDelay(){\r\n" + 
                        "       if((typeof jQuery != 'undefined') && (jQuery.active !== 0 || $(\":animated\").length !== 0))\r\n" + 
                        "           window.mssCount="+counter+";\r\n" + 
                        "       window.mssCount-->0 &&\r\n" + 
                        "       setTimeout(window.mssJSDelay,window.mssCount+1);\r\n" + 
                        "   }\r\n" + 
                        "   window.mssJSDelay();\r\n" + 
                        "}\r\n" + 
                        "return window.mssCount;"));
            }
            catch (NoSuchWindowException a){
                a.printStackTrace();
                return true;
            }
            catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return ready;
        }
        @Override
        public String toString() {
            return String.format("Timeout waiting for documentNotActive script");
        }
    };
}
1

isProcessing & quot; что я могу получить доступ (к объекту «ae»), который они устанавливают, когда что-то запускается, и очищать, когда все готово. Затем я запускаю его в аккумуляторе, который проверяет его каждые 100 мс, пока он не получит пять подряд, то есть всего 500 мс без каких-либо изменений. Если пройдет 30 секунд, я выдаю исключение, потому что к тому времени что-то должно было случиться. Это в C #.

public static void WaitForDocumentReady(this IWebDriver driver)
{
    Console.WriteLine("Waiting for five instances of document.readyState returning 'complete' at 100ms intervals.");
    IJavaScriptExecutor jse = (IJavaScriptExecutor)driver;
    int i = 0; // Count of (document.readyState === complete) && (ae.isProcessing === false)
    int j = 0; // Count of iterations in the while() loop.
    int k = 0; // Count of times i was reset to 0.
    bool readyState = false;
    while (i < 5)
    {
        System.Threading.Thread.Sleep(100);
        readyState = (bool)jse.ExecuteScript("return ((document.readyState === 'complete') && (ae.isProcessing === false))");
        if (readyState) { i++; }
        else
        {
            i = 0;
            k++;
        }
        j++;
        if (j > 300) { throw new TimeoutException("Timeout waiting for document.readyState to be complete."); }
    }
    j *= 100;
    Console.WriteLine("Waited " + j.ToString() + " milliseconds. There were " + k + " resets.");
}
4

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

public void waitForJavascript(int maxWaitMillis, int pollDelimiter) {
    double startTime = System.currentTimeMillis();
    while (System.currentTimeMillis() < startTime + maxWaitMillis) {
        String prevState = webDriver.getPageSource();
        Thread.sleep(pollDelimiter); // <-- would need to wrap in a try catch
        if (prevState.equals(webDriver.getPageSource())) {
            return;
        }
    }
}
Сначала я обнаружил, что это грязное решение, но, похоже, оно прекрасно работает и не требует установки переменных на стороне клиента. Одним предупреждением может быть страница, которая постоянно изменяется с помощью javascript (то есть часов javascript), но у меня нет такой возможности, чтобы она работала!
Здесь я хотел бы отметить, что я зарегистрировал время, необходимое для опроса страницы среднего размера, дважды, а затем сравнил эти строки в Java, и это заняло чуть более 20 мс.
0

чтобы справиться с этим. Я написал метод, который будет возвращать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;
        }
    }
0

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

WebDriverWait wait = new WebDriverWait(driver, 50);

Использование ниже redayState будет ждать до загрузки страницы

wait.until((ExpectedCondition<Boolean>) wd ->
   ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));

Ниже JQuery будет ждать, пока данные не будут загружены

  int count =0;
            if((Boolean) executor.executeScript("return window.jQuery != undefined")){
                while(!(Boolean) executor.executeScript("return jQuery.active == 0")){
                    Thread.sleep(4000);
                    if(count>4)
                        break;
                    count++;
                }
            }

После этих JavaScriptCode попробуйте найти наш webElement.

WebElement we = wait.until(ExpectedConditions.presenceOfElementLocated(by));
Selenium также делает это по умолчанию, это просто добавит дополнительное время выполнения кода к вашему сценарию (которого может быть достаточно для загрузки дополнительных вещей, но, опять же, может и не быть)
61

он был бы реализованeverywhere давным-давно и сделает нашу жизнь намного проще.

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

As Ashwin Prabhu said, if you know the script well, you can observe its behaviour and track some of its variables on window or document etc. This solution, however, is not for everyone and can be used only by you and only on a limited set of pages.

Your solution by observing the HTML code and whether it has or hasn't been changed for some time is not bad (also, there is a method to get the original and not-edited HTML directly by WebDriver), but:

It takes a long time to actually assert a page and could prolong the test significantly. You never know what the right interval is. The script might be downloading something big that takes more than 500 ms. There are several scripts on our company's internal page that take several seconds in IE. Your computer may be temporarily short on resources - say that an antivirus will make your CPU work fully, then 500 ms may be too short even for a noncomplex scripts. Some scripts are never done. They call themselves with some delay (setTimeout()) and work again and again and could possibly change the HTML every time they run. Seriously, every "Web 2.0" page does it. Even Stack Overflow. You could overwrite the most common methods used and consider the scripts that use them as completed, but ... you can't be sure. What if the script does something other than changing the HTML? It could do thousands of things, not just some innerHTML fun.

There are tools to help you on this. Namely Progress Listeners together with nsIWebProgressListener and some others. The browser support for this, however, is horrible. Firefox began to try to support it from FF4 onwards (still evolving), IE has basic support in IE9.

И я думаю, что скоро смогу найти другое ошибочное решение. Дело в том, что нет точного ответа о том, когда сказать «теперь страница заполнена». из-за вечных сценариев, выполняющих свою работу. Выберите тот, который служит вам лучше всего, но остерегайтесь его недостатков.

23

Выполните Javascript, чтобы проверить,jQuery.active является0 а такжеdocument.readyState являетсяcomplete, что означает, что загрузка JS и jQuery завершена.

public boolean waitForJStoLoad() {

    WebDriverWait wait = new WebDriverWait(driver, 30);

    // wait for jQuery to load
    ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() {
      @Override
      public Boolean apply(WebDriver driver) {
        try {
          return ((Long)executeJavaScript("return jQuery.active") == 0);
        }
        catch (Exception e) {
          return true;
        }
      }
    };

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

  return wait.until(jQueryLoad) && wait.until(jsLoad);
}
document.readyState == 'complete' && jQuery.active == 0 прекрасно работает, но есть ли что-то подобное для React? Поскольку эти проверки не поймут, что все еще загружаются данные / компонент реакции?
Это работает и для меня, но есть одна хитрость: скажем, ваша страница уже загружена. Если вы взаимодействуете с элементом [например, отправить .click () или .send несколько ключей к нему], а затем вы хотите проверить, когда ваша страница закончила обработку, вам нужно добавить задержку: например, щелкните (), переведите в спящий режим (0,5 с), подождите, пока (readyState = 'complete' & amp; jQuery.active == 0). Если вы не добавите спящий режим, iQuery не будет активен во время теста! (у меня ушло несколько часов, чтобы выяснить это, поэтому я решил поделиться этим)
Не уверен, что это будет работать вevery обстоятельства, но это работает удовольствие для моего случая использования
27

В моем случае мне нужно подождать выполнения плагина jquery в каком-то элементе, в частности & quot; qtip & quot;

Исходя из твоего намека, он отлично сработал для меня:

wait.until( new Predicate<WebDriver>() {
            public boolean apply(WebDriver driver) {
                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
            }
        }
    );

Note: I'm using Webdriver 2

Откуда исходит Predicate? какой импорт ??
@AmadoSaladino & quot; импорт com.google.common.base.Predicate; & quot; из ссылки на google lib guava-15.0:mvnrepository.com/artifact/com.google.guava/guava/15.0 Есть более новая версия 27.0, вы можете попробовать!
Этот блок кода прекрасно работает для меня.
Это работает очень хорошо для меня тоже. В моем случае элемент был изменен Javascript сразу после завершения загрузки страницы, и Selenium не ожидала этого неявно.
8

енную в окне?

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

((JavascriptExecutor)driver).executeScript(String script, Object... args)

проверить это условие (что-то вроде:window.SomeClass && window.SomeClass.variable != null) и вернуть логическое значениеtrue / false.

Оберните это вWebDriverWaitи подождите, пока скрипт не вернетсяtrue.

0

Вот как я делаю ожидание iFrame. Это требует, чтобы ваш тестовый класс JUnit передавал экземпляр RemoteWebDriver в объект страницы:

public class IFrame1 extends LoadableComponent<IFrame1> {

    private RemoteWebDriver driver;

    @FindBy(id = "iFrame1TextFieldTestInputControlID" )
    public WebElement iFrame1TextFieldInput;

    @FindBy(id = "iFrame1TextFieldTestProcessButtonID" )
    public WebElement copyButton;

    public IFrame1( RemoteWebDriver drv ) {
        super();
        this.driver = drv;
        this.driver.switchTo().defaultContent();
        waitTimer(1, 1000);
        this.driver.switchTo().frame("BodyFrame1");
        LOGGER.info("IFrame1 constructor...");
    }

    @Override
    protected void isLoaded() throws Error {        
        LOGGER.info("IFrame1.isLoaded()...");
        PageFactory.initElements( driver, this );
        try {
            assertTrue( "Page visible title is not yet available.", driver
     .findElementByCssSelector("body form#webDriverUnitiFrame1TestFormID h1")
                    .getText().equals("iFrame1 Test") );
        } catch ( NoSuchElementException e) {
            LOGGER.info("No such element." );
            assertTrue("No such element.", false);
        }
    }

    @Override
    protected void load() {
        LOGGER.info("IFrame1.load()...");
        Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
                .withTimeout(30, TimeUnit.SECONDS)
                .pollingEvery(5, TimeUnit.SECONDS)
                .ignoring( NoSuchElementException.class ) 
                .ignoring( StaleElementReferenceException.class ) ;
            wait.until( ExpectedConditions.presenceOfElementLocated( 
            By.cssSelector("body form#webDriverUnitiFrame1TestFormID h1") ) );
    }
....

ПРИМЕЧАНИЕ: вы можетеувидеть весь мой рабочий пример здесь.

1

nodejs Библиотека Selenium, я использовал следующий фрагмент. В моем случае я искал два объекта, которые добавляются в окно, которые в этом примере<SOME PROPERTY>, 10000 время ожидания в миллисекундах,<NEXT STEP HERE> это то, что происходит после того, как свойства найдены в окне.

driver.wait( driver => {
    return driver.executeScript( 'if(window.hasOwnProperty(<SOME PROPERTY>) && window.hasOwnProperty(<SOME PROPERTY>)) return true;' ); }, 10000).then( ()=>{
        <NEXT STEP HERE>
}).catch(err => { 
    console.log("looking for window properties", err);
});
-1

new WebDriverWait(driver, 20).until(
       ExpectedConditions.jsReturnsValue(
                   "return document.readyState === 'complete' ? true : false"));
Пробовал, не работает.
Selenium также делает это по умолчанию, это просто добавит дополнительное время выполнения кода к вашему сценарию (которого может быть достаточно для загрузки дополнительных вещей, но, опять же, может и не быть)
3

ержит сложные сценарии Java

public void checkPageIsReady() {

  JavascriptExecutor js = (JavascriptExecutor)driver;


  //Initially bellow given if condition will check ready state of page.
  if (js.executeScript("return document.readyState").toString().equals("complete")){ 
   System.out.println("Page Is loaded.");
   return; 
  } 

  //This loop will rotate for 25 times to check If page Is ready after every 1 second.
  //You can replace your value with 25 If you wants to Increase or decrease wait time.
  for (int i=0; i<25; i++){ 
   try {
    Thread.sleep(1000);
    }catch (InterruptedException e) {} 
   //To check page ready state.
   if (js.executeScript("return document.readyState").toString().equals("complete")){ 
    break; 
   }   
  }
 }

Источник -Как ждать загрузки страницы / готовности в Selenium WebDriver

Вы должны использовать селен ожидания вместо этих циклов

Похожие вопросы