web-dev-qa-db-fra.com

Résolution d'ambiguïté de Capybara

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.

93
neilmarion

Ma solution est

first(:link, link).click

au lieu de

click_link(link)
140
e-zinc

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:

  1. all et first n'attend pas que l'élément avec un tel localisateur apparaisse sur la page bien que find attende
  2. all(...).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"

72
Andrei Botalov

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:

http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/

25
Greg L

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 findwithin 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.

23
Amit Kumar Gupta

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.

9
TALlama

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:

6
johncip

En raison de cet article , vous pouvez y remédier via l'option "correspondance":

Capybara.configure do |config|
  config.match = :prefer_exact
end
5
Skydan

Pour éviter une erreur ambiguë dans le concombre.

Solution 1

first("#tag1").click

Solution 2

Cucumber features/filename.feature --guess
0
Aravin