135

I'm using Rspec and Capybara.

How can I write a step to check a checkbox? I've tried check by value but it can't find my checkbox. I'm not sure what to do, as I have in fact same ID with different values

Here is the code:

 <input id="cityID" type="checkbox" style="text-align: center; opacity: 0;" value="61" name="cityID">
 <input id="cityID" type="checkbox" style="text-align: center; opacity: 0;" value="62" name="cityID">
 <input id="cityID" type="checkbox" style="text-align: center; opacity: 0;" value="63" name="cityID">
Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
John Dow
  • 1,431
  • 2
  • 11
  • 8

14 Answers14

159

I found the following worked for me:

# Check
find(:css, "#cityID[value='62']").set(true)

# Uncheck
find(:css, "#cityID[value='62']").set(false)
Jon M
  • 11,669
  • 3
  • 41
  • 47
  • @Jon M I have some odd id with empty brackets so for the check example... `find(:css, "#cityID[value='62']").set(true)` will work but `find(:css, "#cityID[][value='62']").set(true)` will not be found and fail. How do I run the same function with an empty bracket id? – TangibleDream Dec 14 '12 at 17:05
  • 1
    @TangibleDream just to clarify - are you saying the checkbox has an ID of '[]'? – Jon M Dec 14 '12 at 20:50
  • @Jin M Yes, it looks like so `` – TangibleDream Dec 14 '12 at 21:02
  • I guess you'll just need to escape the square brackets, similar to [this question](http://stackoverflow.com/questions/1239095/find-dom-element-by-id-when-id-contains-square-brackets). Maybe something like `find(:css, "#Extrapainful\\[\\][value='12345']").set(true)`. – Jon M Dec 14 '12 at 21:40
  • @Jon M arrgh \\\[\\\] throws back `findElements execution failed; Unable to locate an element with the xpath expression .//Extrapain\[\][ @value = '12345'] because of the following error: Error: INVALID_EXPRESSION_ERR: DOM XPath Exception 51 (Selenium::WebDriver ::Error::InvalidSelectorError)` – TangibleDream Dec 15 '12 at 07:16
  • on the other hand if I do \\[\\] I get... `unexpected ']' after '[' (Nokogiri::CSS::SyntaxError) (eval):3:in _racc_do_parse_c' (eval):3:in do_parse'` – TangibleDream Dec 15 '12 at 07:18
  • 1
    I couldn't find a way to make this work with the CSS selector at all! There must be some way to escape the square bracket but I couldn't find it. I had to resort to an XPath finder: `find(:xpath, ".//input[@id='Extrapainful[]'][@value='12345']").set(true)` – Jon M Dec 16 '12 at 00:53
  • This is one of those answers that works technically, but doesn't really make full use of the Capybara DSL. See the answer below – Mark Weston Apr 10 '13 at 15:59
  • @MarkWeston indeed, I have the similar experience. Using Installero answer works perfectly and seems much simpler. – rept Jul 11 '13 at 22:11
  • 3
    Can I mark this answer down... I know it works, but its counter intuitive to mark up an answer that isn't part of the simple api available: check('name, id or text here') (see answer below) – Pure Function Dec 07 '13 at 03:12
  • agree with the above -- "check" and "uncheck" are provided for this purpose. http://www.rubydoc.info/github/jnicklas/capybara/Capybara/Node/Actions#check-instance_method – Jacob Oct 23 '14 at 21:55
  • if one looks at the capybara tests https://github.com/jnicklas/capybara/blob/master/lib/capybara/spec/session/check_spec.rb and associated views https://github.com/jnicklas/capybara/blob/master/lib/capybara/spec/views/form.erb#L157 it seems clear that the way check is intended to be used is with a proper HTML label ... – Sam Joseph Jun 25 '15 at 06:28
150

It's better not to create multiple elements with the same id, so that (and not only for that) you can easily check/uncheck a checkbox with elegant

check 'cityID'
uncheck 'cityID'

If one can not avoid multiple elements with the same id and still needs to check a checkbox with certain value, he can do so with

find(:css, "#cityID[value='62']").set(true)
find(:css, "#cityID[value='62']").set(false)

More information on capybara input manipulations can be found here

