Como selecionar a opção em testes de protractorjs e2e suspensos


121

Estou tentando selecionar uma opção em uma lista suspensa para os testes do e2e angular usando transferidor.

Aqui está o snippet de código da opção de seleção:

<select id="locregion" class="create_select ng-pristine ng-invalid ng-invalid-required" required="" ng-disabled="organization.id !== undefined" ng-options="o.id as o.name for o in organizations" ng-model="organization.parent_id">
    <option value="?" selected="selected"></option>
    <option value="0">Ranjans Mobile Testing</option>
    <option value="1">BeaverBox Testing</option>
    <option value="2">BadgerBox</option>
    <option value="3">CritterCase</option>
    <option value="4">BoxLox</option>
    <option value="5">BooBoBum</option>
</select>

Eu tentei:

ptor.findElement(protractor.By.css('select option:1')).click();

Isso me dá o seguinte erro:

Uma string inválida ou ilegal foi especificada Informações de compilação: versão: '2.35.0', revisão: 'c916b9d', hora: '2013-08-12 15:42:01' Informações do sistema: os.name: 'Mac OS X' , os.arch: 'x86_64', os.version: '10 .9 ', java.version:' 1.6.0_65 'Informações do driver: driver.version: desconhecido

Eu também tentei:

ptor.findElement(protractor.By.xpath('/html/body/div[2]/div/div[4]/div/div/div/div[3]/ng-include/div/div[2]/div/div/organization-form/form/div[2]/select/option[3]')).click();

Isso me dá o seguinte erro:

ElementNotVisibleError: O elemento não está visível no momento e, portanto, não pode interagir com a duração do comando ou tempo limite: 9 milissegundos Informações da versão: versão: '2.35.0', revisão: 'c916b9d', hora: '2013-08-12 15:42: 01 'Informações do sistema: os.name:' Mac OS X ', os.arch:' x86_64 ', os.version: '10 .9', java.version: '1.6.0_65' ID da sessão: bdeb8088-d8ad-0f49-aad9 -82201c45c63f Informações do driver: org.openqa.selenium.firefox.FirefoxDriver Capabilities [{platform = MAC, acceptSslCerts = true, javascriptEnabled = true, browserName = firefox, rotatable = false, locationContextEnabled = true, version = 24.0, cssSelectorsEnabled = true, banco de dados = true, handlesAlerts = true, browserConnectionEnabled = true, nativeEvents = false, webStorageEnabled = true, applicationCacheEnabled = false, takesScreenshot = true}]

Alguém pode me ajudar com este problema ou lançar alguma luz sobre o que posso estar fazendo de errado aqui.

Respostas:


88

Tive um problema semelhante e, eventualmente, escrevi uma função auxiliar que seleciona valores suspensos.

Acabei decidindo que estava bem ao selecionar por número de opção e, portanto, escrevi um método que pega um elemento e a opção Número e seleciona essa opção Número. Se o optionNumber for nulo, ele não seleciona nada (deixando a lista suspensa desmarcada).

var selectDropdownbyNum = function ( element, optionNum ) {
  if (optionNum){
    var options = element.all(by.tagName('option'))   
      .then(function(options){
        options[optionNum].click();
      });
  }
};

Eu escrevi uma postagem no blog se você quiser mais detalhes, ela também cobre a verificação do texto da opção selecionada em um menu suspenso: http://technpol.wordpress.com/2013/12/01/protractor-and-dropdowns-validation/


1
Não funcionou para mim, obtive element.findElements não é uma função.
Justin

251

Para mim funcionou como um encanto

element(by.cssContainingText('option', 'BeaverBox Testing')).click();

Espero que ajude.


2
Observação secundária - apenas v0.22 (acabei de substituir meu código por este hoje e tive que atualizar para obtê-lo)
PaulL

Existe uma maneira de direcionar os elementos usando isso? Por exemplo, menus de seleção de estado duplicados.
Christopher Marshall

