Set time and datetime-local fields via JS to support other locales

This commit is contained in:
Thomas Walpole 2018-05-09 12:44:47 -07:00
parent 56458cb93d
commit e8874a1cbc
4 changed files with 52 additions and 77 deletions

View File

@ -6,6 +6,7 @@ Release date: unreleased
* Support for using `select` with text inputs associated with a datalist element
* `type` filter on `:button` selector
* Support for server operating in https mode
* Selenium driver now uses JS to fill_in date and time fields when passed date or time objects [Aleksei Gusev, Thomas Walpole]
# Version 3.0.3
Release date: 2018-04-30
@ -21,7 +22,7 @@ Release date: 2018-04-13
### Fixes
* Fixexpression filter descriptions in some selector failure messages
* Fix expression filter descriptions in some selector failure messages
* Fix compounding of negated matechers - Issue #2010
# Version 3.0.1

View File

@ -1,19 +1,6 @@
# frozen_string_literal: true
class Capybara::Selenium::Node < Capybara::Driver::Node
SET_FORMATS = Hash.new(date: '%Y-%m-%d', time: '%H:%M', datetime: "%m%d%Y\t%I%M%P").merge(
firefox: {
date: '%Y-%m-%d',
time: '%H:%M',
datetime: "%m%d%Y\t%I%M%P"
},
chrome: {
date: '%m%d%Y',
time: '%I%M%P',
datetime: "%m%d%Y\t%I%M%P"
}
)
def visible_text
native.text
end
@ -253,39 +240,35 @@ private
yield
end
def set_date(new_value) # rubocop:disable Naming/AccessorMethodName
new_value = new_value.to_date.strftime('%Y-%m-%d') if new_value.respond_to?(:to_date)
begin
is_value_changing = new_value != value
driver.execute_script(<<-JS, self)
arguments[0].dispatchEvent(new Event('focus'));
arguments[0].value = '#{new_value}';
JS
if is_value_changing
driver.execute_script(<<-JS, self)
arguments[0].dispatchEvent(new Event('input'));
arguments[0].dispatchEvent(new Event('change'));
JS
end
rescue Capybara::NotSupportedByDriverError
set_text(new_value)
end
def set_date(value) # rubocop:disable Naming/AccessorMethodName
return set_text(value) unless value.respond_to?(:to_date)
# TODO: this would be better if locale can be detected and correct keystrokes sent
update_value_js(value.to_date.strftime('%Y-%m-%d'))
end
def set_time(value) # rubocop:disable Naming/AccessorMethodName
if value.respond_to?(:to_time)
set_text(value.to_time.strftime(SET_FORMATS[driver.browser_name][:time]))
else
set_text(value)
end
return set_text(value) unless value.respond_to?(:to_time)
# TODO: this would be better if locale can be detected and correct keystrokes sent
update_value_js(value.to_time.strftime('%H:%M'))
end
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
if value.respond_to?(:to_time)
set_text(value.to_time.strftime(SET_FORMATS[driver.browser_name][:datetime]))
else
set_text(value)
end
return set_text(value) unless value.respond_to?(:to_time)
# TODO: this would be better if locale can be detected and correct keystrokes sent
update_value_js(value.to_time.strftime('%Y-%m-%dT%H:%M'))
end
def update_value_js(value)
driver.execute_script(<<-JS, self, value)
if (document.activeElement !== arguments[0]){
arguments[0].focus();
}
if (arguments[0].value != arguments[1]) {
arguments[0].value = arguments[1]
arguments[0].dispatchEvent(new InputEvent('input'));
arguments[0].dispatchEvent(new Event('change', { bubbles: true }));
}
JS
end
def set_file(value) # rubocop:disable Naming/AccessorMethodName

View File

@ -64,40 +64,4 @@ RSpec.describe "Capybara::Session with chrome" do
end
end
end
describe '#fill_in' do
before do
@session = TestSessions::Chrome
@session.visit('/form')
end
context "Date/Time" do
before do
@session.execute_script <<-JS
window.capybara = {formDateFiredEvents: []};
['focus', 'input', 'change'].forEach(function(eventType) {
document.getElementById('form_date')
.addEventListener(eventType, function() { window.capybara.formDateFiredEvents.push(eventType) });
});
JS
end
it "should generate standard events on changing value" do
expect {
@session.fill_in('form_date', with: Date.today)
}.to change {
@session.evaluate_script('window.capybara.formDateFiredEvents')
}.to %w[focus input change]
end
it "should not generate input and change events if the value is not changed" do
expect {
@session.fill_in('form_date', with: Date.today)
@session.fill_in('form_date', with: Date.today)
}.to change {
@session.evaluate_script('window.capybara.formDateFiredEvents')
}.to %w[focus input change focus]
end
end
end
end

View File

@ -129,6 +129,33 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
end
end
context '#fill_in with Date' do
before do
session.visit('/form')
session.execute_script <<-JS
window.capybara_formDateFiredEvents = [];
['focus', 'input', 'change'].forEach(function(eventType) {
document.getElementById('form_date')
.addEventListener(eventType, function() { window.capybara_formDateFiredEvents.push(eventType); });
});
JS
# work around weird FF issue where it would create an extra focus issue in some cases
session.find(:css, 'body').click
end
it "should generate standard events on changing value" do
session.fill_in('form_date', with: Date.today)
expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq %w[focus input change]
end
it "should not generate input and change events if the value is not changed" do
session.fill_in('form_date', with: Date.today)
session.fill_in('form_date', with: Date.today)
# Chrome adds an extra focus for some reason - ok for now
expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq(%w[focus input change])
end
end
context "#fill_in with { clear: Array } fill_options" do
it 'should pass the array through to the element' do
pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(session)