Comment résoudre l'ambiguïté à Capybara? Pour une raison quelconque, j'ai besoin de liens avec les mêmes valeurs dans une page mais je ne peux pas créer de test car j'obtiens l'erreur
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
La raison pour laquelle je ne peux pas éviter cela est à cause de la conception. J'essaie de recréer la page Twitter avec des tweets / tags à droite et les tags à gauche de la page. Par conséquent, il sera inévitable que des pages de liens identiques apparaissent sur la même page.
ruby-on-rails-3
rspec
capybara
Neilmarion
la source
la source
Réponses:
Ma solution est
au lieu de
la source
first
comme suggéré ci-dessus, à moins que vous ne sachiez absolument ce que vous faites, est susceptible d'entraîner des spécifications qui passent pour vous mais qui échouent dans une construction CI ou sur la machine d'un collègue.Un tel comportement de Capybara est intentionnel et je pense qu'il ne devrait pas être corrigé comme suggéré dans la plupart des autres réponses.
Les versions de Capybara antérieures à 2.0 ont renvoyé le premier élément au lieu de lever une exception, mais les responsables ultérieurs de Capybara ont décidé que c'était une mauvaise idée et qu'il était préférable de la soulever. Il a été décidé que dans de nombreuses situations, le retour du premier élément conduit à ne pas renvoyer l'élément que le développeur voulait renvoyer.
La réponse la plus votée ici recommande d'utiliser
first
ouall
au lieu defind
mais:all
etfirst
n'attendez pas que l'élément avec un tel localisateur apparaisse sur la page bienfind
qu'il attendall(...).first
etfirst
ne vous protégera pas de la situation où à l'avenir un autre élément avec un tel localisateur peut apparaître sur la page et, par conséquent, vous pouvez trouver un élément incorrectIl est donc conseillé de choisir un autre localisateur, moins ambigu : par exemple, sélectionnez un élément par id, classe ou autre localisateur css / xpath afin qu'un seul élément le corresponde.
En guise de note, voici quelques localisateurs que je considère généralement utiles pour résoudre une ambiguïté:
find('ul > li:first-child')
C'est plus utile que
first('ul > li')
d'attendre que le premierli
apparaisse sur la page.click_link('Create Account', match: :first)
C'est mieux que
first(:link, 'Create Account').click
d'attendre qu'au moins un lien Créer un compte apparaisse sur la page. Cependant, je pense qu'il est préférable de choisir un localisateur unique qui n'apparaît pas deux fois sur la page.fill_in('Password', with: 'secret', exact: true)
exact: true
dit à Capybara de ne trouver que les correspondances exactes, c'est-à-dire de ne pas trouver "Confirmation du mot de passe"la source
La solution ci-dessus fonctionne très bien, mais pour les curieux, vous pouvez également utiliser la syntaxe suivante.
Vous pouvez trouver plus d'informations ici:
http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/
la source
NOUVELLE RÉPONSE:
Vous pouvez essayer quelque chose comme
Il peut y avoir un moyen de le faire qui utilise mieux la syntaxe Capybara disponible - quelque chose du genre,
all("a[text='#tag1']").first.click
mais je ne peux pas penser à la syntaxe correcte et je ne trouve pas la documentation appropriée. Cela dit , il est un peu une situation étrange pour commencer, avec deux<a>
étiquettes avec le mêmeid
,class
et le texte. Y a-t-il une chance qu'ils soient des enfants de divs différents, puisque vous pourriez alors faire votrefind
within
segment approprié du DOM. (Il serait utile de voir un peu de votre source HTML).ANCIENNE RÉPONSE: (où je pensais que '# tag1' signifiait que l'élément avait un
id
"tag1")Sur lequel des liens voulez-vous cliquer? Si c'est le premier (ou peu importe), vous pouvez faire
Sinon tu peux faire
pour cliquer sur le second.
la source
find('#tag1')
signifie que vous ne voulez trouver qu'un seul élément avec idtag1
. Une exception est soulevée car il y a plusieurs éléments avec identifianttag1
sur la pageall(:xpath, '//a[text()="#tag1"]').first.click
.Vous pouvez vous assurer de trouver le premier en utilisant
match
:Mais surtout, vous ne voulez probablement pas faire cela , car cela conduira à des tests fragiles qui ignorent l'odeur du code de sortie en double, ce qui conduit à son tour à des faux positifs qui continuent de fonctionner alors qu'ils auraient dû échouer, car vous en avez supprimé un correspondant. élément mais le test a heureusement trouvé l'autre.
Le mieux est d'utiliser
within
:Cela garantit que vous trouvez l'élément que vous vous attendez à trouver, tout en tirant parti des capacités d'attente automatique et de relance automatique de Capybara (que vous perdez si vous utilisez
find('.selector').click
), et cela rend beaucoup plus clair l'intention.la source
Pour compléter le corpus de connaissances existant ici:
Pour les tests JS, Capybara doit garder deux threads (un pour RSpec, un pour Rails) et un second processus (le navigateur) synchronisés. Pour ce faire, il attend (jusqu'au temps d'attente maximal configuré) dans la plupart des correspondeurs et des méthodes de recherche de nœuds.
Capybara a aussi des méthodes qui n'attendent pas, principalement
Node#all
. Les utiliser, c'est comme dire à vos spécifications que vous aimeriez qu'elles échouent par intermittence.La réponse acceptée suggère
page.first('selector')
. Ceci est indésirable, au moins pour les spécifications JS, parce que lesNode#first
utilisationsNode#all
.Cela dit,
Node#first
va attendre si vous configurez Capybara comme ceci:Cette option a été ajoutée dans Capybara 2.5.0 et est fausse par défaut.
Comme Andrei l'a mentionné, vous devriez plutôt utiliser
ou modifiez votre sélecteur. L'un ou l'autre fonctionnera bien indépendamment de la configuration ou du pilote.
Pour compliquer davantage les choses, dans les anciennes versions de Capybara (ou avec une option de configuration activée),
#find
ignorera volontiers l'ambiguïté et renverra simplement le premier sélecteur correspondant. Ce n'est pas génial non plus, car cela rend vos spécifications moins explicites, ce qui, j'imagine, n'est plus le comportement par défaut. Je vais laisser de côté les détails car ils ont déjà été discutés ci-dessus.Plus de ressources:
la source
En raison de ce message , vous pouvez le corriger via l'option "match":
la source
En considérant toutes les options ci-dessus, vous pouvez également essayer ceci
Si vous utilisez du concombre, vous pouvez également suivre ceci
Vous pouvez passer le texte en tant que paramètre des étapes du scénario qui peuvent être une étape générique à réutiliser
Quelque chose comme
When a user clicks on "text" link
Et en étape de définition
When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|
De cette façon, vous pouvez réutiliser la même étape en minimisant les lignes de code et il serait facile d'écrire de nouveaux scénarios de concombre
la source
Pour éviter une erreur ambiguë dans le concombre.
Solution 1
Solution 2
la source