160 votes

WebDriver click() vs JavaScript click()

L'histoire :

Ici, sur StackOverflow, j'ai vu des utilisateurs signaler qu'ils ne peuvent pas cliquer sur un élément via la commande "click" de Selenium WebDriver et qu'ils peuvent le contourner avec un clic JavaScript en exécutant un script.

Exemple en Python :

element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)

Exemple dans WebDriverJS/Protractor :

var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());

La question :

Pourquoi un clic "via JavaScript" fonctionne-t-il alors qu'un clic WebDriver normal ne fonctionne pas ? Quand cela se produit-il exactement et quel est l'inconvénient de cette solution de contournement (le cas échéant) ?

J'ai personnellement utilisé cette solution de contournement sans bien comprendre pourquoi je dois le faire et quels problèmes cela peut entraîner.

198voto

Louis Points 13534

Contrairement à ce que le réponse actuellement acceptée suggère, il n'y a rien de spécifique à PhantomJS quand il s'agit de la différence entre faire faire un clic par WebDriver et le faire en JavaScript.

La différence

La différence essentielle entre les deux méthodes est commune à tous les navigateurs et peut être expliquée assez simplement :

  • WebDriver : Lorsque WebDriver effectue le clic, il tente du mieux qu'il peut de simuler ce qui se passe lorsqu'un utilisateur réel utilise le navigateur. Supposons que vous ayez un élément A qui est un bouton qui dit "Cliquez sur moi" et un élément B qui est un bouton qui dit "Cliquez sur moi". div élément qui est transparent mais qui a ses dimensions et zIndex de sorte qu'il couvre complètement A. Ensuite, vous demandez à WebDriver de cliquer sur A. WebDriver simulera le clic de sorte que B reçoive le clic. premièrement . Pourquoi ? Parce que B couvre A, et que si un utilisateur essayait de cliquer sur A, B serait le premier à recevoir l'événement. Le fait que A obtienne ou non l'événement de clic dépend de la manière dont B traite l'événement. En tout cas, le comportement de WebDriver dans ce cas est le même que lorsqu'un utilisateur réel essaie de cliquer sur A.

  • JavaScript : Maintenant, supposons que vous utilisez JavaScript pour faire A.click() . Cette méthode de clic ne reproduit pas ce qui se passe réellement lorsque l'utilisateur essaie de cliquer sur A. JavaScript envoie le click directement à A, et B ne recevra pas d'événement.

Pourquoi un clic JavaScript fonctionne-t-il alors qu'un clic WebDriver ne fonctionne pas ?

Comme je l'ai mentionné ci-dessus, WebDriver essaiera de simuler au mieux ce qui se passe lorsqu'un utilisateur réel utilise un navigateur. Le fait est que le DOM peut contenir des éléments avec lesquels un utilisateur ne peut pas interagir, et WebDriver ne vous permettra pas de cliquer sur ces éléments. Outre le cas de chevauchement que j'ai mentionné, cela implique également que les éléments invisibles ne peuvent pas être cliqués. Un cas courant que je vois dans les questions Stack Overflow est celui d'une personne qui essaie d'interagir avec un élément GUI qui existe déjà dans le DOM mais qui ne devient visible que lorsqu'un autre élément a été manipulé. Cela se produit parfois avec les menus déroulants : il faut d'abord cliquer sur le bouton qui fait apparaître le menu déroulant avant de pouvoir sélectionner un élément du menu. Si quelqu'un essaie de cliquer sur l'élément de menu avant que le menu ne soit visible, WebDriver se rebiffera et dira que l'élément ne peut pas être manipulé. Si la personne essaie ensuite de le faire avec JavaScript, cela fonctionnera car l'événement est transmis directement à l'élément, indépendamment de la visibilité.

Quand faut-il utiliser le JavaScript pour les clics ?

