web-dev-qa-db-fra.com

Sélénium attend que le document soit prêt

Quelqu'un peut-il me laisser comment puis-je faire en sorte que Selenium attende le chargement complet de la page? Je veux quelque chose de générique, je sais que je peux configurer WebDriverWait et appeler quelque chose comme 'trouver' pour le faire attendre, mais je ne vais pas si loin. Je dois juste vérifier que la page se charge avec succès et passer à la page suivante.

J'ai trouvé quelque chose dans .net mais je ne pouvais pas le faire fonctionner en 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"));

Des pensées quelqu'un?

115
Girish

Essayez ce code:

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

Le code ci-dessus attend jusqu'à 10 secondes pour le chargement de la page. Si le chargement de la page dépasse le temps écoulé, la variable TimeoutException sera lancée. Vous attrapez l'exception et faites vos besoins. Je ne suis pas sûr s'il quitte le chargement de la page après la levée de l'exception. Je n'ai pas encore essayé ce code. Je veux juste l'essayer.

C'est une attente implicite. Si vous définissez cette option une fois, elle aura la portée jusqu'à la destruction de l'instance du pilote Web.

Pour plus info.

74
Manigandan

Votre solution suggérée n’attend que DOM readyState to signal complete. Mais Selenium essaie par défaut d’attendre le chargement de ceux-ci (et un peu plus) via les méthodes driver.get() et element.click() . Ils bloquent déjà, ils attendent que la page soit complètement chargée et ceux-ci devraient fonctionner correctement.

Le problème, évidemment, ce sont les redirections via des requêtes AJAX et des scripts en cours d'exécution - ceux-ci ne peuvent pas être capturés par Selenium, il n'attend pas qu'ils se terminent. De plus, vous ne pouvez pas les attraper de manière fiable via readyState - il attend un peu, ce qui peut être utile, mais il signalera complete longtemps avant que tout le contenu AJAX soit téléchargé.

Il n'y a pas de solution générale qui fonctionnerait partout et pour tout le monde, c'est pourquoi c'est difficile et tout le monde utilise quelque chose d'un peu différent.

La règle générale consiste à faire confiance à WebDriver, puis à utiliser des attentes implicites, puis à utiliser des attentes explicites pour les éléments que vous souhaitez affirmer sur la page, mais il existe beaucoup plus de techniques pouvant être utilisées. Vous devez choisir celui (ou une combinaison de plusieurs d'entre eux) qui fonctionne le mieux dans votre cas, sur votre page testée.

Voir mes deux réponses à ce sujet pour plus d'informations:

81
Petr Janeček

Voici une version Java de l'exemple que vous avez donné: 

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

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

Voici ma tentative de solution complètement générique, en Python:

Tout d’abord, une fonction générique "wait" (utilisez un WebDriverWait si vous voulez, je les trouve laids):

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

Ensuite, la solution repose sur le fait que Selenium enregistre un numéro d'identification (interne) pour tous les éléments d'une page, y compris l'élément <html> de niveau supérieur. Lorsqu'une page est actualisée ou chargée, elle obtient un nouvel élément html avec un nouvel ID.

Donc, en supposant que vous vouliez cliquer sur un lien avec le texte "mon lien", par exemple:

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)

Pour plus d’assistants génériques réutilisables Pythonic, vous pouvez créer un gestionnaire de contexte:

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)

Et puis, vous pouvez l’utiliser sur à peu près toutes les interactions de sélénium:

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

Je pense que c'est pare-balles! Qu'est-ce que tu penses? 

Plus d'infos dans un blog post à ce sujet ici

10
hwjp

J'avais un problème similaire. Je devais attendre que mon document soit prêt, mais aussi jusqu'à ce que tous les appels Ajax soient terminés. La deuxième condition s'est avérée difficile à détecter. Finalement, j'ai vérifié les appels actifs à l'Ajax et cela a fonctionné.

Javascript:

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

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

Pour C # NUnit, vous devez convertir WebDriver en JSExecuter, puis exécuter le script pour vérifier si l'état de document.ready est complet ou non. Vérifiez ci-dessous le code pour référence: 

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

Cela attendra que la condition soit satisfaite ou que le délai d'attente soit dépassé.

4
S.Akruwala

Pour le chargement initial de la page, j'ai remarqué que "maximiser" la fenêtre du navigateur attend pratiquement jusqu'à ce que le chargement de la page est terminé (y compris les sources)

