Jak zdobyć węzeł rodzica w Kapibara?


85

Pracuję z wieloma wtyczkami jQuery, które często tworzą elementy DOM bez id lub innych właściwości identyfikacyjnych, a jedynym sposobem, aby uzyskać je w Kapibara (na przykład na kliknięcie) - jest najpierw zdobycie sąsiada (innego dziecka swojego przodka) . Ale nigdzie nie znalazłem, czy Kapibara obsługuje takie rzeczy np:

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

?


Również będzie dla mnie bardzo przydatne, jeśli powiesz, czy Kapibara generuje kliknięcia na elementach z {display: hidden} i czy jest sposób na znalezienie elementów w jakimś zakresie, gdzie display! = Hidden?
sandrew

To osobne pytanie, ale zależy to od używanego sterownika. webrat szczęśliwie znajdzie ukryte rzeczy, ale selen nie jest tak szczęśliwy, gdy kliknie na przedmioty, których nie widzisz.
jamuraa

Odpowiedzi:


111

Naprawdę uznałem, że odpowiedź jamuraa jest pomocna, ale przejście na pełną ścieżkę xpath dało mi koszmar w postaci ciągu znaków w moim przypadku, więc szczęśliwie skorzystałem z możliwości łączenia znalezisk w Kapibara, co pozwoliło mi mieszać wybór css i xpath. Twój przykład wyglądałby wtedy następująco:

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

Aktualizacja Kapibary 2.0 : find(:xpath,".//..")najprawdopodobniej spowoduje Ambiguous matchbłąd. W takim przypadku użyj first(:xpath,".//..")zamiast tego.


Dzięki za ten pomysł - nigdy bym o tym nie pomyślał! Robiłem el.parent dla elementu td, który znalazłem za pomocą find (: css), ale z jakiegoś powodu nie rozumiem, el.parent zwracał # <Capybara :: Document>. el.find (: xpath, ".// ..") z drugiej strony zwraca # <Capybara :: Element tag = "tr">, czego potrzebowałem.
Tyler Rick,

Innym sposobem rekurencyjnego znalezienia określonego węzła nadrzędnego jest użycie selektora przodków lub siebie xpath. Sprawdź stackoverflow.com/questions/1362466/ ...
vrish88

25
Nie musisz .//..szukać rodzica - ..wystarczy, i to też nigdy nie jest niejednoznaczne.
Eamon Nerbonne

ty! Wiem, że ten komentarz jest trochę spóźniony na imprezę, ale po przeczytaniu edycji i komentarz Eamona podsumowałem go w następujący sposób: el.first (: xpath, '..')
aschyiel

39

Nie da się tego zrobić w przypadku kapibary i CSS. W przeszłości używałem XPath, aby osiągnąć ten cel, który ma sposób na uzyskanie elementu nadrzędnego i jest obsługiwany przez Capybarę:

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

2
Kiedy wykonuję podobny wynik xpath, pojawia się błąd Nokogiri, który mówi, że wyrażenie jest nieprawidłowe. Dodałem gwiazdkę przed otwartym ramieniem kwadratowym w wyrażeniu, aby poprawić wyrażenie:find(:xpath, '//*[@id="some_button"]/..')
Andrew Ferk

35

Znalazłem następujące, które działają:

find(:xpath, '..')

Kapibara została zaktualizowana, aby to wspierać.

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


1
Wydaje mi się, że odpowiadasz / uzupełniasz poprzednią odpowiedź tutaj („To„ co ”nie działało?). Byłoby lepiej, gdyby była to pełna i niezależna odpowiedź. Więc polecam ci to edytować. ;-)
helenov

Potwierdzam. W najnowszej wersji Capybara 2.14.4 jest to właściwy sposób na uzyskanie węzła nadrzędnego.
fny

10

Jeśli natknąłeś się na to, próbując dowiedzieć się, jak znaleźć dowolny węzeł nadrzędny (jak w przodku ) (jak wskazano w komentarzu @ vrish88 do odpowiedzi @Pascal Lindelauf):

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

5

Ta odpowiedź dotyczy tego, jak manipulować elementem rodzeństwa, do czego, moim zdaniem, nawiązuje pierwotne pytanie

Twoja hipoteza pytania działa z niewielkimi zmianami. Jeśli dynamicznie generowane pole wygląda tak i nie ma identyfikatora:

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

Twoje zapytanie byłoby wtedy:

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

Jeśli dynamicznie generowane dane wejściowe są dołączone do elementu id (uważaj na te, choć jak w angularach, zwykle zmieniają się w oparciu o dodawanie i usuwanie elementów), byłoby to mniej więcej tak:

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

Mam nadzieję, że to pomoże.


4

Używam innego podejścia, znajdując element nadrzędny, najpierw używając tekstu w tym elemencie nadrzędnym:

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

Może jest to przydatne w podobnej sytuacji.


1
To świetna opcja. Zasięg działań interfejsu użytkownika do części strony zawierającej określony tekst jest dla mnie częstym przypadkiem użycia.
clemensp

2

Musiałem znaleźć przodka z klasą css, chociaż było to nieokreślone, jeśli docelowy przodek miał jedną lub więcej klas css, więc nie widziałem sposobu na wykonanie deterministycznego zapytania xpath. Zamiast tego opracowałem to:

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

Ostrzeżenie: używaj tego oszczędnie, może to kosztować dużo czasu w testach, więc upewnij się, że przodek jest tylko kilka przeskoków.


Przyspieszenie: zamień swoją drugą przerwę na break if ancestor[:class].split(" ").include?(css_class).
hakunin

@hakunin Ciekaw jestem, jak duże przyspieszenie widzisz? Znaczący?
kross

Nie można teraz przetestować, ale has_css wydawało się zająć sekundę, podczas gdy dopasowanie klasy wydawało się natychmiastowe.
hakunin

Kapibara ma teraz ancestor(selector)metodę zbudowany w See. Github.com/teamcapybara/capybara/commit/...
Tyler Rick

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.