94

I'm working with many jQuery plugins, that often create DOM elements without id or other identification properties, and the only way to get them in Capybara (for clicking for example) - is to get their neighbor (another child of its ancestor) first. But I didn't find anywhere, does Capybara support such things for example:

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

?

Rimian
  • 32,654
  • 13
  • 106
  • 109
sandrew
  • 2,980
  • 4
  • 17
  • 28
  • Also it will be very useful for me, if you tell, does Capybara generate click on elements with { display: hidden }, and is there a way to find elements in some scope, where display != hidden ? – sandrew Feb 01 '11 at 11:51
  • This is a separate question, but it depends on the driver that you're using. webrat will find hidden things happily, but selenium is not as happy to click on items that you can't see. – jamuraa Feb 01 '11 at 13:54

9 Answers9

114

I really found jamuraa's answer helpful, but going for full xpath gave me a nightmare of a string in my case, so I happily made use of the ability to concatenate find's in Capybara, allowing me to mix css and xpath selection. Your example would then look like this:

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

Capybara 2.0 update: find(:xpath,".//..") will most likely result in an Ambiguous match error. In that case, use first(:xpath,".//..") instead.

Pascal Lindelauf
  • 4,454
  • 4
  • 36
  • 51
  • Thanks for that idea -- I never would have thought of that! I was doing el.parent for a td element I found with find(:css), but for some reason I don't understand, el.parent was returning #. el.find(:xpath,".//..") on the other hand returns #, which is what I needed. – Tyler Rick Dec 07 '11 at 19:07
  • Another way to recursively find a certain parent node is with xpath's ancestor-or-self selector. Check it out http://stackoverflow.com/questions/1362466/xpath-recursive-ancestor-search – vrish88 May 07 '12 at 23:05
  • 26
    You don't need `.//..` to find the parent - just `..` suffices, and that's never ambiguous either. – Eamon Nerbonne Aug 02 '13 at 09:28
  • ty! I know this comment is a little late to the party, but I condensed it down to the following after reading the edit, and Eamon's comment: el.first(:xpath, '..') – aschyiel Sep 05 '14 at 01:13
39

There isn't a way to do this with capybara and CSS. I've used XPath in the past to accomplish this goal though, which does have a way to get the parent element and is supported by Capybara:

find(:xpath, '//*[@id="some_button"]/..').fill_in "Name:", :with => name
sj26
  • 6,370
  • 2
  • 23
  • 24
jamuraa
  • 3,409
  • 23
  • 29
  • 2
    When i do a similar xpath find, I get a Nokogiri error saying the expression is invalid. I added an asterisk infront of the open square braket in the expression to fix the expression: `find(:xpath, '//*[@id="some_button"]/..')` – Andrew Ferk Jun 28 '12 at 20:28
38

I found the following that does work:

find(:xpath, '..')

Capybara has been updated to support this.

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

Mike Campbell
  • 7,663
  • 2
  • 34
  • 50
B Seven
  • 39,557
  • 59
  • 208
  • 346
  • 1
    It seems to me you are responding/completing a previous answer here ("This" what "wasn't working"?). It would be better if it were a complete and independent answer. So I recommend you to edit it. ;-) – helenov Oct 19 '16 at 16:12
  • Can confirm. With latest Capybara 2.14.4, this is the proper way to get the parent node. – fny Jul 25 '17 at 00:07
11

If you stumbled across this trying to figure out how to find any parent (as in ancestor) node (as hinted at in @vrish88's comment on @Pascal Lindelauf's answer):

find('#some_button').find(:xpath, 'ancestor::div[@id="some_div_id"]')
Ben Alavi
  • 382
  • 3
  • 7
7

This answer pertains to how to manipulate a sibling element which is what I believe the original question is alluding to

Your question hypothesis works with a minor tweak. If the dynamically generated field looks like this and does not have an id:

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

Your query would then be:

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

If the dynamically generated input does come attached with an id element (be careful with these though as in angular, they are wont to change based on adding and deleting elements) it would be something like this:

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

Hope that helps.

T. Slater
  • 71
  • 1
  • 3
4

I'm using a different approach by finding the parent element first using the text within this parent element:

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

Maybe this is useful in a similiar situation.

Harm de Wit
  • 2,060
  • 2
  • 18
  • 24
  • 1
    This is a great option. Scoping ui actions to a part of the page that contains some particular text is a common use-case for me. – clemensp Apr 04 '16 at 18:02
2

I needed to find an ancestor with a css class, though it was indeterminate if it the target ancestor had one or more css classes present, so I didn't see a way to make a deterministic xpath query. I worked this up instead:

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

Warning: use this sparingly, it could cost you a lot of time in tests so make sure the ancestor is just a few hops away.

kross
  • 3,575
  • 2
  • 27
  • 54
  • Speedup: replace your second break with `break if ancestor[:class].split(" ").include?(css_class)`. – hakunin Nov 12 '15 at 10:16
  • @hakunin I'm curious how much of a speedup are you seeing? Significant? – kross Nov 12 '15 at 17:57
  • Can't test now, but has_css seemed to take a second, while matching the class seemed immediate. – hakunin Nov 14 '15 at 14:34
  • Capybara now has an `ancestor(selector)` method built in. See https://github.com/teamcapybara/capybara/commit/548c8c00a6656520b751f0c02c0a69cfb68912bd – Tyler Rick Jul 17 '19 at 18:21
1

As mentioned in comment by @Tyler Rick Capybara in these days have methods[ ancestor(selector) and sibling(selector)

Foton
  • 1,057
  • 12
  • 20
-5

Here it is

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

There is a parent method present;)