WebDriver click () vs JavaScript click ()

126

Die Geschichte:

Hier bei StackOverflow haben Benutzer berichtet, dass sie nicht über den Selenium WebDriver-Befehl "click" auf ein Element klicken und es mit einem JavaScript-Klick umgehen können, indem sie ein Skript ausführen.

Beispiel in Python:

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

Beispiel in WebDriverJS / Protractor:

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

Die Frage:

Warum funktioniert das Klicken auf "über JavaScript", wenn ein normaler WebDriver-Klick dies nicht tut? Wann genau passiert dies und was ist der Nachteil dieser Problemumgehung (falls vorhanden)?

Ich persönlich habe diese Problemumgehung verwendet, ohne vollständig zu verstehen, warum ich sie ausführen muss und zu welchen Problemen sie führen kann.

Alecxe
quelle

Antworten:

150

Im Gegensatz zu der derzeit akzeptierten Antwort gibt es für PhantomJS nichts Spezielles, wenn es um den Unterschied zwischen dem Klicken von WebDriver und dem Ausführen in JavaScript geht.

Der Unterschied

Der wesentliche Unterschied zwischen den beiden Methoden ist allen Browsern gemeinsam und kann ziemlich einfach erklärt werden:

  • WebDriver: Wenn WebDriver den Klick ausführt, versucht es so gut wie möglich zu simulieren, was passiert, wenn ein realer Benutzer den Browser verwendet. Angenommen, Sie haben ein Element A, bei dem es sich um eine Schaltfläche mit der Aufschrift "Click me" handelt, und ein Element B, bei dem es sich um ein divtransparentes Element handelt, dessen Abmessungen jedoch zIndexso festgelegt sind, dass es A vollständig abdeckt. Anschließend weisen Sie WebDriver an, auf A zu klicken. WebDriver wird darauf klicken simuliert den Klick , so dass B den Klick erhält zuerst . Warum? Da B A abdeckt und ein Benutzer versuchen würde, auf A zu klicken, würde B zuerst das Ereignis erhalten. Ob A schließlich das Klickereignis erhalten würde oder nicht, hängt davon ab, wie B das Ereignis behandelt. In jedem Fall ist das Verhalten mit WebDriver in diesem Fall das gleiche wie wenn ein realer Benutzer versucht, auf A zu klicken.

  • JavaScript: Nehmen wir nun an, Sie verwenden JavaScript A.click(). Diese Klickmethode reproduziert nicht, was wirklich passiert, wenn der Benutzer versucht, auf A zu klicken. JavaScript sendet das clickEreignis direkt an A, und B erhält kein Ereignis.

Warum funktioniert ein JavaScript-Klick, wenn ein WebDriver-Klick nicht funktioniert?

Wie oben erwähnt, versucht WebDriver so gut wie möglich zu simulieren, was passiert, wenn ein echter Benutzer einen Browser verwendet. Tatsache ist, dass das DOM Elemente enthalten kann, mit denen ein Benutzer nicht interagieren kann, und WebDriver Ihnen nicht erlaubt, auf diese Elemente zu klicken. Neben dem erwähnten überlappenden Fall bedeutet dies auch, dass unsichtbare Elemente nicht angeklickt werden können. Ein häufiger Fall, den ich bei Fragen zum Stapelüberlauf sehe, ist jemand, der versucht, mit einem GUI-Element zu interagieren, das bereits im DOM vorhanden ist, aber erst sichtbar wird, wenn ein anderes Element manipuliert wurde. Dies passiert manchmal bei Dropdown-Menüs: Sie müssen zuerst auf die Schaltfläche klicken, um das Dropdown-Menü aufzurufen, bevor ein Menüelement ausgewählt werden kann. Wenn jemand versucht, auf den Menüpunkt zu klicken, bevor das Menü sichtbar ist,Wenn die Person dann versucht, dies mit JavaScript zu tun, funktioniert dies, da das Ereignis unabhängig von der Sichtbarkeit direkt an das Element übermittelt wird.

Wann sollten Sie JavaScript zum Klicken verwenden?

