Selecionando uma opção suspensa com webdriverjs

Tenho trabalhado em um projeto AngularJS recentemente, usando o Protractor para fazer testes de ponta a ponta. Protractor usa webdriverjs para conduzir o navegador, que não tem muita documentação e seus recursos embutidos são bastante primitivos – que é uma das razões pelas quais webdriver em outras linguagens frequentemente tem uma camada de abstração construída sobre ele ( Capybara etc).

Uma coisa que achei muito mais difícil do que deveria é clicar em um item de uma caixa suspensa / lista de seleção, então compartilharei o código aqui:

function selectOption(selector, item){
var selectList, desiredOption;

selectList
= this.findElement(selector);
selectList
.click();

selectList
.findElements(protractor.By.tagName('option'))
.then(function findMatchingOption(options){
options
.some(function(option){
option
.getText().then(function doesOptionMatch(text){
if (item === text){
desiredOption
= option;
return true;
}
});
});
})
.then(function clickOption(){
if (desiredOption){
desiredOption
.click();
}
});
}

Esta é uma função de seleção de item que posso usar assim:

var browser = protractor.getInstance();
browser
.selectOption = selectOption.bind(browser);
browser
.selectOption(protractor.By.id('my-dropdown'), 'My Value');

O ponto principal aqui é que deve haver um segundo “então”. Embora você possa clicar em uma opção na função doesOptionMatch, clicar em uma opção oculta o restante das opções na página, o que as torna obsoletas para o webdriverjs. Isso fará com que uma exceção seja lançada se a função doesOptionMatch ainda estiver em execução usando uma das outras opções. O segundo “então” espera que o primeiro seja concluído para que não tenhamos esse problema.

Outra coisa que vale a pena destacar é que se você fizer uma busca de elementos por nome de tag “opção” no nível do navegador, ele retornará as opções para cada menu suspenso na página, mesmo se elas não estiverem visíveis. Isso parece um pouco inconsistente com o acima – onde as opções ocultas se tornam obsoletas – e é a razão pela qual o código faz os findElements no próprio menu suspenso, em vez do navegador.