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


92

Hemos estado usando selenio con gran éxito para manejar pruebas de sitios web de alto nivel (además de extensas pruebas de documentación de Python a nivel de módulo). Sin embargo, ahora estamos usando extjs para muchas páginas y está resultando difícil incorporar pruebas de Selenium para componentes complejos como cuadrículas.

¿Alguien ha tenido éxito escribiendo pruebas automatizadas para páginas web basadas en extjs? Al buscar en Google, se encuentran personas con problemas similares, pero pocas respuestas. ¡Gracias!


La buena gente de Ext JS ha tenido la amabilidad de publicar sobre este tema en su blog aquí . Espero que esto ayude.
NBRed5

Respuestas:


173

El mayor obstáculo para probar ExtJS con Selenium es que ExtJS no procesa elementos HTML estándar y el IDE de Selenium generará ingenuamente (y con razón) 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 recopilado mientras escribía una prueba automática de Selenium contra una aplicación ExtJS.

Consejos generales

Elementos de localización

Al generar casos de prueba de Selenium registrando las acciones del usuario con Selenium IDE en Firefox, Selenium basará las acciones registradas en los identificadores de los elementos HTML. Sin embargo, para la mayoría de los elementos en los que se puede hacer clic, ExtJS utiliza identificadores generados como "ext-gen-345" que probablemente cambiarán 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 realizar todas las acciones que dependen de los identificadores generados y reemplazarlos. Hay dos tipos de reemplazos que se pueden realizar:

Reemplazo de un localizador de identificación con un localizador CSS o XPath

Los localizadores CSS comienzan con "css =" y los localizadores XPath comienzan con "//" (el prefijo "xpath =" es opcional). Los localizadores CSS son menos detallados y más fáciles de leer y deberían preferirse a los localizadores XPath. Sin embargo, puede haber casos en los que sea necesario utilizar localizadores XPath porque un localizador CSS simplemente no puede cortarlo.

Ejecutando JavaScript

Algunos elementos requieren más que simples interacciones de mouse / teclado debido a la compleja representación realizada por ExtJS. Por ejemplo, un Ext.form.CombBox no es realmente un <select>elemento, sino una entrada de texto con una lista desplegable separada que se encuentra en la parte inferior del árbol del documento. Para simular correctamente una selección de ComboBox, es posible simular primero un clic en la flecha desplegable y luego hacer clic en la lista que aparece. Sin embargo, localizar estos elementos mediante localizadores CSS o XPath puede resultar engorroso. Una alternativa es ubicar el propio componente ComoBox y llamar a métodos en él 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 runScriptcomando se puede usar para realizar la operación anterior de 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 a que se cargue la página cuando una acción del usuario da como resultado transiciones o recargas de página. Sin embargo, dado que las recuperaciones 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 AJAX o la aparición de filas en una cuadrícula, componentes adicionales, enlaces, etc. Por ejemplo:

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

A veces, un elemento aparecerá solo después de una cierta cantidad de tiempo, dependiendo de la rapidez con que ExtJS procese los componentes después de que una acción del usuario produzca un cambio de vista. En lugar de utilizar retrasos arbitrarios con el pausecomando, el método ideal es esperar hasta que el elemento de interés esté a nuestro alcance. 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')

Confiar en pausas arbitrarias no es una buena idea 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 inestables.

Elementos en los que no se puede hacer clic

