WebDriver click () vs JavaScript click ()

126

La historia:

Aquí en StackOverflow, he visto a usuarios que informan que no pueden hacer clic en un elemento mediante el comando "clic" de selenium WebDriver y pueden solucionarlo con un clic de JavaScript ejecutando un script.

Ejemplo en Python:

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

Ejemplo en WebDriverJS / Protractor:

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

La pregunta:

¿Por qué hacer clic en "a través de JavaScript" funciona cuando un clic en WebDriver normal no funciona? ¿Cuándo exactamente está sucediendo esto y cuál es la desventaja de esta solución (si la hay)?

Personalmente utilicé esta solución sin comprender por qué tengo que hacerlo y a qué problemas puede conducir.

alecxe
fuente

Respuestas:

150

Contrariamente a lo que sugiere la respuesta actualmente aceptada , no hay nada específico para PhantomJS cuando se trata de la diferencia entre hacer que WebDriver haga un clic y hacerlo en JavaScript.

La diferencia

La diferencia esencial entre los dos métodos es común a todos los navegadores y puede explicarse de manera bastante simple:

  • WebDriver: cuando WebDriver hace el clic, intenta lo mejor que puede simular lo que sucede cuando un usuario real usa el navegador. Suponga que tiene un elemento A que es un botón que dice "Haga clic en mí" y un elemento B que es un divelemento que es transparente pero tiene sus dimensiones y está zIndexconfigurado para que cubra completamente A. Luego le dice a WebDriver que haga clic en A. WebDriver simule el clic para que B reciba el clic primero . ¿Por qué? Debido a que B cubre a A, y si un usuario intentara hacer clic en A, entonces B obtendría el evento primero. El hecho de que A finalmente obtenga el evento de clic depende de cómo B maneje el evento. En cualquier caso, el comportamiento con WebDriver en este caso es el mismo que cuando un usuario real intenta hacer clic en A.

  • JavaScript: ahora, supongamos que usa JavaScript para hacerlo A.click(). Este método de hacer clic no reproduce lo que realmente sucede cuando el usuario intenta hacer clic en A. JavaScript envía el clickevento directamente a A, y B no obtendrá ningún evento.

¿Por qué funciona un clic de JavaScript cuando no funciona un clic de WebDriver?

Como mencioné anteriormente, WebDriver intentará simular lo mejor que pueda lo que sucede cuando un usuario real está usando un navegador. El hecho es que el DOM puede contener elementos con los que un usuario no puede interactuar, y WebDriver no le permitirá hacer clic en estos elementos. Además del caso superpuesto que mencioné, esto también implica que no se puede hacer clic en elementos invisibles. Un caso común que veo en las preguntas de desbordamiento de pila es alguien que está tratando de interactuar con un elemento de la GUI que ya existe en el DOM pero que se vuelve visible solo cuando se ha manipulado algún otro elemento. Esto sucede a veces con los menús desplegables: primero debe hacer clic en el botón que abre el menú desplegable antes de que se pueda seleccionar un elemento del menú. Si alguien intenta hacer clic en el elemento del menú antes de que el menú sea visible,Si la persona intenta hacerlo con JavaScript, funcionará porque el evento se entrega directamente al elemento, independientemente de la visibilidad.

¿Cuándo debe usar JavaScript para hacer clic?

Si está utilizando Selenium para probar una aplicación , mi respuesta a esta pregunta es "casi nunca". En general, su prueba de Selenium debería reproducir lo que un usuario haría con el navegador. Tomando el ejemplo del menú desplegable: una prueba debe hacer clic en el botón que abre primero el menú desplegable y luego hacer clic en el elemento del menú. Si hay un problema con la GUI porque el botón es invisible, o el botón no muestra los elementos del menú, o algo similar, entonces su prueba fallará y habrá detectado el error. Si usa JavaScript para hacer clic, no podrá detectar estos errores a través de pruebas automatizadas.

Digo "casi nunca" porque puede haber excepciones en las que tiene sentido usar JavaScript. Sin embargo, deberían ser muy raros.

Si está utilizando Selenium para eliminar sitios , entonces no es tan importante intentar reproducir el comportamiento del usuario. Por lo tanto, usar JavaScript para omitir la GUI es un problema menor.

