Exact match in capybara - ruby-on-rails

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")

Related

Test with capybara and simple form won't check the checkbox

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

How do I select an element from the grouped selection by its value?

The problem I'm having (and I've tried all the solutions on the interwebs) is how to select an element from a grouped select with capybara.
Here's the dropdown:
<%= f.grouped_collection_select(:subcategory_id, Category.order(:name), :subcategories, :name, :id, :name, {}, { :class=> "form-control" }) %>
Here's one of the ways I've tried to select it.
select("Ortodoncista", from: 'provider[subcategory_id]')
The error
Unable to find option "Ortodoncista" (Capybara::ElementNotFound)
This answer worked for me Capybara: Select an option by value not text by #d_rail
You create a helper first. I put this helper in spec/support/utilities.rb
def select_by_value(id, value)
option_xpath = "//*[#id='#{id}']/option[#value='#{value}']"
option = find(:xpath, option_xpath).text
select(option, :from => id)
end
Then to use it:
select_by_value "select_id", "select_option"
In my case, the select tag has the id user_category and the option I wanted to select was Musician. So my example was
select_by_value "user_category", "Musician"
As I am more familiar with CSS selection, I would use:
This to find option element by its value, that correspond to the result of the option_key_method (:id), on a child of your collection (Categories):
page.find('select#your-selectbox-id option[value="your-value"]')
This will return the desired capybara element found by your-value instead of searching by the text of the option. Then you can do whatever you want. for example: .text, or .click.
Or (just to remember) like this, if you want to select a given option from your selecbox with capybara:
select 'Option Label', :from => 'Selectbox Label Text'
wich, in your case would be the result of the option_value_method (:name) on a child of your collection (Categories).
Obs: reference for grouped selection

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

haml select_tag with constants

I'm new to Ruby and Haml, so I"m going around in circles on this. Googling isn't giving me any sample code I can use.
I can use the select_tag and populate the list from a table. But I can't figure out how to use a simple static list of items. Can someone change this to be proper Haml? Note: the source table is 'email' and the field is 'status'.
= select_tag(:email, :status, {"canceled", "pending", "success"})
I'm looking to get a dropdown list that just has the items "canceled, pending, success" in it.
The error I get is odd number list for Hash._hamlout.format_script...
Update: I found some sample code that seemed to be what I need, and it doesn't give any errors, but the dropdown box is empty:
= select_tag(:email, :status,{ "canceled" => "1", "pending" => "2", "success"=>"3"})
Here is the HTML it produces:
<select female="2" male="1" id="email" name="email">status </select >
You are using the tag helper rather than the object-oriented helper. Use select
I'd also recommend using options_for_select. Like so:
= select(:email, :status, options_for_select([["canceled", "1"], ["pending", "2"], ["success", "3"]]))
See:
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select
Got it working! I need to use "Select" instead of "Select_tag". :-)
= select(:email, :status,{ "canceled" => "canceled", "pending" => "pending", "success"=>"success"})

Empty attribute with Ruby HAML

I'm implementing Schema microformats on a Ruby project using HAML and can't figure out how to set an empty attribute on a tag. I tried nil and false, but they simply do not shown.
Example: <div itemscope>
I'm tring to set an empty itemscope attribute.
Code added from comment by #StrangeElement:
My code:
.agency.premium{:itemscope => true, :itemtype => 'schema.org/ProfessionalService';}
:itemscope => true seems to be the recommended approach from HAML's documentation. I get the same result as I would get with :itemscope => '', a XHTML-valid attribute with an empty value (i.e. <div itemscope="">).
Probably fine, but I'd rather have it empty as is documented in the Schema doc.
Using something like
%div{:itemscope => true}
is the correct way to specify this in your Haml file.
How this is rendered depends on how you set Haml's format option. The default in Haml 3.1 is xhtml, and with that it will render as itemprop='itemprop', which is valid xhtml. To render with minimized attributes (like <div itemscope>) you need to set the format to html4 or html5. (In Rails 3 the default is html5, and in Haml 4.0 the default is html5).
How to set the Haml options depends on how you are using it, see the options section in the docs.
For example, using Haml directly in Ruby, this:
engine = Haml::Engine.new '%div{:itemscope => true}'
puts engine.render
produces the default xhtml with full attributes:
<div itemscope='itemscope'></div>
But this:
engine = Haml::Engine.new '%div{:itemscope => true}', :format => :html5
puts engine.render
produces the desired result with minimized attributes:
<div itemscope></div>
If someone is interested in how to put more words in that way, he may use "foo bar" => true:
%option{ "disabled selected value" => true } Choose an option
results is:
<option disabled="" selected="" value="">Choose an option</option>
and works as expected.
The accepted answer works, but it produces an HTML attribute with value.
If you want the attribute only to be output on HTML, without value, you can use the HTML-style attributes syntax of HAML:
%div(itemscope)

Resources