112

Selenium でテストする Web アプリケーションがあります。ページの読み込み時に多数の JavaScript が実行されています。
この JavaScript コードはあまりよく書かれていませんが、何も変更できません。そのため、メソッドを使用して要素が DOM に表示されるのを待つことfindElement()はできません。
ページが読み込まれるのを待つためにJavaで汎用関数を作成したいのですが、考えられる解決策は次のとおりです。

  • WebDriver から JavaScript スクリプトを実行し、結果をdocument.body.innerHTML文字列変数に格納しますbody
  • body変数を以前のバージョンの と比較しますbody。それらが同じである場合は、カウンターをインクリメントするように設定します。notChangedCountそうでない場合notChangedCountはゼロに設定します。
  • しばらく待ちます (たとえば 50 ミリ秒)。
  • ページがしばらくの間 (たとえば 500 ミリ秒) 変更されていない場合はnotChangedCount >= 10、ループを終了します。それ以外の場合は、最初のステップにループします。

それは有効な解決策だと思いますか?

4

18 に答える 18

78

誰かが実際に一般的で常に適用可能な答えを知っていれば、それは何年も前にどこでも実装され、私たちの生活をとても楽にしてくれるでしょう.

できることはたくさんありますが、そのすべてに問題があります。

  1. Ashwin Prabhu が言ったように、スクリプトをよく知っていれば、スクリプトの動作を観察し、変数の一部を追跡windowできdocumentます。ページ。

  2. HTMLコードを観察し、それがしばらく変更されているかどうかにかかわらず、あなたの解決策は悪くありません(また、編集されていない元のHTMLを直接取得する方法WebDriverもあります)が、:

    • 実際にページをアサートするには長い時間がかかり、テストが大幅に長引く可能性があります。
    • 適切な間隔が何であるかは決してわかりません。スクリプトは、500 ミリ秒以上かかる大きなものをダウンロードしている可能性があります。弊社の社内ページにはIEで数秒かかるスクリプトがいくつかあります。コンピューターのリソースが一時的に不足している可能性があります。たとえば、アンチウイルスによって CPU が完全に機能するようになるとします。その場合、複雑でないスクリプトでも 500 ミリ秒では短すぎる可能性があります。
    • 一部のスクリプトは実行されません。彼らは少し遅れて自分自身を呼び出し ( setTimeout())、何度も何度も動作し、実行するたびに HTML を変更する可能性があります。真剣に、すべての「Web 2.0」ページでそれが行われています。スタック オーバーフローも。使用される最も一般的な方法を上書きして、それらを使用するスクリプトを完成したものと見なすことはできますが、確かではありません。
    • スクリプトが HTML の変更以外のことを行う場合はどうなるでしょうか? innerHTML楽しいだけでなく、何千ものことができます。
  3. これを支援するツールがあります。つまり、 nsIWebProgressListenerなどと一緒に進行リスナーです。ただし、これに対するブラウザのサポートはひどいものです。Firefox は FF4 以降 (まだ進化中) からサポートを試み始め、IE は IE9 で基本的なサポートを提供しています。

そして、すぐに別の欠陥のある解決策を思い付くことができると思います. 実際のところ、「ページが完成した」といつ言えるかについて明確な答えはありません。永遠に続くスクリプトが作業を行っているからです。最も役立つものを選択しますが、その欠点に注意してください.

于 2012-05-23T19:12:22.553 に答える
34

ありがとうアシュウィン!

私の場合、いくつかの要素でjqueryプラグインの実行を待つ必要があります..具体的には「qtip」

あなたのヒントに基づいて、それは私にとって完璧に機能しました:

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

注:Webdriver 2を使用しています

于 2013-10-17T17:25:01.713 に答える
28

Javascript と jQuery の読み込みが完了するまで待つ必要があります。Javascript を実行してjQuery.activeis0およびdocument.readyStateisかどうかを確認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);
}
于 2015-05-05T16:59:14.440 に答える
10

JS ライブラリは、ウィンドウでよく知られている変数を定義/初期化しますか?

その場合は、変数が表示されるまで待つことができます。使用できます

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

この状態 (: のようなもの) をテストし、ブール値の/window.SomeClass && window.SomeClass.variable != nullを返します。truefalse

これを でラップしWebDriverWait、スクリプトが を返すまで待ち​​ますtrue

于 2012-05-23T15:51:41.330 に答える
1

Selenium ライブラリにはnodejs、次のスニペットを使用しました。私の場合、ウィンドウに追加される 2 つのオブジェクトを探してい<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);
});
于 2018-04-20T03:36:58.910 に答える
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 アニメーションが実行されている場合にリセットできます (アプリが ajax 呼び出しに jQuery を使用している場合のみ...)
...

" 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");
        }
    };
}
于 2014-09-10T20:37:11.497 に答える
0

これを処理するロジックを記述できます。を返すメソッドを作成しました。WebElementこのメソッドは 3 回呼び出されます。または、時間を増やして 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;
        }
    }
于 2014-03-13T11:55:11.007 に答える
-1

これが私がそれを行う方法です:

new WebDriverWait(driver, 20).until(
       ExpectedConditions.jsReturnsValue(
                   "return document.readyState === 'complete' ? true : false"));
于 2018-07-05T23:44:41.317 に答える