Si vous utilisez Selenium pour tester une application ma réponse à cette question est "presque jamais". Dans l'ensemble, votre test Selenium doit reproduire ce qu'un utilisateur ferait avec le navigateur. Prenons l'exemple du menu déroulant : un test devrait d'abord cliquer sur le bouton qui fait apparaître le menu déroulant, puis cliquer sur l'élément de menu. S'il y a un problème avec l'interface graphique parce que le bouton est invisible, ou que le bouton n'affiche pas les éléments du menu, ou quelque chose de similaire, alors votre test échouera et vous aurez détecté le bug. Si vous utilisez JavaScript pour cliquer, vous ne pourrez pas détecter ces bogues par des tests automatisés.

Je dis "presque jamais" parce qu'il peut y avoir des exceptions où il est judicieux d'utiliser JavaScript. Mais elles doivent être très rares.

Si vous utilisez Selenium pour sites de grattage Dans ce cas, il n'est pas aussi important de tenter de reproduire le comportement de l'utilisateur. L'utilisation de JavaScript pour contourner l'interface graphique est donc moins problématique.

1 votes

Bien meilleure réponse, celle-ci devrait être acceptée. La réponse ci-dessus parle d'un cas limite spécifique à PhantomJS, celle-ci est beaucoup plus précise à mon avis.

0 votes

@Ardesco définitivement. Marqué comme accepté. Une réponse parfaite et assez détaillée.

0 votes

J'attribue la prime à Linh comme prévu, mais je vais en créer une nouvelle pour vous remercier d'une autre réponse géniale. Merci.

36voto

Florent B. Points 8564

Le clic exécuté par le pilote tente de simuler le plus fidèlement possible le comportement d'un utilisateur réel tandis que le JavaScript HTMLElement.click() exécute l'action par défaut pour le click même si l'élément n'est pas interactif.

Les différences sont les suivantes :

  • Le pilote s'assure que l'élément est visible en le faisant défiler dans la vue et vérifie que l'élément est interactif .

    Le pilote lève une erreur :

    • lorsque l'élément situé au-dessus aux coordonnées du clic n'est pas l'élément ciblé ou un descendant.
    • lorsque l'élément n'a pas une taille positive ou s'il est totalement transparent.
    • lorsque l'élément est une entrée ou un bouton désactivé (attribut/property disabled es true )
    • lorsque l'élément a le pointeur de la souris désactivé (CSS pointer-events es none )

    A JavaScript HTMLElement.click() exécutera toujours l'action par défaut ou, au mieux, échouera en silence si l'élément est désactivé.

  • On attend du conducteur qu'il mettre l'élément au point si elle est focalisable.

    A JavaScript HTMLElement.click() ne le fera pas.

  • On attend du conducteur qu'il émettre tous les événements (mousemove, mousedown, mouseup, click, ...) tout comme un véritable utilisateur.

    A JavaScript HTMLElement.click() n'émet que le click événement. La page peut dépendre de ces événements supplémentaires et se comporter différemment s'ils ne sont pas émis.

    Ce sont les événements émis par le pilote pour un clic avec Chrome :

    mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }

    Et voici l'événement émis par une injection JavaScript :

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • L'événement émis par un JavaScript .click() n'a pas confiance et l'action par défaut peut ne pas être invoquée :

    https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
    https://googlechrome.github.io/samples/event-istrusted/index.html

    Notez que certains des pilotes génèrent encore des événements non fiables. C'est le cas de PhantomJS à partir de la version 2.1.

  • L'événement émis par un JavaScript .click() n'a pas les coordonnées du clic .

    Les propriétés clientX, clientY, screenX, screenY, layerX, layerY sont fixés à 0 . La page pourrait s'appuyer sur eux et se comporter différemment.

Il est possible d'utiliser un JavaScript .click() pour récupérer certaines données, mais ce n'est pas dans un contexte de test. Cela va à l'encontre de l'objectif du test puisqu'il ne simule pas le comportement d'un utilisateur. Ainsi, si le clic du pilote échoue, il est fort probable qu'un utilisateur réel échouera également à effectuer le même clic dans les mêmes conditions.

