125 votes

Selenium WebDriver : Attendre le chargement d'une page complexe avec JavaScript

J'ai une application web à tester avec Selenium. Il y a beaucoup de JavaScript qui s'exécute au chargement de la page.
Ce code JavaScript n'est pas très bien écrit mais je ne peux rien changer. Donc attendre qu'un élément apparaisse dans le DOM avec findElement() n'est pas une option.
Je veux créer une fonction générique en Java pour attendre le chargement d'une page, une solution possible serait :

  • Exécuter un script JavaScript à partir de WebDriver et stocker le résultat de l'opération. document.body.innerHTML dans une variable de type chaîne body .
  • comparer les body à la version précédente de body . s'ils sont identiques, ils incrémentent un compteur. notChangedCount sinon, fixer notChangedCount à zéro.
  • attendre un petit moment (50 ms par exemple).
  • si la page n'a pas changé depuis un certain temps (500 ms par exemple), alors notChangedCount >= 10 alors quittez la boucle, sinon revenez à la première étape.

Pensez-vous que c'est une solution valable ?

0 votes

FindElement() n'attend pas - que voulez-vous dire par là ?

2 votes

findElement attend qu'un élément soit disponible, mais parfois l'élément est disponible avant que le code javascript soit complètement initialisé, c'est pourquoi ce n'est pas une option.

0 votes

Je l'ai oublié - j'ai l'habitude d'utiliser WebDriverWait et ExpectedCondition ; c'est beaucoup plus flexible.

83voto

Slanec Points 14354

Si quelqu'un connaissait une réponse générale et toujours applicable, il l'aurait mise en œuvre. partout il y a longtemps et qui rendrait nos vies tellement plus faciles.

Il y a beaucoup de choses que vous pouvez faire, mais chacune d'entre elles a un problème :

  1. Comme le dit Ashwin Prabhu, si vous connaissez bien le script, vous pouvez observer son comportement et suivre certaines de ses variables sur window o document etc. Cette solution n'est toutefois pas adaptée à tout le monde et ne peut être utilisée que par vous et uniquement sur un ensemble limité de pages.

  2. Votre solution consistant à observer le code HTML et à déterminer s'il a été modifié ou non depuis un certain temps n'est pas mauvaise (de plus, il y a aussi une méthode pour obtenir le HTML original et non édité directement en WebDriver ), mais :

    • L'affirmation d'une page prend beaucoup de temps et pourrait prolonger le test de manière significative.
    • On ne sait jamais quel est le bon intervalle. Le script pourrait télécharger quelque chose de gros qui prend plus de 500 ms. Il y a plusieurs scripts sur la page interne de notre entreprise qui prennent plusieurs secondes dans IE. Votre ordinateur peut être temporairement à court de ressources - disons qu'un antivirus fait fonctionner votre CPU à plein régime, alors 500 ms peuvent être trop courts même pour un script non complexe.
    • Certains scripts ne sont jamais terminés. Ils s'appellent eux-mêmes avec un certain retard ( setTimeout() ) et fonctionnent encore et encore et pourraient éventuellement modifier le HTML à chaque fois qu'ils sont exécutés. Sérieusement, toutes les pages "Web 2.0" le font. Même Stack Overflow. Vous pourriez écraser les méthodes les plus couramment utilisées et considérer les scripts qui les utilisent comme achevés, mais ... vous ne pouvez pas en être sûr.
    • Et si le script fait autre chose que de modifier le HTML ? Il pourrait faire des milliers de choses, pas seulement quelques innerHTML amusant.
  3. Il existe des outils pour vous aider dans ce domaine. Notamment Les auditeurs du progrès en même temps que nsIWebProgressListener et quelques autres. Le support des navigateurs pour cela, cependant, est horrible. Firefox a commencé à essayer de le prendre en charge à partir de FF4 (toujours en évolution), IE a un support de base dans IE9.

Et je suppose que je pourrais bientôt trouver une autre solution imparfaite. Le fait est qu'il n'y a pas de réponse définitive sur le moment où il faut dire "maintenant la page est complète", à cause des scripts perpétuels qui font leur travail. Choisissez celui qui vous convient le mieux, mais faites attention à ses défauts.

35voto

Eduardo Fabricio Points 306

Merci Ashwin !

Dans mon cas, je dois attendre l'exécution d'un plugin jquery dans un élément spécifiquement "qtip".

basé sur votre conseil, cela a parfaitement fonctionné pour moi :

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

Note : J'utilise Webdriver 2.

2 votes

Cela fonctionne très bien pour moi aussi. Dans mon cas, un élément a été modifié par Javascript juste après la fin du chargement de la page, et Selenium ne l'attendait pas implicitement.

2 votes

D'où vient Predicate ? Quelle importation ?

0 votes

@AmadoSaladino "import com.google.common.base.Predicate ;" from google lib guava-15.0 link : mvnrepository.com/artifact/com.google.guava/guava/15.0 il existe une nouvelle version 27.0 que vous pouvez essayer !

9voto

Ashwin Prabhu Points 4833

La bibliothèque JS définit-elle/initialise-t-elle une variable connue dans la fenêtre ?

Si c'est le cas, vous pouvez attendre que la variable apparaisse. Vous pouvez utiliser

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

pour tester cette condition (quelque chose comme : window.SomeClass && window.SomeClass.variable != null ) et renvoie un booléen true / false .

Enveloppez cela dans un WebDriverWait et attendez le retour du script. true .

1voto

djangofan Points 6046

Pour le faire correctement, vous devez gérer les exceptions.

Voici comment je fais une attente pour un iFrame. Cela nécessite que votre classe de test JUnit passe l'instance de RemoteWebDriver dans l'objet page :

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

NOTE : Vous pouvez voir mon exemple complet de travail ici .

0voto

Vaibhav Pandey Points 9

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 de nullité 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;
        }
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X