it-swarm.com.de

Selen warten, bis das Dokument fertig ist

Kann mir jemand sagen, wie ich Selenium warten lassen kann, bis die Seite vollständig geladen ist? Ich möchte etwas Generisches, ich weiß, ich kann WebDriverWait konfigurieren und so etwas wie 'find' aufrufen, damit es warten kann, aber ich gehe nicht so weit. Ich muss nur testen, ob die Seite erfolgreich geladen wurde, und zur nächsten Seite übergehen, um zu testen.

Ich habe etwas in .net gefunden, konnte es aber nicht in Java zum Laufen bringen ...

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

Irgendwelche Gedanken?

115
Girish

Versuchen Sie diesen Code:

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

Der obige Code wartet bis zu 10 Sekunden auf das Laden der Seite. Wenn das Laden der Seite die Zeit überschreitet, wird der TimeoutException ausgegeben. Sie fangen die Ausnahme und machen Ihre Bedürfnisse. Ich bin nicht sicher, ob das Laden der Seite nach der ausgelösten Ausnahme beendet wird. Ich habe diesen Code noch nicht ausprobiert. Will es einfach mal probieren.

Dies ist ein implizites Warten. Wenn Sie dies einmal festlegen, hat es den Gültigkeitsbereich, bis die Web Driver-Instanz zerstört wird.

Für mehr info.

74
Manigandan

Ihre vorgeschlagene Lösung wartet nur darauf, dass DOM readyStatecomplete signalisiert. Selen versucht jedoch standardmäßig, auf diese (und ein wenig mehr) Seitenladevorgänge über die Methoden driver.get() und element.click() zu warten. Sie blockieren bereits, sie warten, bis die Seite vollständig geladen ist und die sollten funktionieren.

Das Problem sind natürlich Umleitungen über AJAX - Anforderungen und das Ausführen von Skripts. Diese können von Selenium nicht abgefangen werden, es wartet nicht, bis sie abgeschlossen sind. Sie können sie auch nicht zuverlässig über readyState abfangen - es wartet etwas, was nützlich sein kann, aber complete wird signalisiert, lange bevor der gesamte AJAX - Inhalt heruntergeladen wird.

Es gibt keine generelle Lösung, die überall und für jeden funktionieren würde, deshalb ist es schwierig und jeder verwendet etwas anderes.

Die allgemeine Regel besteht darin, sich auf WebDriver zu verlassen, um seine Rolle zu übernehmen, dann implizite Wartezeiten zu verwenden und dann explizite Wartezeiten für Elemente zu verwenden, die Sie auf der Seite bestätigen möchten. Sie sollten auf Ihrer getesteten Seite diejenige (oder eine Kombination aus mehreren davon) auswählen, die in Ihrem Fall am besten funktioniert.

Weitere Informationen hierzu finden Sie in meinen beiden Antworten:

81
Petr Janeček

Dies ist eine funktionierende Java-Version des Beispiels, das Sie angegeben haben: 

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

Beispiel für c #:

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

Hier ist mein Versuch einer völlig generischen Lösung in Python:

Zuerst eine generische "wait" -Funktion (verwenden Sie einen WebDriverWait, wenn Sie möchten, finde ich sie hässlich):

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

Als nächstes stützt sich die Lösung auf die Tatsache, dass Selenium eine (interne) ID-Nummer für alle Elemente auf einer Seite aufzeichnet, einschließlich des Elements <html> der obersten Ebene. Wenn eine Seite aktualisiert oder geladen wird, erhält sie ein neues HTML-Element mit einer neuen ID.

Angenommen, Sie möchten beispielsweise auf einen Link mit dem Text "Mein Link" klicken:

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)

Für mehr Pythonic, wiederverwendbare, generische Helfer können Sie einen Kontextmanager erstellen:

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)

Und dann können Sie es für so ziemlich jede Selenium-Interaktion verwenden:

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

Ich glaube, das ist kugelsicher! Was denkst du? 

Mehr Infos in einem Blogbeitrag dazu hier

10
hwjp

Ich hatte ein ähnliches Problem. Ich musste warten, bis mein Dokument fertig war, aber auch bis alle Ajax-Anrufe beendet waren. Die zweite Bedingung erwies sich als schwierig zu erkennen. Am Ende habe ich nach aktiven Ajax-Anrufen gesucht und es hat funktioniert.

Javascript:

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

Vollständige C # -Methode:

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);
}
7
Rubanov
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));
7
Ram Bharath

Für C # NUnit müssen Sie WebDriver in JSExecuter konvertieren und dann das Skript ausführen, um zu prüfen, ob der document.ready-Status vollständig ist oder nicht. Überprüfen Sie den folgenden Code als Referenz: 

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

Dies wird warten, bis die Bedingung erfüllt ist oder eine Zeitüberschreitung auftritt.

4
S.Akruwala

Beim ersten Laden der Seite ist mir aufgefallen, dass "Maximieren" des Browserfensters praktisch wartet, bis das Laden der Seite abgeschlossen ist (einschließlich Quellen).

Ersetzen:

AppDriver.Navigate().GoToUrl(url);

Mit:

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

als verwenden:

OpenURL(myDriver, myUrl);

Dadurch wird die Seite geladen, bis zum Abschluss gewartet, maximiert und darauf fokussiert. Ich weiß nicht warum es so ist, aber es funktioniert.

Wenn Sie auf das Laden der Seite warten möchten, nachdem Sie auf Weiter geklickt haben, oder auf einen anderen Seitennavigationsauslöser als "Navigate ()", erledigt Ben Dyers Antwort (in diesem Thread) die Arbeit.