Qu'est-ce qui fait que le conducteur ne parvient pas à cliquer sur un élément alors que nous nous attendons à ce qu'il y parvienne ?

  • L'élément ciblé n'est pas encore visible/interactable en raison d'un retard ou d'un effet de transition.

    Quelques exemples :

    https://developer.mozilla.org/fr/docs/Web (menu déroulant de navigation) http://materializecss.com/side-nav.html (barre latérale déroulante)

    Solutions de contournement :

    Ajoutez un serveur pour attendre la visibilité, une taille minimale ou une position stable :

    // wait visible
    browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
    
    // wait visible and not disabled
    browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
    
    // wait for minimum width
    browser.wait(function minimumWidth() {
        return elem.getSize().then(size => size.width > 50);
    }, 5000);

    Réessayer de cliquer jusqu'à ce qu'il réussisse :

    browser.wait(function clickSuccessful() {
        return elem.click().then(() => true, (ex) => false);
    }, 5000);

    Ajouter un délai correspondant à la durée de l'animation/transition :

    browser.sleep(250);
  • L'élément ciblé finit par être couvert par un élément flottant une fois qu'on l'a fait défiler dans la vue :

    Le pilote fait automatiquement défiler l'élément dans la vue pour le rendre visible. Si la page contient un élément flottant ou collant (menu, publicités, pied de page, notification, politique en matière de cookies ), l'élément peut finir par être recouvert et ne sera plus visible/interactif.

    Exemple : https://twitter.com/?lang=en

    Solutions de contournement :

    Définissez la taille de la fenêtre à une taille plus grande pour éviter le défilement ou l'élément flottant.

    Déplacement sur l'élément avec un négatif Y puis cliquez dessus :

      browser.actions()
         .mouseMove(elem, {x: 0, y: -250})
         .click()
         .perform();

    Faites défiler l'élément au centre de la fenêtre avant le clic :

    browser.executeScript(function scrollCenter(elem) {
      var win = elem.ownerDocument.defaultView || window,
        box = elem.getBoundingClientRect(),
        dy = box.top - (win.innerHeight - box.height) / 2;
      win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
    }, element);
    
    element.click();

    Cachez l'élément flottant si vous ne pouvez pas l'éviter :

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);

19voto

Linh Pham Points 1195

NOTE : appelons "clic" le clic de l'utilisateur final. js click' est un clic via JS.

Pourquoi le fait de cliquer "via JavaScript" fonctionne-t-il alors qu'un clic WebDriver normal ne fonctionne pas ?

Il y a deux cas pour que cela se produise :

I. Si vous utilisez PhamtomJS

Alors c'est le comportement le plus communément connu de PhantomJS . Certains éléments ne sont parfois pas cliquables, par exemple <div> . Cela s'explique par le fait que PhantomJS a été conçu à l'origine pour simuler le moteur des navigateurs (comme le HTML initial + CSS -> calcul CSS -> rendu). Mais il n'est pas destiné à être utilisé par l'utilisateur final (visualisation, clic, glissement). C'est pourquoi PhamtomJS n'est que partiellement prise en charge par l'interaction des utilisateurs finaux.

POURQUOI LE CLIC JS FONCTIONNE-T-IL ? Quant aux deux clics, ils sont tous des clics moyens. C'est comme un pistolet avec 1 baril y 2 déclencheurs . Un de la fenêtre d'observation, un de JS. Depuis PhamtomJS très bien pour simuler le moteur du navigateur, un clic JS devrait fonctionner parfaitement.

II. Le gestionnaire d'événement de "click" s'est lié à la mauvaise période.

Par exemple, nous avons obtenu un <div>

  • -> Nous faisons des calculs

  • -> puis nous lions l'événement du clic à l'objet <div> .

  • -> Plus avec un mauvais codage d'angular (par exemple, ne pas gérer correctement le cycle de la portée).

Nous risquons de nous retrouver avec le même résultat. Le clic ne fonctionnera pas, car WebdriverJS essaie de cliquer sur l'élément alors qu'il n'a pas de gestionnaire d'événement de clic.

POURQUOI LE CLIC JS FONCTIONNE-T-IL ? Js click revient à injecter du js directement dans le navigateur. C'est possible de 2 façons,

Poing est par la console de devtools (oui, WebdriverJS communique avec la console de devtools).

