Test with capybara and simple form won't check the checkbox - ruby-on-rails

I'm having quite a bit of trouble checking a terms of service box from simple_form in my capybara/rspec test.
Here is my validation:
validates :terms_of_service, acceptance: true, allow_nil: false
If I remove allow_nil: false then the specs all pass even if the box isn't checked. If I leave it, the validation causes the specs to fail.
Here is the code creating the form/checkbox:
= f.label :terms_of_service, "I agree to the #{link_to 'Terms of Service', terms_of_service_path, :target => "_blank"}".html_safe
= f.check_box :terms_of_service
The resulting html:
<label for="candidate_terms_of_service">I agree to the Terms of Service</label>
<input name="candidate[terms_of_service]" type="hidden" value="0">
<input id="candidate_terms_of_service" name="candidate[terms_of_service]" type="checkbox" value="1">
My attempts in my test which I've tried individually:
page.find_by_id("candidate_terms_of_service").check
find(:xpath, "//*[#id='candidate_terms_of_service']").set(true)
find(:css, "#candidate_terms_of_service").set(true)
check 'candidate[terms_of_service]'
check 'I agree to the Terms of Service'
find('#candidate_terms_of_service').check
And resulting failure:
Failure/Error: let(:candidate) { create(:candidate) }
ActiveRecord::RecordInvalid:
Validation failed: Terms of service must be accepted
How do I check this box?

This particular simple_form field gave me a lot of grief, and while I found several different ways mentioned to query and set it, none of them worked (some mentioned in this issue, others from Capybara issues).
I found the following to actually work with a simple_form boolean with RSpec:
find(:xpath, "//label[#for='candidate_terms_of_service']").click
For the sake of completeness considering the different webdrivers and total solutions given, here are the other solutions found but resulted in invalid selector errors. The input selector would actually error about the inability to check simple_form's default hidden input; this error makes sense, the label is the visible and top-most element.
find('#active_form_terms_of_service').set(true)
# or select by label
find('label[for=active_form[terms_of_service]').click
# and select by label and value if multiple boxes
# with the same ID (not sure why this would happen)
find("label[value='1']").click
# or select the input with xpath
find(:xpath, "//input[value='1']").set(true)

Try:
find('#candidate_terms_of_service').check

Related

Acceptance of Terms and Conditions

I know that there have been a few other posts on this topic, but I have tried their solutions and they don't work. I have a devise user model, and this model has a boolean called :terms_and_conditions. I want to prevent a user from signing up on my website unless they have checked this terms and conditions box. By checking the box, the user's boolean attribute should be set to true. With my current code, a user can sign up without checking the box, and checking the box does not affect the boolean field. How can I fix this?
devise/registrations/new.html.erb:
<div class="checkbox">
<label>
<%= f.check_box :terms_and_conditions %><strong> I accept the terms and conditions<strong>
</label>
</div>
models/user.rb:
validates_acceptance_of :terms_and_conditions
in models/user.rb:
validates_presence_of :terms_and_conditions
set validates :terms_of_service, acceptance: true, and see if that fixes your problem. read more about it at rails guide here

Exact match in capybara

I'm having a problem with Capybara 2.1 to match exact text.
I have a select form looking like this one :
<select class='tags-select'>
<option>Tag 1</options>
<option>Tag 2</options>
</select>
My test should select Tag 2
page.find('.tags-select', :text => 'Tag 2').click
Otherwise that keeps selecting Tag 1 even with this Capybara config in my helper :
Capybara.configure do |config|
config.match = :prefer_exact
config.exact = true
config.exact_options = true
config.ignore_hidden_elements = true
config.visible_text_only = true
config.default_wait_time = 10
end
I know that I can use some regex here to avoid the problem, but I want to understand what I'm doing wrong with that test.
The main problem, based on the query.rb file, is that the :exact option does not apply to the :text option:
def matches_filters?(node)
if options[:text]
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
return false if not node.text(visible).match(regexp)
end
So the finder:
page.click_link('id', :text => 'Password', :exact => true)
Will match both of the following elements
<a id="id" href="#">Password</a>
and
<a id="id" href="#">Password Other</a>
Your approach might also be failing because your find method is actually returning the select list rather than the option (ie I believe you are clicking the wrong element). The following will click the right option, however you would still have the exactness problem.
page.find('.tags-select option', :text => 'Tag 2').click
To address the exactness issue, you need to pass the text value to the finder without using the text option.
If you are just selecting an option, use the select method as DevDude mentioned:
page.find('.tags-select').select("Tag 2")
If you really need to do a click instead, you could do:
page.find('.tags-select').find(:option, 'Tag 2').click
The problem lies in the fact that you are using a text selector on an Element with multiple options. You should be using another specification for your selector that includes the option. Also, you should add a value for each option, as they are both equivalent (empty value). I would redo your work like this:
<select class='tags-select'>
<option value="1">Tag 1</options>
<option value="2">Tag 2</options>
</select>
And then try to select the option with this:
page.find('.tags-select').select("1")

