2

I've got an issue with a method shared across a wide number of integration tests.

The problem is, I need to find one of two buttons, and have so far only come up with the following unwieldy syntax for avoiding Capybara's ElementNotFound error:

new_button = begin
      find(".button_one")
    rescue Capybara::ElementNotFound
      begin
        find('.button_two')
      rescue Capybara::ElementNotFound
        raise Capybara::ElementNotFound, "Missing Button"
      end
    end
new_button.click

This works as expected: if the first button's not found, the second one is, and they're clicked. If neither are present, the error is raised.

Despite this, I really don't like the nested rescues and would like to tidy this up.

The simplest solution which feels like it should exist, though I've not found this anywhere: does anyone know if there's an option to return nil in Capybara's find method, rather than raising the exception?

For example, the following pseudocode...

new_button = find('.button_one', allow_nil: true) || find('.button_two', allow_nil: true)
new_button ? new_button.click : raise(Capybara::ElementNotFound, "Missing Button")

...would be perfect.

Otherwise, any suggestion of how best to rescue the two errors and avoid the horrible nested rescue?


Footnote: this code exists within a large existing structure, which previously worked fine where it shouldn't have. Fixing another issue has caused this problem, which is used widely throughout the suite. I'd love to adjust the calls and use the correct elements (and so avoid this altogether), though that's going to be a big project a little later in the day.

SRack
  • 11,495
  • 5
  • 47
  • 60

3 Answers3

3

If only one of the buttons is ever on the page, the simplest solution is to just look for both buttons at once using the CSS comma

find(".button_one, .button_two").click

If it's possible for both buttons to be on the page at the same time then that will get you an Ambiguous match error, in which case you could do something like

find(".button_one, .button_two", match: :first).click

or

all(".button_one, .button_two")[0].click

It's also possible to check whether an element exists without raising an exception using the Capybara provided predicates has_css?/has_xpath?/etc. which would give code like

if page.has_css?(".button_one")
  find(".button_one")
else
  find(".button_two")
end.click

but in this case using the CSS comma would definitely be the better solution.

Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
2

I'm not familiar with Ruby, so I'll leave the link to Ruby docu and Capybara docu. The idea is to use find_elements instead of find_element. Why? find_elements won't throw any exception if there is no elements found. The pseudo code would be like this:

new_button = find_elements('.button_one').size() > 0 ? find('.button_one') || find('.button_two')

And you don't need to handle with exceptions anymore.

Andrei Suvorkov
  • 5,559
  • 5
  • 22
  • 48
  • Very handy to know - am I right that `find_elements` comes with Selenium? Thanks for the answer. I've gone with the `css` approach, though this is definitely useful too +1 – SRack Jul 18 '18 at 14:45
  • 1
    `am I right that find_elements comes with Selenium?` So far I know, yes. @ThomasWalpole has provided elegant solution, you are right – Andrei Suvorkov Jul 18 '18 at 15:06
2

Try with the x-path //button[contains(@class,'button_one') or contains(@class, 'button_two'] as given below,

new_button = begin
        find(:xpath, "//button[contains(@class,'button_one') or contains(@class, 'button_two']")
       rescue Capybara::ElementNotFound
        raise Capybara::ElementNotFound, "Missing Button"
      end

new_button.click
Murthi
  • 5,299
  • 1
  • 10
  • 15
  • 2
    Using XPath with Capybara is generally only necessary if one needs to do text content matching (that can't be done with the :text option), if you need to search up the document, or you need to escape the current scope. Your example would work but is just more complicated than necessary. Your example also inadvertently/mistakenly escapes the current scope - https://github.com/teamcapybara/capybara#beware-the-xpath--trap - which can lead to hard to debug errors. – Thomas Walpole Jul 18 '18 at 13:56
  • Thanks @Murthi - appreciate the suggestion. Have gone with the `css` approach, but +1 as this will likely come in handy in future. – SRack Jul 18 '18 at 14:43