Pergunta

Estamos usando o selênio com grande sucesso aos testes website de alto nível pega (além de extensas doctests python em um nível de módulo). No entanto, agora estamos usando extjs para um monte de páginas e seu provando difícil incorporar testes Selenium para os componentes complexos, como grades.

Alguém já teve sucesso escrever testes automatizados para páginas web extjs baseada? Muita googling encontra pessoas com problemas semelhantes, mas poucas respostas. Obrigado!

Foi útil?

Solução

O maior obstáculo no teste ExtJS com selênio é que ExtJS não processar elementos padrão HTML e Selenium IDE vai ingenuamente (e justamente) gerar comandos dirigidos a elementos que apenas agem como decoração - elementos supérfluos que ExtJS ajudar com o toda desktop look-and-feel. Aqui estão algumas dicas e truques que eu recolhi ao escrever automatizado de teste de selênio contra um app ExtJS.

Dicas gerais

Localizando Elements

Ao gerar casos de teste de selênio por gravar ações do usuário com Selenium IDE no Firefox, selênio irá basear as ações gravadas sobre os ids dos elementos HTML. No entanto, para a maioria dos elementos clicáveis, ExtJS usa gerado ids como "ext-gen-345", que são susceptíveis de alteração em uma visita posterior à mesma página, mesmo se nenhuma alteração de código foram feitas. Depois de gravar ações do usuário para um teste, é preciso haver um esforço manual para passar por todas essas ações que dependem de ids gerados e para substituí-los. Existem dois tipos de substituições que podem ser feitas:

Substituir um Id Locator com uma CSS ou XPath Locator

localizadores CSS começam com "css =" e localizadores XPath começam com "//" (o "xpath =" prefixo é opcional). localizadores CSS são menos detalhadas e são mais fáceis de ler e deve ser preferido sobre localizadores XPath. No entanto, pode haver casos em que localizadores XPath precisam ser usado porque um localizador CSS simplesmente não pode cortá-la.

Executando JavaScript

Alguns elementos exigem mais do que as interações do mouse / teclado simples devido ao processamento complexo realizado pela ExtJS. Por exemplo, um Ext.form.CombBox não é realmente um elemento <select> mas uma entrada de texto com uma lista drop-down destacou que está em algum lugar no fundo da árvore do documento. A fim de simular adequadamente uma seleção de caixa de combinação, é possível primeiro simular um clique no drop-down arrow e então clique na lista que aparece. No entanto, localizar estes elementos através de CSS ou XPath localizadores pode ser complicado. Uma alternativa é localizar o próprio componente ComoBox e chamar métodos nele para simular a seleção:

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

Em Selenium o comando runScript pode ser usado para executar a operação acima de uma forma mais concisa:

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

Lidar com AJAX e Rendering lenta

O selênio tem "* AndWait" sabores para todos os comandos para à espera de carregamento da página quando um usuário ação resulta em transições de página ou recarrega. No entanto, desde buscas AJAX não envolvem cargas de página reais, esses comandos não pode ser usado para sincronização. A solução é fazer uso de pistas visuais como a presença / ausência de um indicador de progresso AJAX ou a aparência de linhas em uma grade, componentes adicionais, links etc. Por exemplo:

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

Às vezes, um elemento aparecerá somente após um determinado período de tempo, dependendo de como ExtJS rápido torna componentes após um usuário ação resulta em uma mudança vista. Em vez de usar atrasos arbitrários com o comando pause, o método ideal é esperar até que o elemento de interesse vem ao nosso alcance. Por exemplo, para clicar em um item depois de esperar para que ele apareça:

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

Baseando-se em pausas arbitrárias não é uma boa idéia, pois diferenças temporárias que resultam da execução dos testes em diferentes navegadores ou em diferentes máquinas farão os casos de teste escamosa.

Os produtos não-clicáveis ??

Alguns elementos podem não ser desencadeada pelo comando click. É porque o ouvinte de evento é realmente no recipiente, olhando para os eventos do mouse sobre seus elementos filho, essa bolha, eventualmente, até o pai. O controlo de separador é um exemplo. Para clicar sobre o um guia, você tem que simular um evento mouseDown no rótulo da guia:

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

Validação de campo

Os campos do formulário (Ext.form. * Componentes) que associou expressões regulares ou vtypes para validação irá tvalidação rigger com um certo atraso (ver a propriedade validationDelay que é definido para 250ms por padrão), depois que o usuário insere texto ou imediatamente quando o campo perde o foco - ou borrões (veja a propriedade validateOnDelay). A fim de validação de campo gatilho após emitir o comando Tipo de selênio para entrar algum texto dentro de um campo, você tem que fazer um dos seguintes procedimentos:

  • Disparo Retardado Validação

    ExtJS incêndios fora o temporizador de validação quando o campo recebe keyup eventos. Para acionar este temporizador, simplesmente emitir um evento fictício keyup (não importa qual chave você usar como ExtJS ignora-lo), seguido de uma curta pausa que é maior que o validationDelay:

    Command: keyUp
    Target: someTextArea
    Value: x
    Command: pause
    Target: 500
    
  • Triggering Imediato Validação

    Você pode injetar um evento de borrão para o campo para acionar a validação imediata:

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

Verificação de Validação resultados

Após a validação, você pode verificar a presença ou ausência de um campo de erro:

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

Note que o "display: none". Verificação é necessária porque uma vez que um campo de erro é mostrado e, em seguida, ele precisa ser escondido, ExtJS vai simplesmente campo hide erro em vez de removê-lo inteiramente a partir da árvore DOM

Dicas específicos do elemento

