Clic WebDriver () vs clic JavaScript ()


126

La storia:

Qui su StackOverflow, ho visto gli utenti segnalare che non possono fare clic su un elemento tramite il comando "click" di selenio WebDriver e possono aggirarlo con un clic JavaScript eseguendo uno script.

Esempio in Python:

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

Esempio in WebDriverJS / Goniometro:

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

La domanda:

Perché il clic "via JavaScript" funziona quando un normale clic su WebDriver non funziona? Quando sta esattamente succedendo questo e qual è il lato negativo di questa soluzione alternativa (se presente)?

Personalmente ho usato questa soluzione alternativa senza comprendere appieno il motivo per cui devo farlo e quali problemi può portare.

Risposte:


150

Contrariamente a quanto suggerisce la risposta attualmente accettata , non c'è nulla di specifico in PhantomJS quando si tratta della differenza tra avere WebDriver a fare un clic e farlo in JavaScript.

La differenza

La differenza essenziale tra i due metodi è comune a tutti i browser e può essere spiegata semplicemente:

  • WebDriver: quando WebDriver fa il clic, tenta nel miglior modo possibile di simulare ciò che accade quando un utente reale utilizza il browser. Supponiamo di avere un elemento A che è un pulsante che dice "Click me" e un elemento B che è un divelemento che è trasparente ma ha le sue dimensioni e zIndeximpostato in modo che copra completamente A. Quindi dici a WebDriver di fare clic su A. WebDriver simula il clic in modo che B riceva prima il clic . Perché? Perché B copre A e se un utente dovesse provare a fare clic su A, allora B otterrebbe prima l'evento. Il fatto che A alla fine ottenga o meno l'evento click dipende da come B gestisce l'evento. In ogni caso, il comportamento con WebDriver in questo caso è lo stesso di quando un utente reale tenta di fare clic su A.

  • JavaScript: ora supponiamo di usare JavaScript per farlo A.click(). Questo metodo di clic non riproduce ciò che accade realmente quando l'utente tenta di fare clic su A. JavaScript invia l' clickevento direttamente ad A e B non riceverà alcun evento.

Perché un clic JavaScript funziona quando un clic WebDriver non funziona?

Come accennato in precedenza, WebDriver cercherà di simulare al meglio ciò che può accadere quando un utente reale utilizza un browser. Il fatto è che il DOM può contenere elementi con cui un utente non può interagire e WebDriver non ti permetterà di fare clic su questi elementi. Oltre al caso sovrapposto che ho citato, ciò implica anche che non è possibile fare clic su elementi invisibili. Un caso comune che vedo nelle domande di Stack Overflow è qualcuno che sta cercando di interagire con un elemento GUI che esiste già nel DOM ma diventa visibile solo quando è stato manipolato qualche altro elemento. Questo a volte accade con i menu a discesa: devi prima fare clic sul pulsante per visualizzare il menu a discesa prima di poter selezionare una voce di menu. Se qualcuno prova a fare clic sulla voce di menu prima che il menu sia visibile,Se la persona tenta quindi di farlo con JavaScript, funzionerà perché l'evento viene recapitato direttamente all'elemento, indipendentemente dalla visibilità.

Quando utilizzare JavaScript per fare clic?

Se stai usando Selenium per testare un'applicazione , la mia risposta a questa domanda è "quasi mai". Nel complesso, il test del selenio dovrebbe riprodurre ciò che un utente farebbe con il browser. Prendendo l'esempio del menu a discesa: un test dovrebbe fare clic sul pulsante che fa apparire prima il menu a discesa, quindi fare clic sulla voce di menu. Se si verifica un problema con la GUI perché il pulsante è invisibile o il pulsante non mostra le voci di menu o qualcosa di simile, il test fallirà e avrai rilevato il bug. Se usi JavaScript per fare clic, non sarai in grado di rilevare questi bug attraverso test automatici.

Dico "quasi mai" perché potrebbero esserci delle eccezioni in cui ha senso usare JavaScript. Dovrebbero essere molto rari, però.

Se si utilizza Selenium per i siti di scraping , non è altrettanto importante tentare di riprodurre il comportamento degli utenti. Quindi l'utilizzo di JavaScript per bypassare la GUI è meno problematico.


1
Risposta molto migliore, questa dovrebbe essere quella accettata. La risposta sopra sta parlando di un caso limite specifico per PhantomJS, questo è IMHO molto più accurato.
Ardesco,

@Ardesco sicuramente. Contrassegnato come accettato. Risposta perfetta e abbastanza dettagliata.
Alecxe

Assegnare la generosità a Linh come previsto, ma ne inizierò uno nuovo per ringraziarti per l'ennesima fantastica risposta. Grazie.
Alecxe