Wenn Sie Selen zum Testen einer Anwendung verwenden , lautet meine Antwort auf diese Frage "fast nie". Im Großen und Ganzen sollte Ihr Selenium-Test reproduzieren, was ein Benutzer mit dem Browser tun würde. Nehmen Sie das Beispiel des Dropdown-Menüs: Ein Test sollte zuerst auf die Schaltfläche klicken, die das Dropdown-Menü aufruft, und dann auf den Menüpunkt klicken. Wenn es ein Problem mit der GUI gibt, weil die Schaltfläche unsichtbar ist oder die Menüelemente oder ähnliches nicht angezeigt werden, schlägt Ihr Test fehl und Sie haben den Fehler erkannt. Wenn Sie mit JavaScript herumklicken, können Sie diese Fehler nicht durch automatisierte Tests erkennen.

Ich sage "fast nie", weil es Ausnahmen geben kann, in denen es sinnvoll ist, JavaScript zu verwenden. Sie sollten jedoch sehr selten sein.

Wenn Sie Selenium zum Scraping von Websites verwenden , ist es nicht so wichtig, zu versuchen, das Benutzerverhalten zu reproduzieren. Die Verwendung von JavaScript zur Umgehung der GUI ist daher weniger problematisch.

Louis
quelle
1
Viel bessere Antwort, dies sollte die akzeptierte sein. Die obige Antwort spricht von einem für PhantomJS spezifischen Randfall, der meiner Meinung nach viel genauer ist.
Ardesco
@Ardesco auf jeden Fall. Als akzeptiert markiert. Perfekt und eine recht detaillierte Antwort.
Alecxe
Ich gebe Linh das Kopfgeld wie geplant, aber ich werde ein neues beginnen, um Ihnen für eine weitere großartige Antwort zu danken. Vielen Dank.
Alecxe
1
Sehr gute und verständliche Antwort. Nach meiner Erfahrung hatte WebDriver viele Probleme mit AngularJS-Seiten: Mit einigen Elementen mögen clickoder sendKeysfunktionierten WebDriver-Methoden - aber nicht immer. Es gab kein Lokalisierungsproblem oder eine andere Ausnahme, der Testfall ging einfach nicht weiter. Die Protokollierung zeigte, dass die Aktion ausgeführt wurde. Manchmal hat die Verwendung Actiongeholfen. Wenn ich manuell auf das Element geklickt habe (nachdem der Testfall gestoppt wurde), hat alles gut funktioniert. Also haben wir bei diesen Gelegenheiten zu Raw JS gewechselt. Ich habe in den letzten 2 Jahren nicht mehr mit AngularJS gearbeitet, daher könnte es jetzt besser werden.
Würgspaß
1
@ Alex Ich habe keinen praktischen Link. Meine Antwort basiert auf Erfahrungen mit Selen im Besonderen und mit der Simulation von Benutzerereignissen im Allgemeinen. Ich habe vor 5 Jahren angefangen, Selen zu verwenden. Während der Zeit, in der ich Selenium verwendet habe, musste ich den Code von Selenium lesen, um einige Probleme zu beheben, und ich habe einige Fehlerberichte über das Versenden von Ereignissen eingereicht und die Fehler mit den Selenium-Entwicklern besprochen. "so gut es geht" ist das Ziel. Ich bin auf jeden Fall auf (jetzt behobene) Fehler gestoßen, die es daran gehindert haben, dieses Ziel zu erreichen. Einige Fehler können bestehen bleiben.
Louis
30

Der vom Treiber ausgeführte Klick versucht, das Verhalten eines realen Benutzers so nah wie möglich zu simulieren, während das JavaScript HTMLElement.click()die Standardaktion für das clickEreignis ausführt , auch wenn das Element nicht interaktiv ist.