Remplacer:

AppDriver.Navigate().GoToUrl(url);

Avec:

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

que d'utiliser:

OpenURL(myDriver, myUrl);

Cela va charger la page, attendre la fin, maximiser et se concentrer dessus. Je ne sais pas pourquoi c'est comme ça mais ça marche.

Si vous souhaitez attendre le chargement de la page après avoir cliqué sur Suivant ou sur tout autre déclencheur de navigation entre autres que "Navigate ()", la réponse de Ben Dyer (dans ce fil) fera le travail.

3
Roei Sabag

Dans Nodejs, vous pouvez l'obtenir via des promesses ...

Si vous écrivez ce code, vous pouvez être sûr que la page est entièrement chargée lorsque vous arrivez à la ...

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

Si vous écrivez ce code, vous allez naviguer et Selenium attendra 3 secondes ...

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

De la documentation Selenium:

this.get (url) → Thenable

Planifie une commande pour accéder à l'URL donnée.

Renvoie une promesse qui sera résolue lorsque le document aura fini de charger.

Documentation Selenium (Nodejs)

1
sidanmor

normalement, lorsque Selenium ouvrira une nouvelle page à partir d’un clic ou soumettra ou obtiendra des méthodes, il attendra que la page ne soit pas chargée, mais le problème est que lorsque la page a un appel xhr (ajax), il n’attendra jamais que la xhr soit chargée. créer une nouvelle méthode pour surveiller un xhr et attendre qu’il soit bon. (désolé pour mon mauvais anglais: 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);
}

si $ .active == 0, l'appel n'est donc pas actif (cela ne fonctionne qu'avec jQuery) . Pour un appel javascript ajax, vous devez créer une variable dans votre projet et la simuler. 

1
elazzam

Regardez tapestry web-framework. Vous pouvez télécharger le code source ici.

L'idée est de signaler que cette page est prête par l'attribut HTML du corps. Vous pouvez utiliser cette idée pour ignorer les poursuites judiciaires compliquées.

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

Et puis créer une extension de sélénium webdriver (selon le cadre de tapisserie)

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

Utilisez l'extension ElementIsDisplayed écrite par Alister Scott .

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

Et enfin créer le test:

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

J'ai essayé ce code et cela fonctionne pour moi. J'appelle cette fonction chaque fois que je passe à une autre page

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

La réponse de Ben Dryer n'a pas été compilée sur ma machine ("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait"). 

Version de travail de Java 8:

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

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

L'attente de l'événement document.ready ne constitue pas la solution intégrale à ce problème, car ce code est toujours en état de concurrence critique: parfois, ce code est déclenché avant que l'événement click ne soit traité. Il est donc renvoyé directement car le navigateur n'a pas démarré chargement de la nouvelle page pour le moment. 

Après quelques recherches, j'ai trouvé un article sur Obay, le test de chèvre , qui propose une solution à ce problème. Le code c # de cette solution ressemble à ceci:

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

}

` Je déclenche cette méthode directement après le fichier driver.navigate.gotourl, afin qu’il obtienne une référence de la page le plus rapidement possible. Aie du plaisir avec ça!

1
Maarten Kieft

Vous pouvez écrire une logique pour gérer cela. J'ai écrit une méthode qui retournera le WebElement et cette méthode sera appelée trois fois ou vous pouvez augmenter le temps et ajouter une vérification nulle pour WebElement Voici un exemple

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

Le code suivant devrait probablement fonctionner:

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

J'ai exécuté un code javascript pour vérifier si le document est prêt. M'a permis de gagner beaucoup de temps à déboguer les tests Selenium pour les sites dotés d'un rendu client.

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

Si votre page ou votre connexion réseau est lente, il est probable que rien de ce qui précède ne fonctionnera. Je les ai tous essayés et la seule chose qui a fonctionné pour moi est d'attendre le dernier élément visible sur cette page. Prenons par exemple la page Web Bing. Ils ont placé une icône APPAREIL PHOTO (bouton de recherche par image) à côté du bouton de recherche principal qui est visible uniquement après le chargement de la page complète. Si tout le monde a fait cela, alors tout ce que nous avons à faire est d’utiliser une attente explicite comme dans les exemples ci-dessus.

0
user3988148

Comme Rubanov a écrit pour C #, je l’écris pour Java, et c’est:

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

En Java, ce sera comme ci-dessous: -

  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