From cfc507ff12149225905e0c0dce4a5912dc54e123 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 7 Apr 2015 17:26:02 -0700 Subject: [PATCH] Add fill_option to Selenium for clearing with backspaces Sending the correct number of backspace keys to clear an input before filling it in with the desired text more accurately mimics real-world behavior, which can be useful when used with libraries that want to maintain a tight control over the DOM, such as React.js. This option can be used by passing `:fill_options` to the `fill_in` method in your feature specs: fill_in 'my_input', :with => 'my text', :fill_options => { :clear => :backspace } Or, with the Ruby 1.9 style syntax: fill_in 'my input', with: 'my text' fill_options: { clear: :backspace } --- History.md | 1 + lib/capybara/selenium/node.rb | 16 ++++++++++++---- lib/capybara/spec/session/fill_in_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 69a8e047..469b84b8 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ Release date: unreleased ### Added * 'formmethod' attribute support in RackTest driver [Emilia Andrzejewska] +* Clear field using backspaces in Selenium driver by using `:fill_options => { :clear => :backspace }` [Joe Lencioni] #Version 2.4.4 Release date: 2014-10-13 diff --git a/lib/capybara/selenium/node.rb b/lib/capybara/selenium/node.rb index 25938a4f..4701b552 100644 --- a/lib/capybara/selenium/node.rb +++ b/lib/capybara/selenium/node.rb @@ -23,7 +23,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node end end - def set(value) + def set(value, fill_options) tag_name = self.tag_name type = self[:type] if (Array === value) && !self[:multiple] @@ -42,9 +42,17 @@ class Capybara::Selenium::Node < Capybara::Driver::Node elsif value.to_s.empty? native.clear else - #script can change a readonly element which user input cannot, so dont execute if readonly - driver.browser.execute_script "arguments[0].value = ''", native - native.send_keys(value.to_s) + if fill_options[:clear] == :backspace + # Clear field by sending the correct number of backspace keys. + backspaces = [:backspace] * self.value.to_s.length + native.send_keys(*(backspaces + [value.to_s])) + else + # Clear field by JavaScript assignment of the value property. + # Script can change a readonly element which user input cannot, so + # don't execute if readonly. + driver.browser.execute_script "arguments[0].value = ''", native + native.send_keys(value.to_s) + end end elsif native.attribute('isContentEditable') #ensure we are focused on the element diff --git a/lib/capybara/spec/session/fill_in_spec.rb b/lib/capybara/spec/session/fill_in_spec.rb index ea62ad8c..43d64f70 100644 --- a/lib/capybara/spec/session/fill_in_spec.rb +++ b/lib/capybara/spec/session/fill_in_spec.rb @@ -180,4 +180,24 @@ Capybara::SpecHelper.spec "#fill_in" do end.to raise_error(Capybara::ElementNotFound) end end + + context "with { :clear => :backspace } fill_option", :requires => [:js] do + it 'should only trigger onchange once' do + @session.visit('/with_js') + @session.fill_in('with_change_event', :with => 'some value', + :fill_options => { :clear => :backspace }) + # click outside the field to trigger the change event + @session.find(:css, 'body').click + expect(@session.find(:css, '.change_event_triggered', :match => :one)).to have_text 'some value' + end + + it 'should trigger change when clearing field' do + @session.visit('/with_js') + @session.fill_in('with_change_event', :with => '', + :fill_options => { :clear => :backspace }) + # click outside the field to trigger the change event + @session.find(:css, 'body').click + expect(@session).to have_selector(:css, '.change_event_triggered', :match => :one) + end + end end