installero
  • 9,096
  • 3
  • 39
  • 43
  • 2
    It's also better to not create multiple elements with the same id because it's not valid HTML. That should not be an issue if using rails form helpers properly. – ihaztehcodez Jan 06 '16 at 17:43
  • 2
    I would like to add that the checkbox/uncheck only accepts the following values: id, name or related label element. [Here](https://kolosek.com/rails-capybara-setup/) you can read more about it. – Nesha Zoric Feb 26 '18 at 12:26
60

When running capybara test, you got the page object. This you can use to check/uncheck any checkboxes. As @buruzaemon already mentioned:

to find and check a checkbox by name, id, or label text.

So lets assume you got a checkbox in your html like:

<label>  
  <input type="checkbox" value="myvalue" name="myname" id="myid">
  MyLabel
</label>

You could check this with:

page.check('myid')
page.check('MyLabel')
page.check('myname')

Uncheck is the same just use page.uncheck method.

p1100i
  • 3,710
  • 2
  • 29
  • 45
  • 1
    solid answer, seems cleaner than the accepted answer using css selectors (even if that's what the check method does underneath the covers) – agmin Jul 10 '13 at 23:15
  • 1
    Yep, this is the best answer. It is cleaner and it closely mimics the user action. It does not pollute the form with extra id's and it makes the tests easy to read. – B Seven Jun 04 '15 at 09:52
  • 1
    I agree this is cleaner. Interestingly though, the accepted answer isn't much different from how the `check` method is implemented in Capybara. `def check(locator, options={}) find(:checkbox, locator, options).set(true) end` – bigtunacan Jun 12 '15 at 15:04
  • Thanks, and this is the same answer according to the docs: http://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions#check-instance_method "Find a check box and mark it as checked. The check box can be found via name, id or label text." – Mike Vallano Feb 21 '18 at 15:11
26

I think you may have to give unique ids to your form elements, first of all.

But with regards to Capybara and checkboxes, the Capybara::Node::Actions#check instance method will allow you to find and check a checkbox by name, id, or label text.

buruzaemon
  • 3,847
  • 1
  • 23
  • 44
14

If the box is associated with text, e.g. 'Option 3', then as of capybara 3.0.3 you can just do

check 'Option 3'
Obromios
  • 15,408
  • 15
  • 72
  • 127
9

Had some issues with custom checkbox which is hidden behind label element. Needed a allow_label_click: true.

With reference to this blog post,

check 'checkbox[name]', allow_label_click: true

For cases where there is a link in your label like "I agree to terms and conditions", the above code will open the page, which is not what you want.

Do this instead.

find(:css, "#checkbox_id", visible: false).execute_script('this.checked = true')
Vic
  • 1,512
  • 17
  • 25
  • 1
    Thank you! This worked perfectly with a Boostrap 4 custom checkbox field. Also, in case you need to uncheck it, you can: uncheck 'checkbox[name]', allow_label_click: true – pastullo Jul 29 '20 at 13:57
  • Great answer! This works really well! – holyonline Feb 25 '22 at 02:53
7

I know this is an older question, but I have been working through this myself, and having tried all of the above, this is what finally worked for me:

find("input[type='checkbox'][value='#{cityID.id}']").set(true)

Hope this is helpful to someone. I am using Capybara 2.4.4.

Michael Cruz
  • 864
  • 6
  • 13
7

An old topic but another solution is:

check('Option 3', allow_label_click: true)
Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
Samuel
  • 513
  • 3
  • 17
1

you can also use :xpath instead of :css if you have some problems finding it.

find(:xpath , '//*[@id="example"]').set(true)

on Chrome (and surely other browsers), you can "inspect element" and then by right clicking on the element you are interested in, there is 'copy xpath' if you don't know what xpath was, now you do.

kulssaka
  • 226
  • 8
  • 27
1

You can also check that all the checkboxes are not checked with this example.

all('input[type=checkbox]').each do |checkbox| checkbox.should_not be_checked end

1

.set(true) didn't work for me so I had to call .click:

find(...).click

HectorPerez
  • 744
  • 6
  • 11
  • (1) I do not think `click` on its own is a valid capybara command (or at least if it is, it doesn't seem to be on the docs), and (2) if it was, it would probably toggle the checkbox, not make sure it was on or off – NotAnAmbiTurner Dec 06 '17 at 06:22
1

To select the checkbox

  check 'name_of_checkbox'
vidur punj
  • 5,019
  • 4
  • 46
  • 65
1

I believe this looks more elegant:

find_by_id("cityID").set(true)

Tested on Rails 7. Works fine.

drjorgepolanco
  • 7,479
  • 5
  • 46
  • 47
0
check find(".whenever input")[:id]

I think this will make capybara wait for any event listener attached to that input, which sometimes is a pain-in-the-ass if it doesn't waits .... If that input doesn't have an ID, choose another property (there must be one)...