How to get Capybara to check a checkbox by its label

10,790

Solution 1

The html for Option 1 looks like:

<label class="control-label" for="__Option:0x000001061be660_">Option 1</label>
<div class="controls">
  <input id="options_" name="options[]" type="checkbox" value="1" />
</div>

The problem is that the label and checkbox are not properly associated with each other:

  • The label has a for attribute of "__Option:0x000001061be660_"
  • The checkbox has an id attribute of "options_"

As a result, Capybara finds the label "Option 1", but not one that is associated to a checkbox.

The page needs to be updated so that the label for attribute and checkbox id attribute match. For example:

<label class="control-label" for="__Option:0x000001061be660_">Option 1</label>
<div class="controls">
  <input id="__Option:0x000001061be660_" name="options[]" type="checkbox" value="1" />
</div>

Solution 2

You can also make Capybara find the checkbox by using check(\[locator\], options) method by passing the value of the checkbox as an option.

The code is page.check(name, option: value), where name is the name of the checkbox and value - its value.

This also should work for id instead of name.

Solution 3

You should use allow_label_click: true option

for checkboxes:

choose("mention_sentiment_positive", allow_label_click: true)

for radios

choose("mention_sentiment_positive", allow_label_click: true)

Don't know if this option was available in your days :D

Solution 4

Have you tried doing it directly based off of the input instead?

<div class="control-group offset2"> 
<label class="control-label" for="__Option:0x000001061be660_">Option 1</label>
<div class="controls">
<input id="options_" name="options[]" type="checkbox" value="1" />
/div>
</div>

If you ever want to debug further, you could add a binding.pry before it checks it, and then just run a few capybara node checking commands to see if it can actually detect the element or not.

If you don't want to use pry, you could just do a

puts all('.control-label', text: 'Option 1').size

and if it returns 1, then it's detected, and thus you could just do a

find('.control-label', text: 'Option 1').set(true)

The check method for Capybara is this:

File 'lib/capybara/node/actions.rb', line 78
def check(locator, options={})
  find(:checkbox, locator, options).set(true)
end

If it doesn't work to do it based off the label like that, you could try doing it based off of the input.

Share:
10,790
digijim
Author by

digijim

Updated on July 17, 2022

Comments

  • digijim
    digijim almost 2 years

    Capybara is not finding my checkbox's label, and I know I'm referencing it correctly by it's label. What am I doing wrong, or is this a bug in Capybara?

    According to http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions:check, "The check box can be found via name, id or label text."

    Here's the section of my request spec I ran:

    describe "with valid information" do   
    
    it_should_behave_like "all item pages"
    
    before { valid_create_item }
    
    it "should create an item" do
        expect { click_button submit }.to change(Item, :count).by(1)
    end
    
    describe "after saving the item" do
        before { click_button submit }
    
        it { should have_link('Sign out') }
        it { should have_selector('h1', text: "Items") }
        it { should have_title("Items") }
        it { should have_success_message }
    end      
    
    describe "and options selected" do
    
        before do
            puts page.html
            check('Option 1')
            click_button "Save changes"
        end
    
        it { should have_title("Items") }
        it { should have_success_message }
        it { should have_link('Sign out', href: signout_path) }
        specify { expect(item.reload.options.find_by_name("Option 1")).to eq true }
    end
    

    And here's a gist of the resulting test, including the html generated for the page: https://gist.github.com/anonymous/11270055

    According to the test results, Capybara couldn't find the checkbox labelled "Option 1", when it's clearly in the generated html.

    As a sidenote, I've also noticed I can fill in a form by it's label with Capybara only when I let the rails FormHelper display a field's default label text.

    For example: The FormHelper label text for a field called "email_address" appears as "Email address", and fill_in "Email address", with: "[email protected]" works. If I don't use the FormHelper to generate the label, and instead make the label "Email", fill_in "Email", with: "[email protected]" doesn't work, because Capybara can't find the field labeled "Email". It seems this behavior is consistent with all form elements (or at least with text fields and checkboxes -- the only ones I've tested so far).

  • digijim
    digijim about 10 years
    I'm less interested in a different technique than I am in figuring out if Capybara should be finding the checkbox the way I've written the test. "puts all('.control-label', text: 'Option 1').size" returned 2, which is weird, since there's only one in the generated html, and if two are found, Capybara2 is supposed to return an Ambiguous error, AFAIK.
  • digijim
    digijim about 10 years
    Update: I just tried "find('.control-label', text: 'Option 1').set(true)", which did return an Ambiguous match error. The html generated in the gist linked in my question clearly shows only one instance of a label 'Option 1', so I don't understand why Capybara is detecting two.
  • digijim
    digijim about 10 years
    So the statement "The check box can be found via name, id or label text" in the Capybara documentation is not entirely true. Now that I've got the label's for attribute matching the checkbox's id, check 'Option 1' works as expected. Thanks @JustinKo
  • 安杰帅
    安杰帅 about 10 years
    Hey sorry just saw this. Yes I realize that you were asking specifically for the label, I'm pretty sure Justin answered that for you. It may be that it's detecting Option 1 and Option 10.