How to select date from a select box using Capybara in Rails 3?
Asked Answered
T

11

23

I'm writing a spec for a controller in Rails 3 project using RSpec and Capybara, and I want to select current date from a select box. I tried:

select Date.today, :from => 'Date of birth'

but the spec fails and I get error:

Failure/Error: select Date.today, :from => 'Date of birth' NoMethodError: undefined method `to_xpath' for Mon, 18 Jul 2011:Date

How to fix it?

P.S. In view file I use simple_form_for tag and the select box is generated by code:

f.input :date_of_birth
Turnbow answered 18/7, 2011 at 7:24 Comment(0)
S
22

You need to specify the exact value as it's in the select menu in html. So if your select has values like "2011/01/01" then you need to write:

select '2011/01/01', :from => 'Date of birth'

Your code fails because you pass a date object.

Sacramental answered 18/7, 2011 at 14:9 Comment(2)
It doesn't work. I get error: Failure/Error: select '2011/07/18', :from => 'Date of birth' Capybara::ElementNotFound: cannot select option, no option with text '2011/07/18' in select box 'Date of birth' By the way, Rails generates 3 select boxes for each date part.Turnbow
Then you need to select 3 options from 3 select boxes and use the same values as you see on the page.Sacramental
B
26

Had the same problem. I googled at lot and solved it this way:

  1. Wrote date select macros into /spec/request_macros.rb The select_by_id method is necessary for me, because the month is dependent on the translation

    module RequestMacros
      def select_by_id(id, options = {})
        field = options[:from]
        option_xpath = "//*[@id='#{field}']/option[#{id}]"
        option_text = find(:xpath, option_xpath).text
        select option_text, :from => field
      end
    
      def select_date(date, options = {})
        field = options[:from]
        select date.year.to_s,   :from => "#{field}_1i"
        select_by_id date.month, :from => "#{field}_2i"
        select date.day.to_s,    :from => "#{field}_3i"  
      end
    end
    
  2. Added them to my /spec/spec_helper.rb

    config.include RequestMacros, :type => :request
    

Now in my integration tests in spec/requests i can use

select_date attr[:birthday], :from => "user_birthday"

Thanks to http://jasonneylon.wordpress.com/2011/02/16/selecting-from-a-dropdown-generically-with-capybara/ and https://gist.github.com/558786 :)

Brusa answered 22/2, 2012 at 15:59 Comment(0)
S
22

You need to specify the exact value as it's in the select menu in html. So if your select has values like "2011/01/01" then you need to write:

select '2011/01/01', :from => 'Date of birth'

Your code fails because you pass a date object.

Sacramental answered 18/7, 2011 at 14:9 Comment(2)
It doesn't work. I get error: Failure/Error: select '2011/07/18', :from => 'Date of birth' Capybara::ElementNotFound: cannot select option, no option with text '2011/07/18' in select box 'Date of birth' By the way, Rails generates 3 select boxes for each date part.Turnbow
Then you need to select 3 options from 3 select boxes and use the same values as you see on the page.Sacramental
P
9

with credit to Markus Hartmair for an excellent solution, I prefer to use labels as selectors because of improved readability. So my version of his helper module is:

module SelectDateHelper
  def select_date(date, options = {})
    field = options[:from]
    base_id = find(:xpath, ".//label[contains(.,'#{field}')]")[:for]
    year, month, day = date.split(',')
    select year,  :from => "#{base_id}_1i"
    select month, :from => "#{base_id}_2i"
    select day,   :from => "#{base_id}_3i"
  end
end

call it like this:

select_date "2012,Jan,1", :from => "From date"
Polak answered 27/5, 2013 at 2:9 Comment(1)
I had to use find(:xpath, ".//label[contains(.,'#{field}')]")[:for][0..-4] and Date::MONTHNAMES[date.month]Metonymy
T
9

I found a clean solution for rspec and capybara to test using date and time select methods, where in your HTML you use a datetime select or date select. This works with Rails 4, RSpec 3.1 and Capybara 2.4.4.

Say in your HTML form you have the following:

<%= f.datetime_select(:start_date, {default: DateTime.now, prompt: {day: 'Choose day', month: "Choose month", year: "Choose year"}}, {class: "date-select"}) %>

the DateTime Select View helper will create 5 select fields with ids such as id="modelname_start_date_1i", where the id the is appended with 1i, 2i, 3i, 4i, 5i. By default Year, Month, Day, Hour, Minute. If you change the order of the fields, make sure to change the feature helper below.

1) Create a Feature Helper for dates and times helpers

spec/support/helpers/date_time_select_helpers.rb

module Features
  module DateTimeSelectHelpers

    def select_date_and_time(date, options = {})
      field = options[:from]
      select date.strftime('%Y'),  :from => "#{field}_1i" #year
      select date.strftime('%B'),  :from => "#{field}_2i" #month
      select date.strftime('%-d'), :from => "#{field}_3i" #day 
      select date.strftime('%H'),  :from => "#{field}_4i" #hour
      select date.strftime('%M'),  :from => "#{field}_5i" #minute
    end

    def select_date(date, options = {})
      field = options[:from]
      select date.strftime('%Y'),  :from => "#{field}_1i" #year
      select date.strftime('%B'),  :from => "#{field}_2i" #month
      select date.strftime('%-d'), :from => "#{field}_3i" #day 
    end
  end
end 

Note that for the day I use %-d that gives you a non-padded numeric value (i.e. 4) instead of %d that has a zero-padded numeric value (i.e. 04). Check the date formats with strftime

2) You then need to include your date and time helpers methods in spec/support/helpers.rb so you can use them in any spec file.

require 'support/helpers/date_time_select_helpers'
RSpec.configure do |config|
  config.include Features::DateTimeSelectHelpers, type: :feature
end

3) In your Spec file you can call your helper. For example:

feature 'New Post' do
  scenario 'Add a post' do
    visit new_post_path
    fill_in "post[name]", with: "My post"
    select_date_and_time(2.days.from_now, from:"post_start_date")
    click_button "Submit"
    expect(page).to have_content "Your post was successfully saved"
  end
end
Transfigure answered 2/12, 2014 at 7:41 Comment(0)
N
7

A slight adaption of Markus's answer:

def select_date(date, options = {})  
  raise ArgumentError, 'from is a required option' if options[:from].blank?
  field = options[:from].to_s
  select date.year.to_s,               :from => "#{field}_1i"
  select Date::MONTHNAMES[date.month], :from => "#{field}_2i"
  select date.day.to_s,                :from => "#{field}_3i"
end
Nurse answered 8/5, 2012 at 9:51 Comment(0)
M
2

Thanks to Dylan for pointing it out, but in case anyone is looking for the cucumber version, you can use this:

select_date("Date of birth", :with => "1/1/2011")

For more information, see select_date.

Mingo answered 6/2, 2012 at 3:48 Comment(1)
This is a link to the cucumber method. The question is asking about using capybara with rspec.Swor
R
1

Given the following Formtastic code renders Rails default date selector:

= f.input :born_on, end_year: Time.now.year, start_year: 60.years.ago.year

In your spec, break the date into separate calls to each individual select tag:

select '1956', from: 'person_born_on_1i'
select 'July', from: 'person_born_on_2i'
select '9', from: 'person_born_on_3i'

I don't like that this code is so aware of the HTML, but it does work with the versions of gems at this time.

Gems:

  • Capybara 2.1.0
  • Formtastic 2.2.1
  • Rails 3.2.13
  • RSpec 2.13.0
Riplex answered 7/5, 2013 at 21:42 Comment(0)
C
1

In my particular situation, I'm adding potentially multiple date select fields to the page with accepts_nested_attributes_for functionality. This means, I'm not sure what the full id or name of the fields are going to be.

Here's the solution I came up with in case it helps anyone else Googling this:

I'm wrapping the date select field in a container div with a class:

<div class='date-of-birth-container'>
  <%= f.date_select :date_of_birth %>
</div>

Then in my feature spec:

within '.date-of-birth-container' do
  find("option[value='1']", text: 'January').select_option
  find("option[value='1']", text: '1').select_option
  find("option[value='1955']").select_option
end

Here's a helper method I wrote for it:

def select_date_within_css_selector(date, css_selector)
  month_name = Date::MONTHNAMES.fetch(date.month)
  within css_selector do
    find("option[value='#{date.month}']", text: month_name).select_option
    find("option[value='#{date.day}']", text: date.day.to_s).select_option
    find("option[value='#{date.year}']").select_option
  end
end

Then using the helper:

select_date_within_css_selector(Date.new(1955, 1, 1), '.date-of-birth-container')
Cosmism answered 16/9, 2014 at 18:20 Comment(0)
D
0

The following worked for me, using a date_field:

fill_in "Date", with: DateTime.now.strftime('%m/%d/%Y')
Donalddonaldson answered 27/10, 2015 at 6:23 Comment(0)
M
0

For Rails 4, in case somebody gets to this question without being limited to Rails 3.

  select '2020',  from: 'field_name_{}_1i'
  select 'January',  from: 'field_name_{}_2i'
  select '1', from: 'field_name_{}_3i'

You can of course extract this to a helper and make it dynamic.

Marven answered 9/3, 2018 at 10:14 Comment(0)
C
-1

It looks like this one has been sufficiently covered, but see Capybara's docs for an official answer. You can select by name, id, or label text.

Coo answered 4/2, 2013 at 17:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.