¿Alguna sugerencia para probar el código extjs en un navegador, preferiblemente con selenium?

Hemos estado usando selenium con gran éxito para manejar pruebas de sitios web de alto nivel (además de extensos doctests de Python a nivel de módulo). Sin embargo, ahora estamos usando extjs para muchas páginas y resulta difícil incorporar pruebas de Selenium para los componentes complejos, como las cuadrículas.

¿Alguien ha tenido éxito al escribir pruebas automatizadas para páginas web basadas en extjs? Muchos de Google encuentran personas con problemas similares, pero pocas respuestas. ¡Gracias!

El mayor obstáculo para probar ExtJS con Selenium es que ExtJS no representa elementos HTML estándar y el Selenium IDE genera ingenuamente (y legítimamente) comandos dirigidos a elementos que simplemente actúan como decoración (elementos superfluos que ayudan a ExtJS con todo el escritorio) mira y siente. Aquí hay algunos consejos y trucos que he reunido al escribir una prueba automatizada de Selenium en una aplicación ExtJS.

Consejos generales

Localización de elementos

Al generar casos de prueba de Selenium al registrar las acciones del usuario con Selenium IDE en Firefox, Selenium basará las acciones registradas en los ID de los elementos HTML. Sin embargo, para la mayoría de los elementos en los que se puede hacer clic, ExtJS usa identificadores generados como “ext-gen-345” que es probable que cambien en una visita posterior a la misma página, incluso si no se han realizado cambios en el código. Después de registrar las acciones del usuario para una prueba, es necesario realizar un esfuerzo manual para pasar por todas las acciones que dependen de los identificadores generados y para reemplazarlos. Hay dos tipos de reemplazos que se pueden hacer:

Reemplazar un localizador de Id con un localizador de CSS o XPath

Los localizadores de CSS comienzan con “css =” y los localizadores de XPath comienzan con “//” (el prefijo “xpath =” es opcional). Los localizadores de CSS son menos detallados y son más fáciles de leer, y deberían preferirse a los localizadores de XPath. Sin embargo, puede haber casos donde los localizadores XPath necesitan ser utilizados porque un localizador CSS simplemente no puede cortarlo.

Ejecutando JavaScript

Algunos elementos requieren más que simples interacciones entre el mouse y el teclado debido a la compleja ejecución realizada por ExtJS. Por ejemplo, un Ext.form.CombBox no es realmente un elemento , sino una entrada de texto con una lista desplegable separada que está en algún lugar en la parte inferior del árbol de documentos. Para simular correctamente una selección de ComboBox, primero es posible simular un clic en la flecha desplegable y luego hacer clic en la lista que aparece. Sin embargo, ubicar estos elementos a través de localizadores CSS o XPath puede ser engorroso. Una alternativa es ubicar el componente ComoBox y llamar a los métodos para simular la selección:

 var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components combo.setValue('female'); // set the value combo.fireEvent('select'); // because setValue() doesn't trigger the event 

En Selenium, el comando runScript se puede usar para realizar la operación anterior en una forma más concisa:

 with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); } 

Hacer frente a AJAX y renderizado lento

Selenium tiene sabores “* AndWait” para todos los comandos para esperar cargas de página cuando una acción del usuario da como resultado transiciones o recargas de página. Sin embargo, como las recuperaciones de AJAX no implican cargas de página reales, estos comandos no se pueden usar para la sincronización. La solución es hacer uso de pistas visuales como la presencia / ausencia de un indicador de progreso de AJAX o la aparición de filas en una grilla, componentes adicionales, enlaces, etc. Por ejemplo:

 Command: waitForElementNotPresent Target: css=div:contains('Loading...') 

A veces, un elemento aparecerá solo después de un cierto período de tiempo, dependiendo de qué tan rápido extJS represente los componentes después de que una acción del usuario provoque un cambio en la vista. En lugar de usar demoras arbitrarias con el comando de pause , el método ideal es esperar hasta que el elemento de interés esté a nuestro scope. Por ejemplo, para hacer clic en un elemento después de esperar a que aparezca:

 Command: waitForElementPresent Target: css=span:contains('Do the funky thing') Command: click Target: css=span:contains('Do the funky thing') 

No es una buena idea confiar en pausas arbitrarias, ya que las diferencias de tiempo que resultan de ejecutar las pruebas en diferentes navegadores o en diferentes máquinas harán que los casos de prueba sean escamosos.

Elementos no clicables

Algunos elementos no pueden ser activados por el comando de click . Se debe a que el detector de eventos se encuentra realmente en el contenedor, mirando los eventos del mouse en sus elementos secundarios, que eventualmente llegan hasta el padre. El control de tabs es un ejemplo. Para hacer clic en una pestaña, debe simular un evento mouseDown en la etiqueta de la pestaña:

 Command: mouseDownAt Target: css=.x-tab-strip-text:contains('Options') Value: 0,0 