Louis
fuente
1
Respuesta mucho mejor, esta debería ser la aceptada. La respuesta anterior está hablando de un caso límite específico para PhantomJS, este es mucho más preciso en mi humilde opinión.
Ardesco
@Ardesco definitivamente. Marcado como aceptado. Perfecto y una respuesta bastante detallada.
alecxe
Otorgar la recompensa a Linh según lo planeado, pero comenzaré una nueva para agradecerle por otra respuesta increíble. Gracias.
alecxe
1
Muy buena y comprensible respuesta. En mi experiencia, WebDriver ha tenido muchos problemas con las páginas de AngularJS: con algunos elementos, los métodos de WebDriver gustaron clicko sendKeysfuncionaron, pero no siempre. No hubo problema de localización u otra excepción, el caso de prueba simplemente no progresó más. El registro mostró que la acción se ejecutó. A veces usando Actionayudado. Si hice clic en el elemento manualmente (después de que el caso de prueba se detuvo), todo funcionó bien. Entonces cambiamos a JS sin procesar en esas ocasiones. No he trabajado con AngularJS en los últimos 2 años, por lo que las cosas podrían mejorar ahora.
Würgspaß
1
@Alex No tengo un enlace útil. Mi respuesta se deriva de la experiencia con Selenium en particular, y con la simulación de eventos de usuarios en general. Empecé a usar Selenium hace 5 años. Durante el tiempo que utilicé Selenium, tuve que leer el código de Selenium para solucionar algunos problemas y presenté bastantes informes de errores sobre el envío de eventos y discutí los errores con los desarrolladores de Selenium. "lo mejor que puede" es el objetivo. Ciertamente me he encontrado con errores (ahora corregidos) que impidieron que alcanzara ese objetivo. Algunos errores pueden permanecer.
Louis
30

El clic ejecutado por el controlador intenta simular el comportamiento de un usuario real lo más cerca posible mientras el JavaScript HTMLElement.click()realiza la acción predeterminada para el clickevento, incluso si el elemento no es interactivo.

Las diferencias son:

  • El controlador se asegura de que el elemento sea ​​visible desplazándolo hacia la vista y verifica que el elemento sea interactivo .

    El controlador generará un error:

    • cuando el elemento en la parte superior en las coordenadas del clic no es el elemento objetivo o un descendiente
    • cuando el elemento no tiene un tamaño positivo o si es completamente transparente
    • cuando el elemento es una entrada o botón deshabilitado (atributo / propiedad disabledes true)
    • cuando el elemento tiene el puntero del mouse deshabilitado (CSS pointer-eventses none)


    Un JavaScript HTMLElement.click()siempre realizará la acción predeterminada o, en el mejor de los casos, fallará silenciosamente si el elemento está deshabilitado.

  • Se espera que el controlador ponga el elemento en foco si es enfocable.

    Un JavaScript HTMLElement.click()no lo hará.

  • Se espera que el controlador emita todos los eventos (mousemove, mousedown, mouseup, click, ...) como un usuario real.

    Un JavaScript HTMLElement.click()emite solo el clickevento. La página puede confiar en estos eventos adicionales y puede comportarse de manera diferente si no se emiten.

    Estos son los eventos emitidos por el controlador para 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, ... }

    Y este es el evento emitido con una inyección de JavaScript:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • El evento emitido por un JavaScript .click() no es confiable y la acción predeterminada no se puede invocar:

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

    Tenga en cuenta que algunos de los controladores siguen generando eventos no confiables. Este es el caso de PhantomJS a partir de la versión 2.1.

  • El evento emitido por un JavaScript .click() no tiene las coordenadas del clic .

    Las propiedades clientX, clientY, screenX, screenY, layerX, layerYestán establecidas en 0. La página puede confiar en ellos y comportarse de manera diferente.


Puede estar bien usar un JavaScript .click()para eliminar algunos datos, pero no está en un contexto de prueba. Derrota el propósito de la prueba ya que no simula el comportamiento de un usuario. Por lo tanto, si falla el clic del controlador, lo más probable es que un usuario real tampoco realice el mismo clic en las mismas condiciones.


¿Qué hace que el controlador no haga clic en un elemento cuando esperamos que tenga éxito?

  • El elemento objetivo aún no es visible / interactible debido a un retraso o un efecto de transición.

    Algunos ejemplos :

    https://developer.mozilla.org/fr/docs/Web (menú desplegable de navegación) http://materializecss.com/side-nav.html (barra lateral desplegable)

    Lugares de trabajo:

    Agregue un camarero para esperar la visibilidad, un tamaño mínimo o una posición estable:

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

    Vuelva a intentar hacer clic hasta que tenga éxito:

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

    Agregue un retraso que coincida con la duración de la animación / transición:

    browser.sleep(250);


  • El elemento objetivo termina cubierto por un elemento flotante una vez que se desplaza hacia la vista:

    El controlador desplaza automáticamente el elemento hacia la vista para que sea visible. Si la página contiene un elemento flotante / adhesivo (menú, anuncios, pie de página, notificación, política de cookies ...), el elemento puede terminar cubierto y dejará de ser visible / interactivo.

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

    Soluciones alternativas:

    Establezca el tamaño de la ventana en una más grande para evitar el desplazamiento o el elemento flotante.

    Mueva sobre el elemento con un Ydesplazamiento negativo y luego haga clic en él:

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

    Desplace el elemento hacia el centro de la ventana antes 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();

    Ocultar el elemento flotante si no se puede evitar:

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

