Comment résoudre l'ambiguïté dans 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'ai 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 les tweets/tags à droite et les tags à gauche de la page. Par conséquent, il sera inévitable qu'une page de liens identiques apparaisse sur la même page.
Ma solution est
first(:link, link).click
au lieu de
click_link(link)
Un tel comportement de Capybara est intentionnel et je crois 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 renvoyaient le premier élément au lieu de générer une exception, mais les responsables de la maintenance de Capybara ont décidé que c’était une mauvaise idée et qu’il était préférable de le faire. Il a été décidé que dans de nombreuses situations, renvoyer le premier élément conduit à ne pas renvoyer l'élément pour lequel le développeur souhaitait être renvoyé.
La réponse la plus votée ici recommande d'utiliser first
ou all
au lieu de find
mais:
all
et first
n'attend pas que l'élément avec un tel localisateur apparaisse sur la page bien que find
attendeall(...).first
et first
ne vous protègeront pas de la possibilité qu'à l'avenir, un autre élément doté d'un tel localisateur apparaisse sur la page et que, par conséquent, vous trouviez un élément incorrect.Donc il est conseillé de choisir un autre localisateur moins ambig : par exemple, sélectionnez élément par id, par classe ou par un autre localisateur css/xpath afin qu’un seul élément y corresponde.
En guise de remarque, voici quelques localisateurs que je considère habituellement utiles pour résoudre les ambiguïtés:
find('ul > li:first-child')
C'est plus utile que first('ul > li')
car cela attendra que li
apparaisse sur la page.
click_link('Create Account', match: :first)
C'est mieux que first(:link, 'Create Account').click
car cela attendra 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
indique à Capybara de ne rechercher que les correspondances exactes, c’est-à-dire de ne pas trouver "Confirmation du mot de passe"
La solution ci-dessus fonctionne très bien, mais vous pouvez également utiliser la syntaxe suivante pour les curieux.
click_link(link_name, match: :first)
Vous pouvez trouver plus d'informations ici:
NOUVELLE RÉPONSE:
Vous pouvez essayer quelque chose comme
all('a').select {|elt| elt.text == "#tag1" }.first.click
Il y a peut-être un moyen de faire cela qui utilise mieux la syntaxe Capybara disponible - quelque chose dans le sens de all("a[text='#tag1']").first.click
mais je ne peux pas penser à la syntaxe correcte et je ne trouve pas le documentation appropriée. Cela dit, la situation est quelque peu étrange au début: avoir deux balises <a>
Avec les mêmes id
, class
et du texte. Y a-t-il une chance qu'ils soient enfants de différentes divs, puisque vous pourriez alors faire votre find
within
le segment approprié du DOM. (Cela aiderait de voir un peu de votre source HTML).
ANCIENNE REPONSE: (ou je pensais que '# tag1' signifiait que l'élément avait un id
sur "tag1")
Sur quel lien voulez-vous cliquer? Si c'est le premier (ou peu importe), vous pouvez le faire
find('#tag1').click
Sinon tu peux faire
all('#tag1')[1].click
cliquer sur le second.
Vous pouvez vous assurer que vous trouvez le premier en utilisant match
:
find('.selector', match: :first).click
Mais surtout, vous ne voulez probablement pas faire cela, car cela conduira à tests fragiles qui ignorent l'odeur de code de duplication de sortie, ce qui conduit à - faux positifs qui continuent de fonctionner alors qu'ils auraient dû échouer, car vous avez supprimé un élément correspondant, mais le test a heureusement trouvé l'autre.
Le mieux est d’utiliser within
:
within('#sidebar') do
find('.selector).click
end
Cela garantit que vous trouvez l'élément que vous espérez trouver, tout en exploitant les fonctionnalités d'attente automatique et de nouvelle tentative de Capybara (que vous perdez si vous utilisez find('.selector').click
), et cela est beaucoup plus clair. quelle est l'intention.
Pour ajouter au corpus de connaissances existant ici:
Pour les tests JS, Capybara doit conserver deux threads (un pour RSpec, un pour Rails) et un second processus (le navigateur) en synchronisation. Pour ce faire, il attend (jusqu’à concurrence du temps d’attente maximal configuré) dans la plupart des méthodes d’appariement et de recherche de nœuds.
Capybara a aussi des méthodes qui n'attendent pas, principalement Node#all
. Leur utilisation revient à dire à vos spécifications que vous souhaitez qu’elles échouent par intermittence.
La réponse acceptée suggère page.first('selector')
. Ceci n'est pas souhaitable, du moins pour les spécifications JS, car Node#first
Utilise Node#all
.
Ceci dit, Node#first
attendra si vous configurez Capybara comme suit:
# Rails_helper.rb
Capybara.wait_on_first_by_default = true
Cette option était ajouté dans Capybara 2.5. et est false par défaut.
Comme Andrei l’a mentionné, vous devriez plutôt utiliser
find('selector', match: :first)
ou changez votre sélecteur. Cela fonctionnera bien indépendamment de la configuration ou du pilote.
Pour compliquer encore les choses, dans les anciennes versions de Capybara (ou avec une option de configuration activée), #find
Ignorera l’ambiguïté et retournera simplement le premier sélecteur correspondant. Ce n’est pas terrible non plus, car cela rend vos spécifications moins explicites, ce qui, j’imagine, n’est plus le comportement par défaut. Je laisserai de côté les détails car ils ont déjà été discutés ci-dessus.
Davantage de ressources:
En raison de cet article , vous pouvez y remédier via l'option "correspondance":
Capybara.configure do |config|
config.match = :prefer_exact
end
Pour éviter une erreur ambiguë dans le concombre.
Solution 1
first("#tag1").click
Solution 2
Cucumber features/filename.feature --guess