Comment obtenir le nœud parent à Capybara?


85

Je travaille avec de nombreux plugins jQuery, qui créent souvent des éléments DOM sans identifiant ou autres propriétés d'identification, et le seul moyen de les obtenir dans Capybara (pour cliquer par exemple) - est d'obtenir d'abord leur voisin (un autre enfant de son ancêtre) . Mais je n'ai trouvé nulle part, est-ce que Capybara prend en charge de telles choses par exemple:

find('#some_button').parent.fill_in "Name:", :with => name

?


Cela me sera également très utile, si vous le dites, Capybara génère-t-il des clics sur des éléments avec {display: hidden}, et y a-t-il un moyen de trouver des éléments dans une certaine portée, où display! = Hidden?
sandrew

C'est une question distincte, mais cela dépend du pilote que vous utilisez. webrat trouvera les choses cachées avec bonheur, mais le sélénium n'est pas aussi heureux de cliquer sur des éléments que vous ne pouvez pas voir.
jamuraa

Réponses:


111

J'ai vraiment trouvé la réponse de jamuraa utile, mais opter pour un xpath complet m'a donné un cauchemar d'une chaîne dans mon cas, alors j'ai utilisé avec plaisir la capacité de concaténer les find dans Capybara, me permettant de mélanger la sélection css et xpath. Votre exemple ressemblerait alors à ceci:

find('#some_button').find(:xpath,".//..").fill_in "Name:", :with => name

Mise à jour Capybara 2.0 : find(:xpath,".//..")entraînera très probablement une Ambiguous matcherreur. Dans ce cas, utilisez à la first(:xpath,".//..")place.


Merci pour cette idée - je n'aurais jamais pensé à ça! Je faisais el.parent pour un élément td que j'ai trouvé avec find (: css), mais pour une raison que je ne comprends pas, el.parent retournait # <Capybara :: Document>. el.find (: xpath, ".// ..") d'autre part renvoie # <Capybara :: Element tag = "tr">, ce dont j'avais besoin.
Tyler Rick

Une autre façon de trouver récursivement un certain nœud parent consiste à utiliser le sélecteur ancêtre ou soi de xpath. Découvrez stackoverflow.com/questions/1362466/…
vrish88

25
Vous n'avez pas besoin .//..de trouver le parent - cela ..suffit, et ce n'est jamais non plus ambigu.
Eamon Nerbonne

ty! Je sais que ce commentaire est un peu en retard à la fête, mais je l'ai condensé à ce qui suit après avoir lu la modification et le commentaire d'Eamon: el.first (: xpath, '..')
aschyiel

39

Il n'y a pas moyen de faire cela avec capybara et CSS. J'ai cependant utilisé XPath dans le passé pour atteindre cet objectif, qui a un moyen d'obtenir l'élément parent et est pris en charge par Capybara:

find(:xpath, '//*[@id="some_button"]/..').fill_in "Name:", :with => name

2
Lorsque je fais une recherche xpath similaire, j'obtiens une erreur Nokogiri indiquant que l'expression n'est pas valide. J'ai ajouté un astérisque devant le crochet carré ouvert dans l'expression pour corriger l'expression:find(:xpath, '//*[@id="some_button"]/..')
Andrew Ferk

35

J'ai trouvé ce qui suit qui fonctionne:

find(:xpath, '..')

Capybara a été mis à jour pour prendre en charge cela.

https://github.com/jnicklas/capybara/pull/505


1
Il me semble que vous répondez / complétez une réponse précédente ici ("Ce" ce qui "ne fonctionnait pas"?). Ce serait mieux si c'était une réponse complète et indépendante. Je vous recommande donc de le modifier. ;-)
helenov

Peut confirmer. Avec la dernière version de Capybara 2.14.4, c'est la bonne façon d'obtenir le nœud parent.
fny

10

Si vous êtes tombé sur ceci en essayant de trouver un nœud parent (comme dans l' ancêtre ) (comme indiqué dans le commentaire de @ vrish88 sur la réponse de @Pascal Lindelauf):

find('#some_button').find(:xpath, 'ancestor::div[@id="some_div_id"]')

5

Cette réponse concerne la manière de manipuler un élément frère, ce à quoi je pense que la question originale fait allusion

Votre hypothèse de question fonctionne avec une modification mineure. Si le champ généré dynamiquement ressemble à ceci et n'a pas d'identifiant:

<div>
  <input></input>
  <button>Test</button>
</div>

Votre requête serait alors:

find('button', text: 'Test').find(:xpath, "..").find('input').set('whatever')

Si l'entrée générée dynamiquement est jointe avec un élément id (soyez prudent avec ceux-ci bien que comme en angulaire, ils ne changeront pas en fonction de l'ajout et de la suppression d'éléments), ce serait quelque chose comme ceci:

find('button', text: 'Test').find(:xpath, "..").fill_in('#input_1', with: 'whatever')

J'espère que ça t'as aidé.


4

J'utilise une approche différente en recherchant d'abord l'élément parent en utilisant le texte dans cet élément parent:

find("<parent element>", text: "text within your #some_button").fill_in "Name:", with: name

Peut-être que cela est utile dans une situation similaire.


1
C'est une excellente option. Étendre les actions de l'interface utilisateur à une partie de la page qui contient un texte particulier est un cas d'utilisation courant pour moi.
clemensp

2

J'avais besoin de trouver un ancêtre avec une classe css, même s'il était indéterminé si l'ancêtre cible avait une ou plusieurs classes css présentes, donc je ne voyais pas de moyen de faire une requête xpath déterministe. J'ai travaillé ceci à la place:

def find_ancestor_with_class(field, cssClass)
  ancestor = field
  loop do
    ancestor = ancestor.find(:xpath, '..')
    break if ancestor.nil?

    break if ancestor.has_css? cssClass
  end

  ancestor
end

Attention: utilisez-le avec parcimonie, cela pourrait vous coûter beaucoup de temps dans les tests alors assurez-vous que l'ancêtre n'est qu'à quelques sauts.


Speedup: remplacez votre deuxième pause par break if ancestor[:class].split(" ").include?(css_class).
hakunin

@hakunin Je suis curieux de savoir à quel point vous voyez une accélération? Important?
kross

Impossible de tester maintenant, mais has_css a semblé prendre une seconde, alors que la correspondance avec la classe semblait immédiate.
hakunin

Capybara a maintenant une ancestor(selector)méthode intégrée. Voir github.com/teamcapybara/capybara/commit/…
Tyler Rick

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.