Wie bekomme ich einen übergeordneten Knoten in Capybara?

85

Ich arbeite mit vielen jQuery-Plugins, die häufig DOM-Elemente ohne ID oder andere Identifikationseigenschaften erstellen. Die einzige Möglichkeit, sie in Capybara abzurufen (zum Beispiel zum Klicken), besteht darin, zuerst ihren Nachbarn (ein anderes Kind seines Vorfahren) abzurufen . Aber ich habe nirgendwo etwas gefunden, unterstützt Capybara solche Dinge zum Beispiel:

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

?

Sandrew
quelle
Außerdem ist es für mich sehr nützlich, wenn Sie sagen, dass Capybara Klickelemente mit {display: hidden} generiert, und gibt es eine Möglichkeit, Elemente in einem bestimmten Bereich zu finden, in dem display! = Hidden?
Sandrew
Dies ist eine separate Frage, die jedoch vom verwendeten Treiber abhängt. Webrat findet versteckte Dinge glücklich, aber Selen klickt nicht so gerne auf Gegenstände, die Sie nicht sehen können.
Jamuraa

Antworten:

111

Ich fand Jamuraas Antwort wirklich hilfreich, aber wenn ich mich für den vollständigen xpath entschied, bekam ich in meinem Fall den Albtraum einer Zeichenfolge. Daher nutzte ich glücklich die Möglichkeit, Funde in Capybara zu verketten, um CSS- und xpath-Auswahl zu mischen. Ihr Beispiel würde dann so aussehen:

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

Capybara 2.0-Update : find(:xpath,".//..")führt höchstwahrscheinlich zu einem Ambiguous matchFehler. Verwenden Sie in diesem Fall first(:xpath,".//..")stattdessen.

Pascal Lindelauf
quelle
Danke für diese Idee - daran hätte ich nie gedacht! Ich habe el.parent für ein td-Element ausgeführt, das ich mit find (: css) gefunden habe, aber aus irgendeinem Grund, den ich nicht verstehe, hat el.parent # <Capybara :: Document> zurückgegeben. el.find (: xpath, ".// ..") gibt andererseits # <Capybara :: Element tag = "tr"> zurück, was ich brauchte.
Tyler Rick
Eine andere Möglichkeit, einen bestimmten übergeordneten Knoten rekursiv zu finden, ist die Auswahl von xpaths Vorfahren oder Selbst. Überprüfen Sie es aus stackoverflow.com/questions/1362466/…
vrish88
25
Sie müssen .//..den Elternteil nicht finden - ..reicht nur aus, und das ist auch nie mehrdeutig.
Eamon Nerbonne
ty! Ich weiß, dass dieser Kommentar für die Party etwas spät ist, aber ich habe ihn nach dem Lesen der Bearbeitung und Eamons Kommentar auf Folgendes reduziert: el.first (: xpath, '..')
aschyiel
39

Mit Capybara und CSS gibt es keine Möglichkeit, dies zu tun. Ich habe in der Vergangenheit XPath verwendet, um dieses Ziel zu erreichen, das eine Möglichkeit bietet, das übergeordnete Element abzurufen, und das von Capybara unterstützt wird:

find(:xpath, '//*[@id="some_button"]/..').fill_in "Name:", :with => name
Jamuraa
quelle
2
Wenn ich eine ähnliche xpath-Suche durchführe, erhalte ich einen Nokogiri-Fehler, der besagt, dass der Ausdruck ungültig ist. Ich habe ein Sternchen vor der offenen eckigen Klammer in den Ausdruck eingefügt, um den Ausdruck zu korrigieren:find(:xpath, '//*[@id="some_button"]/..')
Andrew Ferk
35

Ich habe folgendes gefunden, das funktioniert:

find(:xpath, '..')

Capybara wurde aktualisiert, um dies zu unterstützen.

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

B Sieben
quelle
1
Mir scheint, Sie antworten / vervollständigen hier eine vorherige Antwort ("Dieses", was "hat nicht funktioniert"?). Es wäre besser, wenn es eine vollständige und unabhängige Antwort wäre. Ich empfehle Ihnen daher, es zu bearbeiten. ;-)
Helenov
Kann bestätigen. Mit der neuesten Version von Capybara 2.14.4 ist dies der richtige Weg, um den übergeordneten Knoten abzurufen.
25.
10

Wenn Sie über diesen Versuch gestolpert sind, herauszufinden, wie Sie einen übergeordneten Knoten (wie im Vorfahren ) finden können (wie in @ vrish88s Kommentar zu @Pascal Lindelaufs Antwort angedeutet):

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

Diese Antwort bezieht sich darauf, wie man ein Geschwisterelement manipuliert, worauf die ursprüngliche Frage meines Erachtens anspielt

Ihre Fragenhypothese funktioniert mit einer kleinen Änderung. Wenn das dynamisch generierte Feld so aussieht und keine ID hat:

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

Ihre Anfrage wäre dann:

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

Wenn die dynamisch generierte Eingabe mit einem ID-Element verbunden ist (seien Sie vorsichtig mit diesen, obwohl sie sich wie im Winkel ändern, wenn sie Elemente hinzufügen und löschen), wäre dies ungefähr so:

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

Hoffentlich hilft das.

T. Slater
quelle
4

Ich verwende einen anderen Ansatz, indem ich das übergeordnete Element zuerst anhand des Textes in diesem übergeordneten Element finde:

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

Vielleicht ist dies in einer ähnlichen Situation nützlich.

Harm de Wit
quelle
1
Dies ist eine großartige Option. Das Festlegen von UI-Aktionen auf einen Teil der Seite, der einen bestimmten Text enthält, ist für mich ein häufiger Anwendungsfall.
Clemensp
2

Ich musste einen Vorfahren mit einer CSS-Klasse finden, obwohl es unbestimmt war, ob auf dem Zielvorfahren eine oder mehrere CSS-Klassen vorhanden waren, sodass ich keine Möglichkeit sah, eine deterministische xpath-Abfrage durchzuführen. Ich habe das stattdessen aufgearbeitet:

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

Warnung: Verwenden Sie diese Option sparsam, da dies bei Tests viel Zeit kosten kann. Stellen Sie daher sicher, dass der Vorfahr nur wenige Schritte entfernt ist.

kross
quelle
Beschleunigung: Ersetzen Sie Ihre zweite Pause durch break if ancestor[:class].split(" ").include?(css_class).
Hakunin
@hakunin Ich bin neugierig, wie viel Beschleunigung du siehst? Von Bedeutung?
Kross
Kann jetzt nicht testen, aber has_css schien eine Sekunde zu dauern, während die Übereinstimmung mit der Klasse sofort schien.
Hakunin
Capybara hat jetzt eine ancestor(selector)eingebaute Methode. Siehe github.com/teamcapybara/capybara/commit/…
Tyler Rick
-5

Hier ist es

http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Base:parent

Es ist eine übergeordnete Methode vorhanden;)

Dmitriy Konovalov
quelle
5
Dies gibt nur das Dokument der obersten Ebene für mich zurück. Ich bin mir nicht sicher, ob es sich um ein Treiberproblem handelt oder was.
Nerdmaster
Wenn Sie die Dokumente lesen, sieht es so aus, als würde dies eher das übergeordnete "Frame" als das übergeordnete Element bedeuten.
Nick