2
votes

What is the proper way to uncheck a checkbox in Capybara integration tests? I have a list of checkboxes that I need to uncheck. I have selected them all using all:

checkboxes = all("input[type='checkbox']")

In order uncheck each one I use each. According to the information I have found on the StackOverflow there should be three ways to uncheck a checkbox:

uncheck(field)
field.click
field.set(false)

In order to test whether these different methods I created the following tests (each of these was in a separate scenario on the same page):

Using uncheck it succeeded:

checkboxes = all("input[type='checkbox']")
checkboxes.each { |field| expect(field.checked?).to eq("checked") }
checkboxes.each { |field| uncheck(field[:id]) }
checkboxes.each { |field| expect(field.checked?).to eq(nil) }
puts page.body
save_and_open_page

Using click it failed, so I assume this is an improper way of unchecking the field:

checkboxes = all("input[type='checkbox']")
checkboxes.each { |field| expect(field.checked?).to eq("checked") }
checkboxes.each { |field| field.click }
checkboxes.each { |field| expect(field.checked?).to eq(nil) }
puts page.body
save_and_open_page

Using set(false) it succeeded:

checkboxes = all("input[type='checkbox']")
checkboxes.each { |field| expect(field.checked?).to eq("checked") }
checkboxes.each { |field| field.set(false) }
checkboxes.each { |field| expect(field.checked?).to eq(nil) }
puts page.body
save_and_open_page

I figured I would just use uncheck, but then I noticed something strange: the page.body still showed the checkbox as checked, even though I had asserted in the test that checked? was nil. save_and_open_page showed the same thing. It appeared as if the checkboxes had not changed since the page was loaded.

Questions:

  • Why aren't uncheck and set(false) altering the form HTML? In order form my test to pass some element associated with field had to be unchecked. Is there some disconnect between page.body and what uncheck deals with?
  • What is the canonical way of unchecking a checkbox? Since uncheck is part of the API and is easy to use it seems to be the proper function to call for this sort of thing, but the fact that it's not changing page.body concerns me.

Related questions:

Thanks in advance!

1

1 Answers

5
votes

Capybara implements #uncheck as find(...).set(false) so calling set(false) on an existing element or uncheck with a locator string are doing the same thing to the element, and will leave a box unchecked if it was already unchecked. If you are using a JS capable driver (not the default rack-test driver) calling #click on a checkbox will toggle the state (unless some JS is capturing/blocking the clicks), so if you want the box unchecked you want to be using uncheck or set(false)

As for why it doesn't update the html, you are experiencing the difference between html attributes and properties. Calling uncheck on the box changes the 'checked' property of the element, but that won't change the attribute (attribute remains at initial state, property is current state). That is expected and correct, the property is what gets submitted with your form.

Note: the fact that #checked? is returning "checked" rather than true for the rack_test driver is a bug, and will be fixed in the next Capybara release. You should be using something like expect(field).to be_checked rather than eq('checked') to not have issues in the future

Note: save_and_open_page - saves the html (attribute) and then loads it - it does not save the properties of elements, so it very well may show checkboxes in a state that they aren't actually in during the test