NOTA: llamemos 'clic' es clic del usuario final. 'js click' es clic a través de JS

¿Por qué hacer clic en "a través de JavaScript" funciona cuando un clic en WebDriver normal no funciona?

Hay 2 casos para que esto suceda:

I. Si está usando PhamtomJS

Entonces este es el comportamiento conocido más común de PhantomJS. Algunos elementos a veces no se pueden hacer clic, por ejemplo <div>. Esto se debe a que PhantomJSfue creado originalmente para simular el motor de los navegadores (como HTML + CSS inicial -> computación CSS -> renderizado). Pero no significa interactuar con el usuario final (ver, hacer clic, arrastrar). Por PhamtomJSlo tanto, solo es parcialmente compatible con la interacción de los usuarios finales.

¿POR QUÉ JS HACE CLIC EN EL TRABAJO? En cuanto a cualquiera de los clics, todos son clics medios. Es como una pistola con 1 cañón y 2 disparadores . Uno de la ventana gráfica, uno de JS. Dado que es PhamtomJSexcelente para simular el motor del navegador, un clic JS debería funcionar perfectamente.

II El controlador de eventos de "clic" debe vincularse en el mal período de tiempo.

Por ejemplo, tenemos un <div>

  • -> Hacemos algunos cálculos

  • -> luego vinculamos el evento de clic al <div>.

  • -> Además con alguna codificación incorrecta de angular (por ejemplo, no manejando el ciclo de alcance adecuadamente)

Podemos terminar con el mismo resultado. Hacer clic no funcionará porque WebdriverJS intenta hacer clic en el elemento cuando no tiene un controlador de eventos de clic.

¿POR QUÉ JS HACE CLIC EN EL TRABAJO? Js click es como inyectar js directamente en el navegador. Posible de 2 maneras,

El puño es a través de la consola de devtools (sí, WebdriverJS se comunica con la consola de devtools).

El segundo es inyectar una <script>etiqueta directamente en HTML.

Para cada navegador, el comportamiento será diferente. Pero independientemente, estos métodos son más complicados que hacer clic en el botón. Click está usando lo que ya está allí (clic de los usuarios finales), js click pasa por la puerta trasera.

Y para js click parecerá ser una tarea asincrónica. Esto está relacionado con un tema un poco complejo de ' tarea asincrónica del navegador y programación de tareas de la CPU ' (léelo hace un tiempo no puedo encontrar el artículo nuevamente). Para abreviar, esto resultará principalmente ya que js click tendrá que esperar un ciclo de programación de tareas de la CPU y se ejecutará un poco más lento después del enlace del evento click. (Podría conocer este caso cuando encontró que el elemento a veces se puede hacer clic, a veces no).

¿Cuándo exactamente está sucediendo esto y cuál es la desventaja de esta solución (si la hay)?

=> Como se mencionó anteriormente, ambos significan para un propósito, pero sobre el uso de qué entrada:

  • Click: está utilizando lo que proporciona por defecto el navegador.
  • JS click: está pasando por la puerta trasera.

=> Para el rendimiento, es difícil de decir porque se basa en los navegadores. Pero en general:

  • Clic: no significa más rápido, pero solo firmó una posición más alta en la lista de programación de la tarea de ejecución de la CPU.
  • Clic JS: no significa más lento, pero solo inició sesión en la última posición de la lista de tareas de CPU.

=> Desventajas:

  • Clic: no parece tener ningún inconveniente, excepto que está utilizando PhamtomJS.
  • JS click: muy malo para la salud. Puede hacer clic accidentalmente en algo que no aparece en la vista. Cuando use esto, asegúrese de que el elemento esté allí y disponible para ver y hacer clic como el punto de vista del usuario final.

PD: si estás buscando una solución.

  • Usando PhantomJS? Sugeriré usar Chrome sin cabeza en su lugar. Sí, puedes configurar Chrome sin cabeza en Ubuntu. La cosa funciona igual que Chrome pero solo no tiene una vista y es menos defectuosa como PhantomJS.
  • ¿No usa PhamtomJS pero aún tiene problemas? Sugeriré usar ExpectedCondition of Protractor con browser.wait()( verifique esto para obtener más información )

(Quiero hacerlo breve, pero terminó mal. Cualquier cosa relacionada con la teoría es complicada de explicar ...)

Linh Pham
fuente