El clickcomando no puede activar algunos elementos . Esto se debe a que el detector de eventos está en realidad en el contenedor, observando los eventos del mouse en sus elementos secundarios, que finalmente llegan al padre. El control de pestañas es un ejemplo. Para hacer clic en la pestaña a, debe simular un mouseDownevento 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 (Ext.form. * Componentes) que tienen expresiones regulares o vtypes asociados para la validación activarán la validación con un cierto retraso (ver la validationDelaypropiedad que está establecida en 250ms por defecto), después de que el usuario ingresa el texto o inmediatamente cuando el campo pierde foco - o desenfoques (ver la validateOnDelaypropiedad). Para activar la validación de campo después de emitir el comando de tipo Selenium para ingresar texto dentro de un campo, debe realizar una de las siguientes acciones:

  • Activación de la validación retrasada

    ExtJS activa el temporizador de retardo de validación cuando el campo recibe eventos de activación. Para activar este temporizador, simplemente emita un evento de keyup ficticio (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 la 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")
    

Comprobación de los resultados de la 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 verificación "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 por completo del árbol DOM.

Consejos específicos de elementos

Hacer clic en un botón de formulario externo

  • Opción 1

    Comando: haga clic en Destino: css = botón: contiene ('Guardar')

    Selecciona el botón por su título

  • opcion 2

    Comando: haga clic en Destino: css = # botón guardar-opciones

    Selecciona el botón por su id.

Seleccionar un valor de un formulario ComboBox externo

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

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



5

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

Básicamente habla de usar el envío de javascript para hacer consultas y usar el método Ext.ComponentQuery.query para recuperar cosas de la misma manera que lo hace en su aplicación ext internamente. De esa manera, puede usar xtypes e itemIds y no tiene que preocuparse por tratar de analizar cualquiera de las cosas locas generadas automáticamente.

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

Podría publicar algo un poco más detallado aquí pronto, todavía estoy tratando de entender cómo hacer esto correctamente


4

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 auxiliar (en la clase SeleniumExtJsUtils, que es una colección de métodos útiles para una interacción más fácil 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("<myGridId>", 5) );

Para que esto funcione, necesito establecer mi identificación en la cuadrícula y no dejar que ExtJs genere la suya propia.


debería poder encontrar la cuadrícula usando su xtype en Ext.ComponentQuery.query (suponiendo que se haya extendido desde la cuadrícula y haya definido el xtype para su propia cuadrícula). He puesto algunas cosas sobre esto más adelante. También descubrí que puede hacer clic en cosas usando xpath para encontrar el td que contiene un valor de identificación para una fila
JonnyRaa

4

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')]"

3

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

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


3

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

HTML5 Robot se ocupa de esto mediante el uso de una serie de mejores prácticas sobre cómo buscar e interactuar de manera confiable con componentes basados ​​en atributos y condiciones que no son dinámicos. Luego proporciona atajos 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 : API familiar basada en Selenium y JUnit que ha incorporado compatibilidad con controladores web para todos los navegadores modernos.
  2. Gwen : un lenguaje de estilo humano para crear y mantener pruebas de navegador de forma rápida y sencilla, que viene con su propio entorno de desarrollo integrado. Todo lo cual se basa en la API de Java.

Por ejemplo, si desea encontrar la fila de la cuadrícula Ext JS que contiene el texto "Foo", puede 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 resultarle útil.


2

Consejos útiles para recuperar la cuadrícula a través de la identificación de la cuadrícula en la página: creo que puede ampliar una 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;
    }

2

Pruebas más fáciles 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 ninguna referencia de objeto disponible. En lugar de usar una identificación con Ext.getCmp, use itemId con Ext.container.Container.getComponent que recuperará las identificaciones de itemId. Dado que los itemId son un índice de la colección mixta interna del contenedor, el itemId tiene un alcance local en el contenedor, lo que evita posibles conflictos con Ext.ComponentManager, que requiere una identificación única.

Anulando la Ext.AbstractComponent's onBoxReadymétodo, me puse un atributo de datos personalizados (cuyo nombre proviene de mi costumbre testIdAttrpropiedad de cada componente) para el componente de itemIdvalor, si es que existe. Agregue la Testing.overrides.AbstractComponentclase a application.jsla requiresmatriz de su archivo .

/**
 * 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 representa la página. No más búsquedas a través de identificadores generados dinámicamente y no descriptivos.


2

Estamos desarrollando un marco de prueba que usa selenio y encontramos problemas con extjs (ya que es la representación del lado del cliente). Encuentro ú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");
}

1

Para una interfaz de usuario compleja que no es HTML formal, xPath siempre es algo con lo que puede contar, pero un poco complejo cuando se trata de diferentes implementaciones de interfaz de usuario utilizando ExtJs.

Puede usar Firebug y Firexpath como extensiones de Firefox para probar el xpath de un determinado elemento y simplemente pasar xpath completo como parámetro a 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);

1

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

<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for">
Name Of Needed Label
<label/>

Y hay que señalar alguna entrada WebDriver: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)].

Por lo tanto, elegirá el id del @foratributo y lo usará más. Este es probablemente el caso más simple, pero le brinda la forma de ubicar el elemento. Es mucho más difícil cuando no tienes una etiqueta, pero luego necesitas encontrar algún elemento y escribir tu xpath buscando hermanos, descender / ascender elementos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.