Skip to content

Commit

Permalink
Merge pull request #535 from mgrunberg/add-date-input-support
Browse files Browse the repository at this point in the history
Add support for alternative select boxes date field helpers
  • Loading branch information
luke-hill authored Mar 16, 2022
2 parents 1e919ed + 409152f commit 5bfb421
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on how to contribute to Cucumber.

### Fixed

*
* Date input support in Rails 7 ([#535](https://github.com/cucumber/cucumber-rails/pull/535) [mgrunberg])

## [v2.5.0](https://github.com/cucumber/cucumber-rails/compare/v2.4.0...v2.5.0) (2022-03-07)

Expand Down
47 changes: 47 additions & 0 deletions features/capybara_javascript_drivers.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,53 @@ Feature: Capybara Javascript Drivers
6 steps (6 passed)
"""

Scenario: Support non HTML5 date inputs
Given I have created a new Rails app and installed cucumber-rails
And I force selenium to run Firefox in headless mode
When I run `bundle exec rails g scaffold appointment name:string when:datetime`
And I force "app/views/appointments/_form.html.erb" to use select boxes for dates
And I write to "features/create_appointment.feature" with:
"""
@javascript
Feature: Create appointments
Scenario: Create an appointment using the Web Interface
Given I am on the new appointment page
When I fill in "Cucumber Trainee" for "Name"
And I select "2026-02-20 15:10:00 UTC" as the "When" date and time
And I press "Create Appointment"
Then I should see "Cucumber Trainee"
And I should see "2026-02-20 15:10:00 UTC"
"""
And I write to "features/create_appointment_steps.rb" with:
"""
Given('I am on the new appointment page') do
visit new_appointment_path
end
When('I fill in {string} for {string}') do |value, field|
fill_in(field, with: value)
end
When('I press {string}') do |button|
click_button(button)
end
When('I select {string} as the {string} date and time') do |datetime, selector|
select_datetime(datetime, from: selector)
end
Then('I should see {string}') do |text|
expect(page).to have_content(text)
end
"""
And I run `bundle exec rake db:migrate`
And I run `bundle exec rake cucumber`
Then the feature run should pass with:
"""
1 scenario (1 passed)
6 steps (6 passed)
"""

Scenario: Use direct DB injection
Given I have created a new Rails app and installed cucumber-rails
And I force selenium to run Firefox in headless mode
Expand Down
6 changes: 6 additions & 0 deletions features/step_definitions/cucumber_rails_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
step 'I append to "features/support/env.rb" with:', selenium_config
end

Given('I force {string} to use select boxes for dates') do |file|
content = File.read(expand_path(file))

overwrite_file(file, content.gsub(/\.(datetime|time|date)_field/, '.\1_select'))
end

When('I run the cukes') do
run_command_and_stop('bundle exec cucumber')
end
Expand Down
38 changes: 26 additions & 12 deletions lib/cucumber/rails/capybara/select_dates_and_times.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ module SelectDatesAndTimes
# Select a Rails date. Options hash must include from: +label+
def select_date(date, options)
date = Date.parse(date)
if ::Rails::VERSION::MAJOR >= 7
# Rails 7 generates date fields using input type="date". Capybara support's them
base_dom_id = get_base_dom_from_options(options)

# Rails 7 use HTML5 input type="date" by default. If input is not present fallback to plain select boxes alternative.
# It's safe to use has_css? without waiting/retry. We already know field's label is visible
if html5_input_field_present?(base_dom_id)
fill_in options[:from], with: date
else
base_dom_id = get_base_dom_id_from_label_tag(options[:from])

find(:xpath, ".//select[@id='#{base_dom_id}_1i']").select(date.year.to_s)
find(:xpath, ".//select[@id='#{base_dom_id}_2i']").select(I18n.l(date, format: '%B'))
find(:xpath, ".//select[@id='#{base_dom_id}_3i']").select(date.day.to_s)
Expand All @@ -23,30 +24,43 @@ def select_date(date, options)
# Select a Rails time. Options hash must include from: +label+
def select_time(time, options)
time = Time.zone.parse(time)
if ::Rails::VERSION::MAJOR >= 7
# Rails 7 generates date fields using input type="time". Capybara support's them
base_dom_id = get_base_dom_from_options(options)

# Rails 7 use HTML5 input type="time" by default. If input is not present fallback to plain select boxes alternative.
# It's safe to use has_css? without waiting/retry. We already know field's label is visible
if html5_input_field_present?(base_dom_id)
fill_in options[:from], with: time
else
base_dom_id = get_base_dom_id_from_label_tag(options[:from])

find(:xpath, ".//select[@id='#{base_dom_id}_4i']").select(time.hour.to_s.rjust(2, '0'))
find(:xpath, ".//select[@id='#{base_dom_id}_5i']").select(time.min.to_s.rjust(2, '0'))
end
end

# Select a Rails datetime. Options hash must include from: +label+
def select_datetime(datetime, options)
if ::Rails::VERSION::MAJOR >= 7
# Rails 7 generates datetime fields using input type="datetime-local". Capybara support's them
base_dom_id = get_base_dom_id_from_label_tag(options[:from])

# Rails 7 use HTML5 input type="datetime-local" by default. If input is not present fallback to plain select boxes alternative.
# It's safe to use has_css? without waiting/retry. We already know field's label is visible
if html5_input_field_present?(base_dom_id)
fill_in options[:from], with: DateTime.parse(datetime)
else
select_date(datetime, options)
select_time(datetime, options)
extended_options = options.merge(base_dom_id: base_dom_id)
select_date(datetime, extended_options)
select_time(datetime, extended_options)
end
end

private

def html5_input_field_present?(base_dom_id)
::Rails::VERSION::MAJOR >= 7 && page.has_css?("##{base_dom_id}", wait: 0)
end

def get_base_dom_from_options(options)
options[:base_dom_id] || get_base_dom_id_from_label_tag(options[:from])
end

# @example "event_starts_at_"
def get_base_dom_id_from_label_tag(field)
find(:xpath, ".//label[contains(., '#{field}')]")['for'].gsub(/(_[1-5]i)$/, '')
Expand Down

0 comments on commit 5bfb421

Please sign in to comment.