Como faço para resolver a ambigüidade na Capivara? Por algum motivo, preciso de links com os mesmos valores em uma página, mas não consigo criar um teste, pois recebo o erro
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
A razão pela qual não posso evitar isso é por causa do design. Estou tentando recriar a página do Twitter com tweets / tags à direita e as tags à esquerda da página. Portanto, será inevitável que a página de links idênticos apareça na mesma página.
ruby-on-rails-3
rspec
capybara
Neilmarion
fonte
fonte
Respostas:
Minha solução é
ao invés de
fonte
first
como sugerido acima, a menos que você saiba absolutamente o que está fazendo, provavelmente resultará em especificações que são aprovadas para você, mas falham em um build de CI ou na máquina de um colega.Tal comportamento da Capivara é intencional e acredito que não deva ser corrigido como sugerido na maioria das outras respostas.
Versões do Capybara anteriores à 2.0 retornavam o primeiro elemento em vez de gerar exceção, mas os mantenedores posteriores do Capybara decidiram que é uma má ideia e é melhor aumentá-la. Foi decidido que em muitas situações o retorno do primeiro elemento leva a não retornar o elemento que o desenvolvedor queria que fosse retornado.
A resposta mais votada aqui recomendo usar
first
ou emall
vez defind
mas:all
efirst
não espere até que o elemento com tal localizador apareça na página, emborafind
espereall(...).first
efirst
não o protegerá de situações em que, no futuro, outro elemento com esse localizador possa aparecer na página e, como resultado, você poderá encontrar o elemento incorretoPortanto , é aconselhável escolher outro localizador menos ambíguo : por exemplo, selecione o elemento por id, classe ou outro localizador css / xpath para que apenas um elemento corresponda a ele.
Como observação, aqui estão alguns localizadores que geralmente considero úteis para resolver ambiguidades:
find('ul > li:first-child')
É mais útil do
first('ul > li')
que esperar até que o primeiroli
apareça na página.click_link('Create Account', match: :first)
É melhor do
first(:link, 'Create Account').click
que esperar até que pelo menos um link Criar conta apareça na página. No entanto, acredito que seja melhor escolher um localizador exclusivo que não apareça na página duas vezes.fill_in('Password', with: 'secret', exact: true)
exact: true
diz a Capivara para encontrar apenas correspondências exatas, ou seja, não encontrar "Confirmação de senha"fonte
A solução acima funciona muito bem, mas para os curiosos, você também pode usar a seguinte sintaxe.
Você pode encontrar mais informações aqui:
http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/
fonte
NOVA RESPOSTA:
Você pode tentar algo como
Pode haver uma maneira de fazer isso que faz melhor uso da sintaxe Capybara disponível - algo parecido com,
all("a[text='#tag1']").first.click
mas não consigo pensar na sintaxe correta de improviso e não consigo encontrar a documentação apropriada. Dito isto, é um pouco de uma situação estranha para começar, ter duas<a>
marcas com o mesmoid
,class
e texto. Existe alguma chance de eles serem filhos de divs diferentes, já que você poderia então fazer seufind
within
segmento apropriado do DOM. (Seria bom ver um pouco do código-fonte HTML).RESPOSTA ANTIGA: (onde eu pensei que '# tag1' significava que o elemento tinha
id
"tag1")Em qual dos links você deseja clicar? Se for o primeiro (ou não importa), você pode fazer
Caso contrário, você pode fazer
para clicar no segundo.
fonte
find('#tag1')
significa que você deseja encontrar apenas um elemento com idtag1
. A exceção é levantada porque há vários elementos com idtag1
na páginaall(:xpath, '//a[text()="#tag1"]').first.click
.Você pode garantir que encontrará o primeiro usando
match
:Mas o mais importante, você provavelmente não quer fazer isso , pois isso levará a testes frágeis que estão ignorando o cheiro de código de saída duplicada, o que por sua vez leva a falsos positivos que continuam funcionando quando deveriam ter falhado, porque você removeu um correspondente elemento, mas o teste felizmente encontrou o outro.
A melhor aposta é usar
within
:Isso garante que você está encontrando o elemento que espera encontrar, enquanto ainda aproveita os recursos de espera automática e repetição automática do Capivara (que você perde se usar
find('.selector').click
), e torna muito mais claro qual é a intenção.fonte
Para adicionar ao corpo de conhecimento existente aqui:
Para os testes de JS, Capybara deve manter dois threads (um para RSpec, um para Rails) e um segundo processo (o navegador) em sincronia. Ele faz isso aguardando (até o tempo de espera máximo configurado) na maioria dos comparadores e métodos de localização de nós.
Capivara também tem métodos que não esperam, principalmente
Node#all
. Usá-los é como dizer às suas especificações que você gostaria que falhassem intermitentemente.A resposta aceita sugere
page.first('selector')
. Isso é indesejável, pelo menos para especificações JS, porqueNode#first
usaNode#all
.Dito isso,
Node#first
vou esperar se você configurar Capivara assim:Esta opção foi adicionada no Capybara 2.5.0 e é falsa por padrão.
Como Andrei mencionou, você deve usar
ou mude seu seletor. Ambos funcionarão bem, independentemente da configuração ou driver.
Para complicar ainda mais as coisas, em versões antigas do Capybara (ou com uma opção de configuração habilitada),
#find
felizmente ignorará a ambigüidade e apenas retornará o primeiro seletor correspondente. Isso também não é ótimo, pois torna suas especificações menos explícitas, o que imagino ser o motivo pelo qual não é mais o comportamento padrão. Vou omitir os detalhes porque já foram discutidos acima.Mais recursos:
fonte
Devido a esta postagem , você pode corrigi-lo através da opção "combinar":
fonte
Considerando todas as opções acima, você também pode tentar isso
Se você estiver usando pepino, também pode seguir
Você pode passar o texto como um parâmetro das etapas do cenário que podem ser uma etapa genérica para reutilizar novamente
Algo como
When a user clicks on "text" link
E na definição da etapa
When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|
Dessa forma, você pode reutilizar a mesma etapa, minimizando as linhas de código e seria fácil escrever novos cenários de pepino
fonte
Para evitar erros ambíguos no pepino.
Solução 1
Solução 2
fonte