3
Roei Sabag

In Nodejs können Sie es über Versprechen erhalten ...

Wenn Sie diesen Code schreiben, können Sie sicher sein, dass die Seite vollständig geladen ist, wenn Sie zu der ...

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

Wenn Sie diesen Code schreiben, navigieren Sie und Selenium wartet 3 Sekunden ...

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...

Aus der Dokumentation von Selen:

this.get (url) → Thenable

Plant einen Befehl, um zur angegebenen URL zu navigieren.

Gibt ein Versprechen zurück, das aufgelöst wird, wenn das Dokument das Laden von abgeschlossen hat.

Selendokumentation (Nodejs)

1
sidanmor

normalerweise öffnet Selenium beim Öffnen einer neuen Seite durch Klicken oder Übermitteln oder Abrufen von Methoden das Unlock, dass die Seite geladen ist. Das Problem ist jedoch, wenn die Seite einen xhr-Aufruf (ajax) hat, wird er niemals auf das Laden der xhr warten, also Das Erstellen einer neuen Methode zum Überwachen einer Xhr und das Warten auf sie wird es gut machen. (Entschuldigung für mein schlechtes Englisch: 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);
}

wenn $ .active == 0 ist, so ist dies kein aktiver xhrs-Aufruf (der nur mit jQuery funktioniert) . Für Javascript-Ajax-Aufruf müssen Sie eine Variable in Ihrem Projekt erstellen und diese simulieren. 

1
elazzam

Werfen Sie einen Blick auf Tapisserie Web-Framework. Sie können Quellcode herunterladen .

Die Idee ist, zu signalisieren, dass die Seite durch das HTML-Attribut "body" fertig ist. Sie können diese Idee verwenden, um komplizierte Klagen zu ignorieren.

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

Erstellen Sie dann eine Erweiterung des Selenium-Web-Treibers (entsprechend dem Gobelin-Rahmen)

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
    }          
}

Verwenden Sie die Erweiterung ElementIsDisplayed geschrieben von Alister Scott .

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

Und zum Schluss test erstellen:

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

Ich habe diesen Code ausprobiert und funktioniert für mich. Ich rufe diese Funktion jedes Mal auf, wenn ich zu einer anderen Seite wechsle

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; 
        }   
      }
 }
1
SOAlgorithm

Die Antwort von Ben Dryer konnte auf meinem Computer nicht kompiliert werden ("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait"). 

Java 8-Version arbeiten:

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

Java 7-Version: 

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);
1
Hazel Troost

Das Warten auf das document.ready -Ereignis ist nicht die gesamte Lösung für dieses Problem, da sich dieser Code noch in einem Race-Zustand befindet: Manchmal wird dieser Code ausgelöst, bevor das Click-Event verarbeitet wird. Daher wird dieses Ereignis direkt zurückgegeben, da der Browser nicht gestartet wurde Laden Sie die neue Seite noch. 

Nach einiger Suche fand ich einen Beitrag auf Obay the testing goat , der eine Lösung für dieses Problem bietet. Der C # -Code für diese Lösung sieht etwa so aus:

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

}

` Ich führe diese Methode direkt nach dem driver.navigate.gotourl aus, damit sie so schnell wie möglich einen Verweis auf die Seite erhält. Viel Spass damit!

1
Maarten Kieft

Sie können eine Logik schreiben, um dies zu handhaben. Ich habe eine Methode geschrieben, die WebElement zurückgibt. Diese Methode wird dreimal aufgerufen, oder Sie können die Zeit erhöhen und eine Nullprüfung für WebElement hinzufügen. Hier ist ein Beispiel

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
Vaibhav Pandey

Für die Menschen, die auf ein bestimmtes Element warten müssen. (gebrauchtes c #)

public static void WaitForElement(IWebDriver driver, By element)
{
    WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
    wait.Until(ExpectedConditions.ElementIsVisible(element));
}

Dann, wenn Sie zum Beispiel warten möchten, wenn eine class = "error-message" im DOM existiert, machen Sie einfach:

WaitForElement(driver, By.ClassName("error-message"));

Für id wird es dann sein

WaitForElement(driver, By.Id("yourid"));

0
Mo D Genesis

Der folgende Code sollte wahrscheinlich funktionieren:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));
0
vrn
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;
    }
0
akhilesh gulati

Ich habe einen Javascript-Code ausgeführt, um zu prüfen, ob das Dokument fertig ist. Mir wurde viel Zeit beim Debuggen von Selenium-Tests für Websites mit clientseitigem Rendering erspart.

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

Wenn Sie eine langsame Seite oder Netzwerkverbindung haben, ist die Wahrscheinlichkeit groß, dass keine der oben genannten Funktionen funktioniert. Ich habe sie alle ausprobiert und das Einzige, was für mich funktioniert hat, ist, auf das letzte sichtbare Element auf dieser Seite zu warten. Nehmen Sie zum Beispiel die Bing-Webseite. Sie haben ein KAMERA-Symbol (Schaltfläche "Suchen nach Bild") neben der Hauptsuchschaltfläche platziert, die nur sichtbar ist, nachdem die gesamte Seite geladen wurde. Wenn jeder das getan hat, müssen wir nur eine explizite Wartezeit verwenden, wie in den obigen Beispielen.

0
user3988148

Wie Rubanov für C # schrieb, schreibe ich es für Java und es ist:

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

In Java wird es unten gefallen: -

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