Die Unterschiede sind:

  • Der Treiber stellt sicher, dass das Element sichtbar ist, indem er es in die Ansicht scrollt und überprüft, ob das Element interaktiv ist .

    Der Fahrer wird einen Fehler auslösen:

    • wenn das Element oben an den Koordinaten des Klicks nicht das Zielelement oder ein Nachkomme ist
    • wenn das Element keine positive Größe hat oder wenn es vollständig transparent ist
    • wenn das Element eine deaktivierte Eingabe oder Schaltfläche ist (Attribut / Eigenschaft disabledist true)
    • wenn für das Element der Mauszeiger deaktiviert ist (CSS pointer-eventsist none)


    Ein JavaScript HTMLElement.click()führt immer die Standardaktion aus oder schlägt bestenfalls stillschweigend fehl, wenn das Element deaktiviert ist.

  • Vom Fahrer wird erwartet, dass er das Element scharfstellt, wenn es fokussierbar ist.

    Ein JavaScript HTMLElement.click()wird nicht.

  • Es wird erwartet, dass der Treiber alle Ereignisse (Mausbewegung, Mausbewegung, Mouseup, Klicken, ...) wie ein echter Benutzer ausgibt.

    Ein JavaScript HTMLElement.click()gibt nur das clickEreignis aus. Die Seite basiert möglicherweise auf diesen zusätzlichen Ereignissen und verhält sich möglicherweise anders, wenn sie nicht ausgegeben werden.

    Dies sind die Ereignisse, die der Treiber für einen Klick mit Chrome ausgibt:

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

    Und dies ist das Ereignis, das mit einer JavaScript-Injektion ausgegeben wird:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • Das von einem JavaScript .click() ausgegebene Ereignis ist nicht vertrauenswürdig und die Standardaktion kann möglicherweise nicht aufgerufen werden:

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

    Beachten Sie, dass einige Treiber immer noch nicht vertrauenswürdige Ereignisse generieren. Dies ist bei PhantomJS ab Version 2.1 der Fall.

  • Das von einem JavaScript .click() ausgegebene Ereignis hat nicht die Koordinaten des Klicks .

    Die Eigenschaften clientX, clientY, screenX, screenY, layerX, layerYsind auf gesetzt 0. Die Seite kann sich auf sie verlassen und sich möglicherweise anders verhalten.


Es mag in Ordnung sein, ein JavaScript .click()zu verwenden, um einige Daten zu verschrotten, aber es befindet sich nicht in einem Testkontext. Es macht den Zweck des Tests zunichte, da es das Verhalten eines Benutzers nicht simuliert. Wenn also der Klick des Treibers fehlschlägt, kann ein echter Benutzer höchstwahrscheinlich auch nicht denselben Klick unter denselben Bedingungen ausführen.


Was führt dazu, dass der Treiber nicht auf ein Element klickt, wenn wir erwarten, dass es erfolgreich ist?

  • Das Zielelement ist aufgrund einer Verzögerung oder eines Übergangseffekts noch nicht sichtbar / interaktiv.

    Einige Beispiele :

    https://developer.mozilla.org/fr/docs/Web (Dropdown-Navigationsmenü) http://materializecss.com/side-nav.html (Dropdown-Seitenleiste)

    Workarrounds:

    Fügen Sie einen Kellner hinzu, um auf die Sichtbarkeit, eine Mindestgröße oder eine stabile Position zu warten:

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

    Versuchen Sie erneut zu klicken, bis dies erfolgreich ist:

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

    Fügen Sie eine Verzögerung hinzu, die der Dauer der Animation / des Übergangs entspricht:

    browser.sleep(250);


  • Das Zielelement wird von einem schwebenden Element abgedeckt, sobald es in die Ansicht gescrollt wurde:

    Der Treiber scrollt das Element automatisch in die Ansicht, um es sichtbar zu machen. Wenn die Seite ein schwebendes / klebriges Element enthält (Menü, Anzeigen, Fußzeile, Benachrichtigung, Cookie-Richtlinie usw.), wird das Element möglicherweise abgedeckt und ist nicht mehr sichtbar / interaktiv.

    Beispiel: https://twitter.com/?lang=de

    Problemumgehungen:

    Stellen Sie die Größe des Fensters auf ein größeres ein, um das Scrollen oder das schwebende Element zu vermeiden.

    Bewegen Sie den Mauszeiger über das Element mit einem negativen YVersatz und klicken Sie darauf:

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

    Scrollen Sie das Element vor dem Klicken in die Mitte des Fensters:

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

    Blenden Sie das schwebende Element aus, wenn es nicht vermieden werden kann:

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

HINWEIS: Nennen wir "Klicken" den Endbenutzer-Klick. 'js click' ist ein Klick über JS

Warum funktioniert das Klicken auf "über JavaScript", wenn ein normaler WebDriver-Klick dies nicht tut?

Hierfür gibt es zwei Fälle:

I. Wenn Sie PhamtomJS verwenden

Dann ist dies das bekannteste Verhalten von PhantomJS. Einige Elemente können beispielsweise manchmal nicht angeklickt werden <div>. Dies liegt daran, dass PhantomJSdas Original ursprünglich für die Simulation der Browser-Engine erstellt wurde (wie anfängliches HTML + CSS -> Berechnen von CSS -> Rendern). Es bedeutet jedoch nicht, als Endbenutzer (Anzeigen, Klicken, Ziehen) mit ihm zu interagieren. Daher PhamtomJSwird die Interaktion mit Endbenutzern nur teilweise unterstützt.