11
Christopher, ElementFinderé capaz de acorrentar, então você pode fazer:element(by.css('.specific-select')).element(by.cssContainingText('option', 'BeaverBox Testing')).click();
Joel Kornbluh

6
Note que você pode obter correspondências parciais, então 'Pequeno' irá corresponder a 'Extra Pequeno'.
TrueWill

3
Para adicionar ao comentário de TrueWill: O lado negativo desta solução é que se você tiver duas opções semelhantes, ela usará a última opção encontrada - que gerou erros com base na seleção errada. O que funcionou para mim foi stackoverflow.com/a/25333326/1945990
Mike W

29

Uma abordagem elegante envolveria fazer uma abstração semelhante ao que outras associações de linguagem selenium oferecem fora da caixa (por exemplo, Selectclasse em Python ou Java).

Vamos fazer um wrapper conveniente e ocultar os detalhes de implementação dentro:

var SelectWrapper = function(selector) {
    this.webElement = element(selector);
};
SelectWrapper.prototype.getOptions = function() {
    return this.webElement.all(by.tagName('option'));
};
SelectWrapper.prototype.getSelectedOptions = function() {
    return this.webElement.all(by.css('option[selected="selected"]'));
};
SelectWrapper.prototype.selectByValue = function(value) {
    return this.webElement.all(by.css('option[value="' + value + '"]')).click();
};
SelectWrapper.prototype.selectByPartialText = function(text) {
    return this.webElement.all(by.cssContainingText('option', text)).click();   
};
SelectWrapper.prototype.selectByText = function(text) {
    return this.webElement.all(by.xpath('option[.="' + text + '"]')).click();   
};

module.exports = SelectWrapper;

Exemplo de uso (observe como é legível e fácil de usar):

var SelectWrapper  = require('select-wrapper');
var mySelect = new SelectWrapper(by.id('locregion'));

# select an option by value
mySelect.selectByValue('4');

# select by visible text
mySelect.selectByText('BoxLox');

Solução retirada do seguinte tópico: Selecione -> abstração de opção .


FYI, criou uma solicitação de recurso: Select -> option abstraction .


Por que você usa 'return' nas funções selecionadas? Isso é necessário?
Michiel

1
@Michiel bom ponto. Pode ser necessário se você quiser resolver explicitamente a promessa retornada por click(). Obrigado.
alecxe de

20
element(by.model('parent_id')).sendKeys('BKN01');

1
É o mais correto para mim. Com cssContainingText not, certifique-se de capturar o campo desejado. Pense se você tem 2 selectbox com os mesmos valores. +1
YoBre,

4
Desculpe, o que é BKN01?
Gerfried

1
@Gerfried BKN01 é o texto que você deseja selecionar no menu suspenso.
MilanYadav

@GalBracha Desculpe, não entendi
MilanYadav

Se você tiver outra opção que compartilhe o mesmo prefixo de BKN01, não funcionará. Um aleatório será escolhido.
zs2020

15

Para acessar uma opção específica, você precisa fornecer o seletor nth-child ():

ptor.findElement(protractor.By.css('select option:nth-child(1)')).click();

Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos de um autor, deixe um comentário abaixo de sua postagem.
jpw

@jpw é minha resposta errada. ou é simplesmente a formulação da minha resposta que está errada?
bekite

A formulação de uma resposta como uma pergunta é o porquê. Não sei se é a resposta correta. Se for, você deve formulá-lo como tal.
jpw

3
@bekite não funcionou para mim.Ainda recebendo o erro Elemento Erro não visível quando tentei sua sugestão
Ranjan Bhambroo

8

Foi assim que fiz minha seleção.

function switchType(typeName) {
     $('.dropdown').element(By.cssContainingText('option', typeName)).click();
};

5

Veja como eu fiz:

$('select').click();
$('select option=["' + optionInputFromFunction + '"]').click();
// This looks useless but it slows down the click event
// long enough to register a change in Angular.
browser.actions().mouseDown().mouseUp().perform();