clicar em um Ext.form.Button

  • Opção 1

    Comando: clique Alvo: css = botão: contém ( 'Save')

    Seleciona o botão por sua legenda

  • Opção 2

    Comando: clique botão css = # save-opções

    : Alvo

    Seleciona o botão por seu id

Selecionando um valor a partir de um Ext.form.ComboBox

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

Primeiro define o valor e, em seguida, dispara explicitamente o evento select no caso de haver observadores.

Outras dicas

Este blogue me ajudou muito. Ele escreveu bastante sobre o assunto e parece que o seu ainda ativo. O cara também parece apreciar um bom design.

Ele basicamente fala sobre usando o envio de javascript para fazer consultas e utilizando o método Ext.ComponentQuery.query para recuperar coisas da mesma forma que você faz em seu aplicativo ext internamente. Dessa forma, você pode usar xtypes e itemIds e não precisa se preocupar em tentar analisar qualquer uma das coisas gerado automaticamente louco.

este artigo em particular muito útil.

Pode postar algo um pouco mais detalhado aqui em breve - ainda tentando colocar minha cabeça em torno de como fazer isso corretamente

Eu tenho vindo a testar a minha aplicação web ExtJs com selênio. Um dos maiores problemas foi selecionar um item na grade, a fim de fazer algo com ele.

Por isso, eu escrevi ajudante método (na classe SeleniumExtJsUtils que é uma coleção de métodos úteis para a interação mais fácil com 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)";
}

e quando eu precisava para selecionar uma linha, eu tinha acabado de chamar:

selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );

Para que isso funcione eu preciso definir o meu id no grid e não deixar ExtJs gerar o seu próprio.

Para detectar esse elemento é visível que você use a cláusula: not(contains(@style, "display: none")

É melhor usar este:

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

Você pode fornecer mais detalhes sobre os tipos de problemas você está tendo com o teste extjs?

Uma extensão selênio acho útil é waitForCondition . Se o seu problema parece ser um problema com os eventos do Ajax, você pode usar waitForCondition que esperar para os eventos a acontecer.

páginas da web Ext JS pode ser complicado de teste, por causa do HTML complicado acabam gerando como com grades Ext JS.

HTML5 Robot lida com isso usando uma série de melhores práticas de como a pesquisa de forma confiável e interagir com os componentes com base em atributos e em condições que não são dinâmicos. Em seguida, fornece atalhos para fazer isso com todo o HTML, Ext JS e componentes Sencha Touch que você precisa para interagir com. Ele vem em 2 sabores:

  1. Java -. Selênio Familiar e JUnit API base que foi construído em suporte de driver web para todos os navegadores modernos
  2. Gwen - A linguagem de estilo humano de forma rápida e fácil criação e manutenção de testes do navegador, que vem com seu próprio ambiente de desenvolvimento integrado. Tudo o que se baseia na API Java.

Por exemplo, se você estava querendo encontrar a linha de grade Ext JS contendo o texto "Foo", você poderia fazer o seguinte em Java:

findExtJsGridRow("Foo");

... e você poderia fazer o seguinte no Gwen:

extjsgridrow by text "Foo"

Há uma grande quantidade de documentação tanto para Java e Gwen para saber como trabalho com componentes específicos Ext JS. A documentação também detalha o HTML resultante de todos estes componentes Ext JS, que você também pode achar útil.

Dicas úteis para buscar grade via Id de grade na página: Eu acho que você pode estender a função mais útil a partir desta 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;
    }

Mais fácil de testes através de costume HTML data- atributos

A partir da documentação Sencha :

Um itemId pode ser usado como uma forma alternativa de obter uma referência a um componente quando nenhum objeto de referência está disponível. Em vez de usar um id com Ext.getCmp, use itemId com Ext.container.Container.getComponent que irá recuperar da itemId ou id do. Desde de ItemId são um índice para MixedCollection interna do recipiente, o itemId tem como escopo localmente para o recipiente -. Evitando potenciais conflitos com Ext.ComponentManager que requer uma identificação única

Substituir método Ext.AbstractComponent do onBoxReady, eu definir um atributo de dados personalizado (cujo nome vem da minha propriedade testIdAttr costume de cada componente) para o valor itemId do componente, se ele existir. Adicione a classe Testing.overrides.AbstractComponent a variedade application.js do seu arquivo requires.

/**
 * 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 fornece aos desenvolvedores uma maneira de reutilizar um identificador descritivo dentro de seu código e ter esses identificadores disponíveis cada vez que a página é processada. Não há mais procura através da não-descritivo, ids geradas dinamicamente.

Estamos desenvolvendo um framework de testes que usos selênio e problemas encontrados com extjs (desde a sua composição do lado do cliente). Acho que é útil para procurar um elemento uma vez que o DOM está pronto.

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 UI complexa que não é HTML formal, XPath é sempre algo que você pode contar, mas um pouco complexa quando se trata de diferentes implementação UI usando ExtJs.

Você pode usar o Firebug e Firexpath como extensões do Firefox para testar xpath um certo elemento, e passe simples XPath completo como parâmetro para selênio.

Por exemplo, no 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);

Quando eu estava testando aplicação ExtJS usando WebDriver eu usei seguinte abordagem: Eu olhei para o campo de texto do rótulo e atributo @for tenho de etiqueta. Por exemplo, temos um rótulo

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

E precisamos ponto WebDriver alguma entrada: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]

.

Assim, ele vai pegar o id do atributo @for e usá-lo ainda mais. Este é provavelmente o caso mais simples, mas dá-lhe o caminho para localizar elemento. É muito mais difícil quando você não tem rótulo, mas então você precisa encontrar algum elemento e escrever seu xpath procurando irmãos, descer / elementos ascender.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top