Deuxièmement est d'injecter un <script> directement dans le code HTML.

Pour chaque navigateur, le comportement sera différent. Mais quoi qu'il en soit, ces méthodes sont plus compliquées que de cliquer sur le bouton. Le clic utilise ce qui est déjà là (clic de l'utilisateur final), le clic du js passe par une porte dérobée.

Et pour js click apparaîtra comme une tâche asynchrone. Ceci est lié à un sujet assez complexe de ' planification des tâches asynchrones du navigateur et des tâches du CPU (je l'ai lu il y a quelque temps, je ne retrouve plus l'article). En bref, cela se traduira surtout par le fait que les clics de js devront attendre un cycle de planification des tâches de l'unité centrale et il sera exécuté un peu plus lentement après la liaison de l'événement de clic. (Vous pouvez connaître ce cas lorsque vous constatez que l'élément est parfois cliquable, parfois non. )

Quand exactement cela se produit-il et quel est l'inconvénient de cela ? de cette solution de contournement (le cas échéant) ?

\=> Comme mentionné ci-dessus, les deux signifient pour un but, mais à propos de l'utilisation de quelle entrée :

  • Click : utilise ce qui est fourni par défaut par le navigateur.
  • JS click : passe par la porte dérobée.

\=> Pour les performances, il est difficile de se prononcer car elles dépendent des navigateurs. Mais en général :

  • Clic : ne signifie pas plus rapide mais seulement une position plus élevée dans la liste des tâches d'exécution du CPU.
  • JS click : ne veut pas dire plus lent mais seulement qu'il a été placé en dernière position de la liste des tâches du CPU.

\=> Inconvénients :

  • Click : ne semble pas avoir d'inconvénient, sauf si vous utilisez PhamtomJS.
  • JS click : très mauvais pour la santé. Vous pouvez accidentellement cliquer sur un élément qui n'est pas présent dans la vue. Lorsque vous l'utilisez, assurez-vous que l'élément est présent et disponible à la vue et au clic comme le point de vue de l'utilisateur final.

P.S. si vous cherchez une solution.

  • Utiliser PhantomJS ? Je vous suggère d'utiliser plutôt Chrome headless. Oui, vous pouvez configurer Chrome headless sur Ubuntu. La chose fonctionne comme Chrome mais n'a pas de vue et est moins boguée que PhantomJS.
  • Vous n'utilisez pas PhamtomJS mais rencontrez toujours des problèmes ? Je vous suggère d'utiliser ExpectedCondition de Protractor avec browser.wait() ( Pour plus d'informations, consultez cette page )

(Je voulais faire court, mais j'ai mal fini. Tout ce qui est lié à la théorie est compliqué à expliquer...)

5voto

Ashish Tomar Points 131

Merci pour cette bonne explication, je rencontrais le même problème et votre explication m'a aidé à résoudre mon problème.

button = driver.wait.until(EC.presence_of_element_located(
    (By.XPATH, "//div[@id='pagination-element']/nav[1]/span[3]/button[1]/span[1]/i[1]")
))
driver.execute_script("arguments[0].click();", button)

1voto

Chuenkei Sit Points 11

enter image description here

if (theElement.Enabled)
{
    if (!theElement.Selected)
    {
      var driver = (IJavaScriptExecutor)Driver;
      driver.ExecuteScript("arguments[0].click();", theElement); //ok

      //theElement.Click();//action performed on theElement, then pops exception   
     }
 }

Je ne suis pas d'accord avec le fait que nous n'utiliserons "presque jamais" JS pour simuler l'action du clic.

Au-dessus de theElement.Click() Nous vérifions le bouton Radio mais l'exception apparaît comme l'image ci-dessus.

En fait, il n'y a pas d'action de chargement de page après le clic, le clic est juste pour sélectionner le bouton radio, et je ne sais pas pourquoi le WebDriver Click() provoquera cette exception, quelqu'un peut-il expliquer pourquoi cette exception s'est produite.

J'utilise le Webdriver 3.141.59 et IE 11 et selenium-server-standalone-3.141.59.jar pour un test à distance.

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