Para esperar que angular termine o processamento pendente, use o waitForAngular()método do transferidor .
Avi Cherry

5

Experimente isso, está funcionando para mim:

element(by.model('formModel.client'))
    .all(by.tagName('option'))
    .get(120)
    .click();

4

Você pode tentar isso espero que funcione

element.all(by.id('locregion')).then(function(selectItem) {
  expect(selectItem[0].getText()).toEqual('Ranjans Mobile Testing')
  selectItem[0].click(); //will click on first item
  selectItem[3].click(); //will click on fourth item
});

4

Outra maneira de definir um elemento de opção:

var select = element(by.model('organization.parent_id'));
select.$('[value="1"]').click();

var orderTest = element (by.model ('ctrl.supplier.orderTest')) orderTest. $ ('[value = "1"]'). click (); estou recebendo um erro Por (seletor de css, [value = "3"])
Anuj KC

3

Para selecionar itens (opções) com ids exclusivos como aqui:

<select
    ng-model="foo" 
    ng-options="bar as bar.title for bar in bars track by bar.id">
</select>

Estou usando este:

element(by.css('[value="' + neededBarId+ '"]')).click();

3

Escrevemos uma biblioteca que inclui 3 maneiras de selecionar uma opção:

selectOption(option: ElementFinder |Locator | string, timeout?: number): Promise<void>

selectOptionByIndex(select: ElementFinder | Locator | string, index: number, timeout?: number): Promise<void>

selectOptionByText(select: ElementFinder | Locator | string, text: string, timeout?: number): Promise<void>

O recurso adicional dessas funções é que elas esperam que o elemento seja exibido antes que qualquer ação selectseja executada.

Você pode encontrá-lo em npm @ hetznercloud / protractor-test-helper . As tipificações para TypeScript também são fornecidas.


2

Talvez não seja super elegante, mas eficiente:

function selectOption(modelSelector, index) {
    for (var i=0; i<index; i++){
        element(by.model(modelSelector)).sendKeys("\uE015");
    }
}

Isso apenas envia a chave para o select que você deseja, no nosso caso, estamos usando modelSelector, mas obviamente você pode usar qualquer outro seletor.

Então, em meu modelo de objeto de página:

selectMyOption: function (optionNum) {
       selectOption('myOption', optionNum)
}

E do teste:

myPage.selectMyOption(1);

2

O problema é que as soluções que funcionam em caixas de seleção angulares regulares não funcionam com a seleção e a opção md Angular Material usando transferidor. Este foi postado por outro, mas funcionou para mim e não posso comentar sobre sua postagem ainda (apenas 23 pontos de repetição). Além disso, eu limpei um pouco, em vez de browser.sleep, usei browser.waitForAngular ();

element.all(by.css('md-select')).each(function (eachElement, index) {
    eachElement.click();                    // select the <select>
    browser.waitForAngular();              // wait for the renderings to take effect
    element(by.css('md-option')).click();   // select the first md-option
    browser.waitForAngular();              // wait for the renderings to take effect
});

Acabei de usar isto:element(by.model('selectModel')).click(); element(by.css('md-option[value="searchOptionValue"]')).click();
Luan Nguyen

Aqui está outra abordagem: mdSelectElement.getAttribute('aria-owns').then( function(val) { let selectMenuContainer = element(by.id(val)); selectMenuContainer.element(by.css('md-option[value="foo"]')).click(); } );
gbruins

1

Há um problema com a seleção de opções no Firefox que o hack do Droogans corrige e que desejo mencionar aqui explicitamente, esperando que isso possa evitar que alguém tenha problemas: https://github.com/angular/protractor/issues/480 .

Mesmo se seus testes estiverem passando localmente com o Firefox, você pode descobrir que eles estão falhando no CircleCI ou TravisCI ou em qualquer outra coisa que você esteja usando para CI e implantação. Estar ciente desse problema desde o início teria me poupado muito tempo :)


1

Ajudante para definir um elemento de opção:

selectDropDownByText:function(optionValue) {
            element(by.cssContainingText('option', optionValue)).click(); //optionValue: dropDownOption
        }

1

Se abaixo está o menu suspenso fornecido-

            <select ng-model="operator">
            <option value="name">Addition</option>
            <option value="age">Division</option>
            </select>

Então o código do transferidor pode ser-

        var operators=element(by.model('operator'));
    		operators.$('[value=Addition]').click();

Source- https://github.com/angular/protractor/issues/600


1

Selecione a opção por índice:

var selectDropdownElement= element(by.id('select-dropdown'));
selectDropdownElement.all(by.tagName('option'))
      .then(function (options) {
          options[0].click();
      });

1

Melhorei um pouco a solução escrita por PaulL. Em primeiro lugar, corrigi o código para ser compatível com a última API do Protractor. E então eu declaro a função na seção 'onPrepare' de um arquivo de configuração do Protractor como um membro da instância do navegador , para que possa ser referenciado a partir de qualquer especificação e2e.

  onPrepare: function() {
    browser._selectDropdownbyNum = function (element, optionNum) {
      /* A helper function to select in a dropdown control an option
      * with specified number.
      */
      return element.all(by.tagName('option')).then(
        function(options) {
          options[optionNum].click();
        });
    };
  },

1

Tenho pesquisado na rede uma resposta sobre como selecionar uma opção em um menu suspenso de modelos e usei essa combinação que me ajudou com o material Angular.

element(by.model("ModelName")).click().element(By.xpath('xpathlocation')).click();

parece que, ao lançar o código todo em uma linha, ele poderia encontrar o elemento no menu suspenso.

Demorou muito para essa solução, espero que isso ajude alguém.


0

Queríamos usar a solução elegante usando o material angularjs, mas ela não funcionou porque não há, na verdade, nenhuma tag de opção / md-opção no DOM até que o md-select tenha sido clicado. Portanto, a maneira "elegante" não funcionou para nós (observe o material angular!) Aqui está o que fizemos por ela, não sei se é a melhor maneira, mas está definitivamente funcionando agora

element.all(by.css('md-select')).each(function (eachElement, index) {
    eachElement.click();                    // select the <select>
    browser.driver.sleep(500);              // wait for the renderings to take effect
    element(by.css('md-option')).click();   // select the first md-option
    browser.driver.sleep(500);              // wait for the renderings to take effect
});

Precisávamos ter 4 selects selecionados e enquanto o select estiver aberto, há uma sobreposição na forma de selecionar o próximo select. é por isso que precisamos esperar 500ms para garantir que não tenhamos problemas com os efeitos materiais ainda em ação.


0

Outra maneira de definir um elemento de opção:

var setOption = function(optionToSelect) {

    var select = element(by.id('locregion'));
    select.click();
    select.all(by.tagName('option')).filter(function(elem, index) {
        return elem.getText().then(function(text) {
            return text === optionToSelect;
        });
    }).then(function(filteredElements){
        filteredElements[0].click();
    });
};

// using the function
setOption('BeaverBox Testing');

0
----------
element.all(by.id('locregion')).then(function(Item)
{
 // Item[x] = > // x is [0,1,2,3]element you want to click
  Item[0].click(); //first item

  Item[3].click();     // fourth item
  expect(Item[0].getText()).toEqual('Ranjans Mobile Testing')


});

0

Você pode selecionar opções suspensas por valor: $('#locregion').$('[value="1"]').click();


0

Aqui está como fazer isso por valor de opção ou índice . Este exemplo é um pouco grosseiro, mas mostra como fazer o que você quer:

html:

<mat-form-field id="your-id">
    <mat-select>
        <mat-option [value]="1">1</mat-option>
        <mat-option [value]="2">2</mat-option>
    </mat-select>
</mat-form-field>

ts:

function selectOptionByOptionValue(selectFormFieldElementId, valueToFind) {

  const formField = element(by.id(selectFormFieldElementId));
  formField.click().then(() => {

    formField.element(by.tagName('mat-select'))
      .getAttribute('aria-owns').then((optionIdsString: string) => {
        const optionIds = optionIdsString.split(' ');    

        for (let optionId of optionIds) {
          const option = element(by.id(optionId));
          option.getText().then((text) => {
            if (text === valueToFind) {
              option.click();
            }
          });
        }
      });
  });
}

function selectOptionByOptionIndex(selectFormFieldElementId, index) {

  const formField = element(by.id(selectFormFieldElementId));
  formField.click().then(() => {

    formField.element(by.tagName('mat-select'))
      .getAttribute('aria-owns').then((optionIdsString: string) => {
        const optionIds = optionIdsString.split(' ');

        const optionId = optionIds[index];
        const option = element(by.id(optionId));
        option.click();
      });
  });
}

selectOptionByOptionValue('your-id', '1'); //selects first option
selectOptionByOptionIndex('your-id', 1); //selects second option

0
static selectDropdownValue(dropDownLocator,dropDownListLocator,dropDownValue){
    let ListVal ='';
    WebLibraryUtils.getElement('xpath',dropDownLocator).click()
      WebLibraryUtils.getElements('xpath',dropDownListLocator).then(function(selectItem){
        if(selectItem.length>0)
        {
            for( let i =0;i<=selectItem.length;i++)
               {
                   if(selectItem[i]==dropDownValue)
                   {
                       console.log(selectItem[i])
                       selectItem[i].click();
                   }
               }            
        }

    })

}

0

Podemos criar uma classe DropDown personalizada para isso e adicionar um método como:

async selectSingleValue(value: string) {
        await this.element.element(by.xpath('.//option[normalize-space(.)=\'' + value + '\']')).click();
    }

Além disso, para verificar qual valor está selecionado atualmente, podemos ter:

async getSelectedValues() {
        return await this.element.$('option:checked').getText();
    }

0

O exemplo abaixo é a maneira mais fácil. Eu testei e passei na versão do transferidor5.4.2

//Drop down selection  using option's visibility text 

 element(by.model('currency')).element(by.css("[value='Dollar']")).click();
 Or use this, it   $ isshort form for  .By.css
  element(by.model('currency')).$('[value="Dollar"]').click();

//To select using index

var select = element(by.id('userSelect'));
select.$('[value="1"]').click(); // To select using the index .$ means a shortcut to .By.css

Código completo

describe('Protractor Demo App', function() {

  it('should have a title', function() {

     browser.driver.get('http://www.way2automation.com/angularjs-protractor/banking/#/');
    expect(browser.getTitle()).toEqual('Protractor practice website - Banking App');
    element(by.buttonText('Bank Manager Login')).click();
    element(by.buttonText('Open Account')).click();

    //Drop down selection  using option's visibility text 
  element(by.model('currency')).element(by.css("[value='Dollar']")).click();

    //This is a short form. $ in short form for  .By.css
    // element(by.model('currency')).$('[value="Dollar"]').click();

    //To select using index
    var select = element(by.id('userSelect'));
    select.$('[value="1"]').click(); // To select using the index .$ means a shortcut to .By.css
    element(by.buttonText("Process")).click();
    browser.sleep(7500);// wait in miliseconds
    browser.switchTo().alert().accept();

  });
});

0

Esta é uma resposta simples de uma linha na qual o angular tem um localizador especial que pode ajudar a selecionar e indexar da lista.

element.all(by.options('o.id as o.name for o in organizations')).get(Index).click()

Embora esse código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade de sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo à pergunta para os leitores no futuro, não apenas para a pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam. Da avaliação
bipe duplo

-1

Selecione a opção por propriedade CSS

element(by.model("organization.parent_id")).element(by.css("[value='1']")).click();

ou

element(by.css("#locregion")).element(by.css("[value='1']")).click();

Onde locregion (id), organization.parent_id (nome do modelo) são os atributos do elemento selecionado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.