How to simply validate a checkbox in rails

How do you simply validate that a checkbox is checked in rails?
The checkbox is for a end user agreement. And it is located in a modal window.
Lets say i have the checkbox:
<%= check_box_tag '' %>
Where and how should i validate this?
I have seen most posts about checkbox validation in rails here, but none of them suit my needs.
Adding
validates :terms_of_service, :acceptance => true
to your model should do it. Look here for more details and options.
However, if accepting the terms is not part of a form for your model, you should use client-side validations, i.e. JavaScript, like this (in jQuery):
function validateCheckbox()
{
if( $('#checkbox').attr('checked')){
alert("you have to accept the terms first");
}
}
You can add a script file to your view like this:
<%= javascript_include_tag "my_javascipt_file" %>
and trigger the function on click:
<%= submit_tag "Submit", :onclick: "validateCheckbox();" %>
EDIT: you can assign an id to your checkbox like this: check_box_tag :checkbox. The HTML will look like this: <input id="checkbox" See these examples for more options.
I was able to skip the jQuery portion and get it validation to work with this questions help. My method is below, I'm on Rails 5.1.2 & Ruby 2.4.2.
Put this code in your slim, erb or haml; note syntax may differ slightly in each.
The line below was specifically for slim.
f.check_box :terms_of_service, required: true
I used a portion of kostja's code suggestion in the model.
validates :terms_of_service, :acceptance => true
Adding on to what has been said already, if you want to add a custom error message, you can add the following to your form:
f.input :terms_of_service, as: :boolean
and then add the following to your model:
validates :terms_of_service, acceptance: { message: "must be accepted"}
Error messages will start with the field name by default followed by your custom message (e.g. Terms of service [CUSTOM MESSAGE]). Something I also found useful was to include a link to the terms of service in the label so users can easily access it to see what they are agreeing to:
f.input :terms_of_service, as: :boolean, label: "I agree to the #{link_to "terms of service", [TERMS AND CONDITIONS PATH]}".html_safe

Error Messages should Match Labels

I have a form. Many of the labels need to have a different name than they are given in the model.
attr_accessible :pin
Simpleform view:
= f.input :pin, :label => "Secret Code"
When validation fails, the error uses the model's name for the field ("Pin"), rather than the label I set in the view ("Secret Code"). The user will be confused on what field has the error.
Currently I have a helper that changes the label:
def fix_pin_errors(msg)
msg.gsub!('Pin', 'Secret Code')
end
If I am doing this for many fields, however, it becomes tedious and not DRY.
What is the best way to have errors match labels that differ from the names the model gives them?
I18n is your friend. Read this guide. It will explain how to globally convert these so they get picked up everywhere you need.
For your example, it might look something like this:
In en.yml:
en:
activerecord:
attributes:
your_model_name:
pin: 'Secret Code'

Capybara testing value of hidden field

I have a form with a hidden field that contains the current date.
I'm trying to figure out how to write a capybara finder to:
Check that the field is there
Check the value of the field
Is this possible with Capybara?
just do this:
find("#id_of_hidden_input", :visible => false).value
the matcher has_field? works with hidden fields as well. no need to do weird gymnastics with find or all in this context.
page.has_field? "label of the field", type: :hidden, with: "field value"
page.has_field? "id_of_the_field", type: :hidden, with: "field value"
the key here is setting the :type option to :hidden explicitly.
why use a label with a hidden field? this comes in handy if you're using a js library, like flatpickr, that cloaks your original text field to hide it. not coupling your behavior tests to specific markup is always a good thing.
You could also instruct Capybara to not ignore hidden elements globally in your spec_helper.rb or equivalent. The default behaviour can be overridden:
# default behavior for hidden elements
# Capybara.ignore_hidden_elements = false
# find all elements (hidden or visible)
page.all(".articles .article[id='foo']")
# find visible elements only (overwrite the standard behavior just for this query)
page.all(".articles .article[id='foo']", :visible => true)
# changing the default behavior (e.g. in your features/support/env.rb file)
Capybara.ignore_hidden_elements = true
# now the query just finds visible nodes by default
page.all(".articles .article[id='foo']")
# but you can change the default behaviour by passing the :visible option again
page.all(".articles .article[id='foo']", :visible => false)
Examples taken from this article.
its simple you can do it by using find_by_css or by using xpath as follow
page.find_by_css('#foo .bar a') #foo is the id the foo has .bar class
page.find('/table/tbody/tr[3]') #path of the element we want to find

Resources