1
Risposta molto buona e comprensibile. Nella mia esperienza WebDriver ha avuto molti problemi con le pagine di AngularJS: con alcuni elementi i metodi di WebDriver sono simili clicko hanno sendKeysfunzionato, ma non sempre. Non ci sono stati problemi di localizzazione o altre eccezioni, il caso di test semplicemente non è andato oltre. La registrazione ha mostrato che l'azione è stata eseguita. A volte l'utilizzo ha Actionaiutato. Se avessi cliccato manualmente sull'elemento (dopo che il test case si era fermato), tutto ha funzionato bene. Quindi in quelle occasioni siamo passati al JS grezzo. Non ho lavorato con AngularJS negli ultimi 2 anni, quindi ora le cose potrebbero andare meglio.
Würgspaß,

1
@Alex Non ho un link utile. La mia risposta deriva dall'esperienza con Selenium in particolare e dalla simulazione di eventi utente in generale. Ho iniziato a usare Selenium 5 anni fa. Durante il periodo in cui ho usato Selenium, ho dovuto leggere il codice di Selenium per risolvere alcuni problemi e ho archiviato parecchi bug report sull'invio di eventi e ho discusso dei bug con gli sviluppatori Selenium. "il meglio che può" è l'obiettivo. Ho sicuramente riscontrato bug (ora corretti) che gli impedivano di raggiungere quell'obiettivo. Alcuni bug potrebbero rimanere.
Louis,

30

Il clic eseguito dal driver tenta di simulare il comportamento di un utente reale il più vicino possibile mentre JavaScript HTMLElement.click()esegue l'azione predefinita per l' clickevento, anche se l'elemento non è interagibile.

Le differenze sono:

  • Il driver assicura che l'elemento sia visibile facendolo scorrere nella vista e verifica che l'elemento sia interabile .

    Il driver genererà un errore:

    • quando l'elemento in cima alle coordinate del clic non è l'elemento target o un discendente
    • quando l'elemento non ha una dimensione positiva o se è completamente trasparente
    • quando l'elemento è un input o pulsante disabilitato (l'attributo / proprietà disabledè true)
    • quando l'elemento ha il puntatore del mouse disabilitato (CSS pointer-eventsè none)


    Un JavaScript HTMLElement.click()eseguirà sempre l'azione predefinita o, nella migliore delle ipotesi, fallirà silenziosamente se l'elemento è disabilitato.

  • Il driver dovrebbe mettere a fuoco l'elemento se è focalizzabile.

    Un JavaScript HTMLElement.click()non lo farà.

  • Il driver dovrebbe emettere tutti gli eventi (mousemove, mousedown, mouseup, click, ...) proprio come un vero utente.

    Un JavaScript HTMLElement.click()emette solo l' clickevento. La pagina potrebbe fare affidamento su questi eventi extra e potrebbe comportarsi diversamente se non vengono emessi.

    Questi sono gli eventi emessi dal driver per un clic con 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, ... }

    E questo è l'evento emesso con un'iniezione JavaScript:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • L'evento emesso da JavaScript .click() non è attendibile e non è possibile richiamare l'azione predefinita:

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

    Si noti che alcuni driver stanno ancora generando eventi non attendibili. Questo è il caso di PhantomJS dalla versione 2.1.

  • L'evento emesso da JavaScript .click() non ha le coordinate del clic .

    Le proprietà clientX, clientY, screenX, screenY, layerX, layerYsono impostate su 0. La pagina potrebbe fare affidamento su di essi e potrebbe comportarsi diversamente.


Potrebbe essere giusto usare un JavaScript .click()per scartare alcuni dati, ma non è in un contesto di test. Sconfigge lo scopo del test poiché non simula il comportamento di un utente. Quindi, se il clic dal driver fallisce, molto probabilmente anche un utente reale non eseguirà lo stesso clic nelle stesse condizioni.


Cosa impedisce al driver di fare clic su un elemento quando ci aspettiamo che abbia successo?

  • L'elemento target non è ancora visibile / interagibile a causa di un ritardo o di un effetto di transizione.

    Qualche esempio :

    https://developer.mozilla.org/fr/docs/Web (menu di navigazione a discesa) http://materializecss.com/side-nav.html (barra laterale a discesa)

    Workarrounds:

    Aggiungi un cameriere per attendere la visibilità, una dimensione minima o una posizione stabile:

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

    Riprova a fare clic finché non riesce:

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

    Aggiungi un ritardo corrispondente alla durata dell'animazione / transizione:

    browser.sleep(250);


  • L'elemento target finisce coperto da un elemento mobile una volta fatto scorrere nella vista:

    Il driver scorre automaticamente l'elemento nella vista per renderlo visibile. Se la pagina contiene un elemento mobile / appiccicoso (menu, annunci, piè di pagina, notifica, politica sui cookie ...), l'elemento potrebbe finire coperto e non sarà più visibile / interagibile.

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

    soluzioni alternative:

    Impostare la dimensione della finestra su una più grande per evitare lo scorrimento o l'elemento mobile.

    Passa sopra l'elemento con un Yoffset negativo e fai clic su di esso:

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

    Scorri l'elemento al centro della finestra prima del 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();

    Nascondi l'elemento mobile se non può essere evitato:

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