WARUM FUNKTIONIERT JS CLICK? Bei jedem Klick handelt es sich um einen mittleren Klick. Es ist wie eine Waffe mit 1 Lauf und 2 Abzügen . Eine aus dem Ansichtsfenster, eine aus JS. Da PhamtomJSgroße in dem Motor zu simulieren - Browser, ein JS Klick sollte perfekt funktionieren.

II. Der Event-Handler von "click" musste in der schlechten Zeit binden.

Zum Beispiel haben wir eine <div>

  • -> Wir rechnen

  • -> dann binden wir das Ereignis des Klickens an das <div>.

  • -> Plus mit einer schlechten Codierung des Winkels (z. B. wenn der Zyklus des Oszilloskops nicht richtig gehandhabt wird)

Wir können mit dem gleichen Ergebnis enden. Klicken funktioniert nicht, da WebdriverJS versucht, auf das Element zu klicken, wenn es keinen Klickereignishandler hat.

WARUM FUNKTIONIERT JS CLICK? Js Klick ist wie das direkte Einfügen von js in den Browser. Möglich mit 2 Möglichkeiten,

Die Faust ist über die devtools-Konsole (ja, WebdriverJS kommuniziert mit der devtools-Konsole).

Zweitens wird ein <script>Tag direkt in HTML eingefügt.

Für jeden Browser ist das Verhalten unterschiedlich. Unabhängig davon sind diese Methoden komplizierter als das Klicken auf die Schaltfläche. Click verwendet das, was bereits vorhanden ist (Endbenutzer klicken), js click geht durch die Hintertür.

Und für js Klick scheint eine asynchrone Aufgabe zu sein. Dies hängt mit einem etwas komplexen Thema zusammen: " Browser-asynchrone Task- und CPU-Task-Planung " (lesen Sie es vor einiger Zeit und finden Sie den Artikel nicht wieder). Kurz gesagt, dies führt meistens dazu, dass js click auf einen Zyklus der Taskplanung der CPU warten muss und nach dem Binden des Klickereignisses etwas langsamer ausgeführt wird. (Sie konnten diesen Fall kennen, als Sie das Element manchmal anklickbar fanden, manchmal nicht.)

Wann genau passiert dies und was ist der Nachteil dieser Problemumgehung (falls vorhanden)?

=> Wie oben erwähnt, bedeuten beide für einen Zweck, aber über die Verwendung des Eingangs:

  • Klicken Sie auf: verwendet standardmäßig den Browser.
  • JS-Klick: geht durch die Hintertür.

=> Für die Leistung ist es schwer zu sagen, da es auf Browsern beruht. Aber allgemein:

  • Klicken: bedeutet nicht schneller, sondern nur eine höhere Position in der Zeitplanliste der CPU-Ausführungsaufgabe.
  • JS-Klick: bedeutet nicht langsamer, sondern nur an der letzten Position der Zeitplanliste der CPU-Aufgabe.

=> Nachteile:

  • Klick: scheint keinen Nachteil zu haben, außer dass Sie PhamtomJS verwenden.
  • JS-Klick: sehr gesundheitsschädlich. Sie können versehentlich auf etwas klicken, das in der Ansicht nicht vorhanden ist. Wenn Sie dies verwenden, stellen Sie sicher, dass das Element vorhanden und verfügbar ist, um es als Standpunkt des Endbenutzers anzuzeigen und darauf zu klicken.

PS, wenn Sie nach einer Lösung suchen.

  • Verwenden Sie PhantomJS? Ich werde vorschlagen, stattdessen Chrome Headless zu verwenden. Ja, Sie können Chrome Headless unter Ubuntu einrichten. Die Sache läuft genau wie Chrome, hat aber nur keine Aussicht und ist weniger fehlerhaft wie PhantomJS.
  • Sie verwenden PhamtomJS nicht, haben aber immer noch Probleme? Ich werde vorschlagen, ExpectedCondition of Protractor mit zu verwenden browser.wait()( überprüfen Sie dies für weitere Informationen )

(Ich möchte es kurz machen, bin aber schlecht gelandet. Alles, was mit Theorie zu tun hat, ist kompliziert zu erklären ...)

Linh Pham
quelle