Validación de campo

Los campos de formulario (componentes Ext.form. *) Que tienen expresiones regulares asociadas o vtypes para la validación activarán la validación con un cierto retraso (ver la propiedad validationDelay que está establecida en 250 ms por defecto), después de que el usuario ingrese texto o inmediatamente cuando el campo pierde foco – o desenfoca (vea la propiedad validateOnDelay ). Para activar la validación de campo después de emitir el comando tipo Selenium para ingresar texto dentro de un campo, debe hacer una de las siguientes acciones:

  • Activación de validación retrasada

    ExtJS desactiva el temporizador de retardo de validación cuando el campo recibe eventos de teclado. Para activar este temporizador, simplemente emita un evento de llave dummy (no importa qué tecla use ya que ExtJS lo ignora), seguido de una breve pausa que es más larga que validationDelay:

     Command: keyUp Target: someTextArea Value: x Command: pause Target: 500 
  • Activación de Validación Inmediata

    Puede inyectar un evento de desenfoque en el campo para activar la validación inmediata:

     Command: runScript Target: someComponent.nameTextField.fireEvent("blur") 

Comprobando los resultados de validación

Después de la validación, puede verificar la presencia o ausencia de un campo de error:

 Command: verifyElementNotPresent Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))] Command: verifyElementPresent Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))] 

Tenga en cuenta que la marca “display: none” es necesaria porque una vez que se muestra un campo de error y luego debe ocultarse, ExtJS simplemente ocultará el campo de error en lugar de eliminarlo completamente del árbol DOM.

Consejos específicos del elemento

Hacer clic en un Ext.form.Button

  • Opción 1

    Comando: haga clic en Objetivo: css = botón: contiene (‘Guardar’)

    Selecciona el botón por su título

  • opcion 2

    Comando: haga clic en el botón Target: css = # save-options

    Selecciona el botón por su id

Seleccionar un valor de un Ext.form.ComboBox

 Command: runScript Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); } 

Primero establece el valor y luego activa explícitamente el evento select en caso de que haya observadores.

Este blog me ayudó mucho. Escribió mucho sobre el tema y parece que todavía está activo. El chico también parece apreciar el buen diseño.

Básicamente habla sobre el uso de enviar javascript para hacer consultas y el uso del método Ext.ComponentQuery.query para recuperar cosas de la misma manera que lo hace internamente en su aplicación de extensión. De esta forma puedes usar xtypes y itemIds y no tienes que preocuparte por intentar analizar cualquiera de las cosas enojadas autogeneradas.

Encontré este artículo en particular muy útil.

Podría publicar algo más detallado aquí pronto; aún estoy tratando de entender cómo hacerlo correctamente.

He estado probando mi aplicación web ExtJs con selenium. Uno de los mayores problemas fue seleccionar un elemento en la cuadrícula para hacer algo con él.

Para esto, escribí el método helper (en la clase SeleniumExtJsUtils, que es una colección de métodos útiles para facilitar la interacción con ExtJs):

 /** * Javascript needed to execute in order to select row in the grid * * @param gridId Grid id * @param rowIndex Index of the row to select * @return Javascript to select row */ public static String selectGridRow(String gridId, int rowIndex) { return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)"; } 

y cuando necesitaba seleccionar una fila, simplemente llamaba:

 selenium.runScript( SeleniumExtJsUtils.selectGridRow("", 5) ); 

Para que esto funcione, necesito configurar mi ID en la grilla y no dejar que ExtJs lo genere.