17

NOTA: chiamiamo 'click' è il clic dell'utente finale. "js click" è un clic tramite JS

Perché il clic "via JavaScript" funziona quando un normale clic su WebDriver non funziona?

Ci sono 2 casi perché ciò accada:

I. Se stai usando PhamtomJS

Quindi questo è il comportamento noto più comune di PhantomJS. Alcuni elementi a volte non sono selezionabili, ad esempio <div>. Questo perché l' PhantomJSoriginale è stato creato per simulare il motore dei browser (come HTML iniziale + CSS -> calcolo CSS -> rendering). Ma ciò non significa essere interagiti con il modo di un utente finale (visualizzazione, clic, trascinamento). Pertanto PhamtomJSè supportato solo parzialmente dall'interazione degli utenti finali.

PERCHÉ FUNZIONA JS CLICK? Per quanto riguarda entrambi i clic, sono tutti clic medi. È come una pistola con 1 barile e 2 grilletti . Uno dal viewport, uno da JS. Da PhamtomJSgrande nel simulare il motore del browser, un clic JS dovrebbe funzionare perfettamente.

II. Il gestore dell'evento di "clic" deve vincolare il periodo di tempo scadente.

Ad esempio, abbiamo ottenuto un <div>

  • -> Facciamo alcuni calcoli

  • -> quindi associamo l'evento del clic al <div>.

  • -> Plus con una cattiva codifica angolare (ad es. Non gestire correttamente il ciclo dell'oscilloscopio)

Potremmo finire con lo stesso risultato. Il clic non funzionerà perché WebdriverJS tenta di fare clic sull'elemento quando non ha un gestore eventi click.

PERCHÉ FUNZIONA JS CLICK? Fare clic su Js è come iniettare js direttamente nel browser. Possibile con 2 modi,

Il pugno è attraverso la console di devtools (sì, WebdriverJS comunica con la console di devtools).

Il secondo è iniettare un <script>tag direttamente in HTML.

Per ciascun browser, il comportamento sarà diverso. Ma a prescindere, questi metodi sono più complicati che fare clic sul pulsante. Click utilizza ciò che già esiste (clic degli utenti finali), js click passa attraverso backdoor.

E per js click sembrerà un'attività asincrona. Ciò è correlato a un argomento piuttosto complesso di " attività asincrone del browser e pianificazione delle attività della CPU " (leggendo qualche tempo fa non è possibile trovare di nuovo l'articolo). In breve, ciò si verificherà principalmente poiché js click dovrà attendere un ciclo di pianificazione delle attività della CPU e verrà eseguito un po 'più lentamente dopo l'associazione dell'evento click. (Potresti conoscere questo caso quando hai trovato l'elemento a volte cliccabile, a volte no.)

Quando sta esattamente succedendo questo e qual è il lato negativo di questa soluzione alternativa (se presente)?

=> Come menzionato sopra, entrambi significano per uno scopo, ma sull'uso dell'ingresso:

  • Clic: utilizza ciò che fornisce per impostazione predefinita del browser.
  • JS clic: sta attraversando backdoor.

=> Per le prestazioni, è difficile dirlo perché si basa sui browser. Ma generalmente:

  • Clic: non significa più veloce ma ha solo firmato una posizione più alta nell'elenco delle attività di esecuzione della CPU.
  • JS clic: non significa più lento ma solo ha eseguito l'accesso all'ultima posizione dell'elenco di pianificazione dell'attività della CPU.

=> Lati negativi:

  • Clic: non sembra avere alcun aspetto negativo se non si utilizza PhamtomJS.
  • JS click: molto male per la salute. È possibile fare clic accidentalmente su qualcosa che non è presente nella vista. Quando lo usi, assicurati che l'elemento sia presente e disponibile per visualizzare e fare clic come punto di vista dell'utente finale.

PS se stai cercando una soluzione.

  • Usi PhantomJS? Suggerirò invece di usare Chrome senza testa. Sì, puoi configurare Chrome senza testa su Ubuntu. La cosa funziona proprio come Chrome ma non ha solo una vista e meno buggy come PhantomJS.
  • Non stai usando PhamtomJS ma hai ancora problemi? Suggerirò di utilizzare ExpectedCondition del goniometro con browser.wait()( controllare questo per ulteriori informazioni )

(Voglio farla breve, ma è finita male. Tutto ciò che è legato alla teoria è complicato da spiegare ...)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.