From ba28a2a20a91e195e45a9de776f4a43b2e87599c Mon Sep 17 00:00:00 2001 From: Joe Ferris Date: Mon, 9 Jul 2012 17:34:09 -0700 Subject: [PATCH] Fix focus/blur event compatibility with Selenium while filling out forms --- spec/driver_spec.rb | 6 ++-- spec/selenium_compatibility_spec.rb | 48 +++++++++++++++++++++++++++++ spec/support/app_runner.rb | 11 ++++--- src/capybara.js | 30 ++++++++++++------ 4 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 spec/selenium_compatibility_spec.rb diff --git a/spec/driver_spec.rb b/spec/driver_spec.rb index e806eaa..e1bbfa7 100644 --- a/spec/driver_spec.rb +++ b/spec/driver_spec.rb @@ -690,7 +690,7 @@ describe Capybara::Webkit::Driver do let(:keyevents) do (%w{focus} + newtext.length.times.collect { %w{keydown keypress keyup input} } + - %w{change blur}).flatten + %w{change}).flatten end %w(email number password search tel text url).each do | field_type | @@ -707,12 +707,12 @@ describe Capybara::Webkit::Driver do it "triggers radio input events" do driver.find("//input[@type='radio']").first.set(true) - driver.find("//li").map(&:text).should == %w(mousedown mouseup change click) + driver.find("//li").map(&:text).should == %w(mousedown focus mouseup change click) end it "triggers checkbox events" do driver.find("//input[@type='checkbox']").first.set(true) - driver.find("//li").map(&:text).should == %w(mousedown mouseup change click) + driver.find("//li").map(&:text).should == %w(mousedown focus mouseup change click) end end diff --git a/spec/selenium_compatibility_spec.rb b/spec/selenium_compatibility_spec.rb new file mode 100644 index 0000000..4400bee --- /dev/null +++ b/spec/selenium_compatibility_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Capybara::Webkit, 'compatibility with selenium' do + include AppRunner + + it 'generates the same events as selenium when filling out forms' do + run_application_for_html(<<-HTML) + +
+ + + +
+ + + HTML + + compare_events_for_drivers(:reusable_webkit, :selenium) do + visit "/" + fill_in "One", :with => "some value" + fill_in "Two", :with => "other value" + click_button "Submit" + end + end + + def compare_events_for_drivers(first, second, &block) + events_for_driver(first, &block).should == events_for_driver(second, &block) + end + + def events_for_driver(name, &block) + session = Capybara::Session.new(name, AppRunner.app) + session.instance_eval(&block) + session.evaluate_script("window.log") + end +end diff --git a/spec/support/app_runner.rb b/spec/support/app_runner.rb index 199c8cb..5b8fac9 100644 --- a/spec/support/app_runner.rb +++ b/spec/support/app_runner.rb @@ -32,13 +32,16 @@ module AppRunner end def driver_for_html(html) - app = lambda do |env| - [200, { 'Content-Type' => 'text/html', 'Content-Length' => html.size.to_s }, [html]] - end - run_application app + run_application_for_html html build_driver end + def run_application_for_html(html) + run_application lambda { |env| + [200, { 'Content-Type' => 'text/html', 'Content-Length' => html.size.to_s }, [html]] + } + end + private def build_driver diff --git a/src/capybara.js b/src/capybara.js index 1e2f859..5c6c2fb 100644 --- a/src/capybara.js +++ b/src/capybara.js @@ -110,6 +110,7 @@ Capybara = { click: function (index) { this.mousedown(index); + this.focus(index); this.mouseup(index); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); @@ -214,7 +215,7 @@ Capybara = { textTypes = ["email", "number", "password", "search", "tel", "text", "textarea", "url"]; if (textTypes.indexOf(type) != -1) { - this.trigger(index, "focus"); + this.focus(index); maxLength = this.attribute(index, "maxlength"); if (maxLength && value.length > maxLength) { @@ -233,22 +234,28 @@ Capybara = { this.trigger(index, "input"); } this.trigger(index, "change"); - this.trigger(index, "blur"); } else if (type === "checkbox" || type === "radio") { if (node.checked != (value === "true")) { - this.click(index) + this.click(index); } } else if (type === "file") { this.lastAttachedFile = value; - this.click(index) + this.click(index); } else { node.value = value; } }, + focus: function(index) { + if (this.focusedIndex) + this.trigger(this.focusedIndex, "blur"); + this.focusedIndex = index; + this.trigger(index, "focus"); + }, + selectOption: function(index) { this.nodes[index].selected = true; this.trigger(index, "change"); @@ -270,7 +277,8 @@ Capybara = { position.x += element.offsetLeft; position.y += element.offsetTop; } while ((element = element.offsetParent)); - position.x = Math.floor(position.x), position.y = Math.floor(position.y); + position.x = Math.floor(position.x); + position.y = Math.floor(position.y); return position; }, @@ -282,7 +290,9 @@ Capybara = { oldStyle[prop] = element.style[prop]; element.style[prop] = newStyle[prop]; } - element.offsetWidth, element.offsetHeight; // force reflow + // force reflow + element.offsetWidth; + element.offsetHeight; for (prop in oldStyle) element.style[prop] = oldStyle[prop]; } @@ -299,12 +309,14 @@ Capybara = { var eventObject = document.createEvent("MouseEvents"); eventObject.initMouseEvent(eventName, true, true, window, 0, 0, 0, options.clientX || 0, options.clientY || 0, false, false, false, false, 0, null); element.dispatchEvent(eventObject); - } + }; mouseTrigger('mousedown', options); - options.clientX += 1, options.clientY += 1; + options.clientX += 1; + options.clientY += 1; mouseTrigger('mousemove', options); - position = this.centerPostion(target), options = { + position = this.centerPostion(target); + options = { clientX: position.x, clientY: position.y };