Para detectar que ese elemento es visible, usa la cláusula: not(contains(@style, "display: none")

Es mejor usar esto:

 visible_clause = "not(ancestor::*[contains(@style,'display: none')" + " or contains(@style, 'visibility: hidden') " + " or contains(@class,'x-hide-display')])" hidden_clause = "parent::*[contains(@style,'display: none')" + " or contains(@style, 'visibility: hidden')" + " or contains(@class,'x-hide-display')]" 

¿Puede proporcionar más información sobre los tipos de problemas que está teniendo con las pruebas extjs?

Una extensión de Selenium que encuentro útil es waitForCondition . Si su problema parece ser un problema con los eventos de Ajax, puede usar waitForCondition para esperar que ocurran los eventos.

Las páginas web de Ext JS pueden ser difíciles de probar, debido al complicado HTML que generan al igual que las cuadrículas de Ext JS.

HTML5 Robot se ocupa de esto mediante el uso de una serie de prácticas recomendadas para buscar e interactuar confiablemente con componentes basados ​​en atributos y condiciones que no son dynamics. A continuación, proporciona accesos directos para hacer esto con todos los componentes HTML, Ext JS y Sencha Touch con los que necesitaría interactuar. Viene en 2 sabores:

  1. Java – Familiar Selenium y API basada en JUnit que ha incorporado compatibilidad con el controlador web para todos los navegadores modernos.
  2. Gwen : lenguaje de estilo humano para crear y mantener de forma rápida y sencilla pruebas de navegador, que viene con su propio entorno de desarrollo integrado. Todo lo cual se basa en la API de Java.

Por ejemplo, si quisiera encontrar la fila de cuadrícula Ext JS que contiene el texto “Foo”, podría hacer lo siguiente en Java:

 findExtJsGridRow("Foo"); 

… y podrías hacer lo siguiente en Gwen:

 extjsgridrow by text "Foo" 

Hay mucha documentación para Java y Gwen sobre cómo trabajar con componentes específicos de Ext JS. La documentación también detalla el HTML resultante para todos estos componentes Ext JS, que también puede serle útil.

Consejos útiles para obtener la cuadrícula a través de la Id de la cuadrícula en la página: creo que puede ampliar la función más útil de esta API.

  sub get_grid_row { my ($browser, $grid, $row) = @_; my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" . "var grid = doc.getElementById('$grid');\n" . "var table = grid.getElementsByTagName('table');\n" . "var result = '';\n" . "var row = 0;\n" . "for (var i = 0; i < table.length; i++) {\n" . " if (table[i].className == 'x-grid3-row-table') {\n". " row++;\n" . " if (row == $row) {\n" . " var cols_len = table[i].rows[0].cells.length;\n" . " for (var j = 0; j < cols_len; j++) {\n" . " var cell = table[i].rows[0].cells[j];\n" . " if (result.length == 0) {\n" . " result = getText(cell);\n" . " } else { \n" . " result += '|' + getText(cell);\n" . " }\n" . " }\n" . " }\n" . " }\n" . "}\n" . "result;\n"; my $result = $browser->get_eval($script); my @res = split('\|', $result); return @res; } 

Estamos desarrollando un marco de prueba que usa selenium y encontramos problemas con extjs (ya que es una representación del lado del cliente). Me parece útil buscar un elemento una vez que el DOM esté listo.

 public static boolean waitUntilDOMIsReady(WebDriver driver) { def maxSeconds = DEFAULT_WAIT_SECONDS * 10 for (count in 1..maxSeconds) { Thread.sleep(100) def ready = isDOMReady(driver); if (ready) { break; } } } public static boolean isDOMReady(WebDriver driver){ return driver.executeScript("return document.readyState"); } 

Para la interfaz de usuario compleja que no es HTML formal, xPath siempre es algo con lo que se puede contar, pero un poco complejo cuando se trata de una implementación de UI diferente utilizando ExtJs.

Puedes usar Firebug y Firexpath como extensiones de Firefox para probar el xpath de un elemento determinado, y pasar simple xpath completo como parámetro al selenium.

Por ejemplo en código java:

 String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button" selenium.click(fullXpath); 

Pruebas más sencillas mediante atributos de datos HTML personalizados

De la documentación de Sencha :

Un itemId se puede utilizar como una forma alternativa de obtener una referencia a un componente cuando no hay referencia de objeto disponible. En lugar de usar una identificación con Ext.getCmp, use itemId con Ext.container.Container.getComponent que recuperará itemId o id’s. Debido a que itemId’s son un índice de la MixedCollection interna del contenedor, itemId tiene un scope local en el contenedor, lo que evita conflictos potenciales con Ext.ComponentManager, que requiere una identificación única.

Sobreescribiendo el método onBoxReady , establezco un atributo de datos personalizado (cuyo nombre proviene de mi propiedad testIdAttr personalizada de cada componente) al valor itemId del componente, si existe. Agregue la clase Testing.overrides.AbstractComponent a su archivo application.js requires matriz.

 /** * Overrides the Ext.AbstracComponent's onBoxReady * method to add custom data attributes to the * component's dom structure. * * @author Brian Wendt */ Ext.define('Testing.overrides.AbstractComponent', { override: 'Ext.AbstractComponent', onBoxReady: function () { var me = this, el = me.getEl(); if (el && el.dom && me.itemId) { el.dom.setAttribute(me.testIdAttr || 'data-selenium-id', me.itemId); } me.callOverridden(arguments); } }); 

Este método proporciona a los desarrolladores una forma de reutilizar un identificador descriptivo dentro de su código y tener esos identificadores disponibles cada vez que se procesa la página. No más búsquedas a través de identificadores no descriptivos generados dinámicamente.

Cuando estaba probando la aplicación ExtJS utilizando WebDriver, utilicé el siguiente enfoque: busqué el campo por el texto de la etiqueta y obtuve el atributo @for de la etiqueta. Por ejemplo, tenemos una etiqueta

  

Y debemos señalar a WebDriver algunas entradas: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)] .

Por lo tanto, elegirá el id del atributo @for y lo usará más. Este es probablemente el caso más simple, pero le da la forma de localizar el elemento. Es mucho más difícil cuando no tiene etiqueta, pero luego necesita encontrar algún elemento y escribir su xpath buscando hermanos, descender / ascender elementos.