capybara: matches_xxx? fails when used with elements located using sibling selectors

Capybara version 3.3.1. Right now, matches_css? expects the CSS in the first argument to match fully the given element - that is, if I have a div.a.b.c the whole CSS should be passed in order for matches_css? to return true. Passing div.a for example or div or any other combination returns false. Having this ability would be nice.

Actually, I see no other way of doing exactly that apart from page.all(css, wait: 0).include?(element) which, however, is extremely slow when css is e.g. div because a million elements are returned from the browser.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (11 by maintainers)

Most upvoted comments

@boris-petrov Thanks for reporting this, and for producing the reproducible example. Having an example of the failure makes debugging and fixing things so much easier. Will probably release a fix as 3.4.2 later today, assuming tests pass, etc.

AHA – this happens because matches_xxx? uses the current elements query_scope (the element that was the current scope when it was found) as the scope for finding all elements that match the passed CSS and then checking if the current element is one of them. This works fine when the current elements query_scope is an ancestor (which it would be most of the time), but doesn’t work when a sibling is selected since its query_scope is not an ancestor of the current element being checked. Easiest way to fix this would be to use the elements parent as the scope for searching in a matches query, need to think about whether that has any downsides.

As for the ‘div’ difference, if that’s a bug it would be a bug in selenium or chromedriver/chrome - I would expect <div class="b"></div> to report the error shown since the element takes up no space on the page and is therefore not visible and wouldn’t, by default, be located by find. With any whitespace contents of the <div> I believe it should actually behave the same way and raise the error since whitespace is stripped and Chrome still reports the element as having 0px height but the element is reported as visible. I’m guessing this may be a bug in selenium but haven’t looked in depth far enough to say that for sure - but I can say it’s not a Capybara issue.

@luke-hill - printing following_sibling prints the correct element:

#<Capybara::Node::Element tag="div" path="/HTML/BODY/DIV[2]">

In the documentation it is not written that find finds only child elements. In any case, the following also does not work:

following_sibling = sess.find('.a', text: /^text$/).sibling('div')
p following_sibling
puts following_sibling.matches_css?('div')

This also prints the same element and also false.

Thirdly, the exception that I pasted above still happens in some cases and is another bug.

Your locator is looking inside the div.a.

Inside the div.a there are no div items defined.

You want the following locator

div.a + div or div.a + .b