From c053ff60de9d375992aa8a4a8760ae45d36f7837 Mon Sep 17 00:00:00 2001 From: Thomas Walpole Date: Wed, 26 Jun 2019 06:37:26 -0700 Subject: [PATCH] Workaround Chrome issue with mouse button state when dragging --- .../selenium/extensions/html5_drag.rb | 14 +++++++-- lib/capybara/selenium/node.rb | 30 +++++++++---------- lib/capybara/selenium/nodes/chrome_node.rb | 9 ++++++ lib/capybara/spec/session/node_spec.rb | 10 +++++++ lib/capybara/spec/views/with_dragula.erb | 22 ++++++++++++++ 5 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 lib/capybara/spec/views/with_dragula.erb diff --git a/lib/capybara/selenium/extensions/html5_drag.rb b/lib/capybara/selenium/extensions/html5_drag.rb index 2f1d0466..8e0630d3 100644 --- a/lib/capybara/selenium/extensions/html5_drag.rb +++ b/lib/capybara/selenium/extensions/html5_drag.rb @@ -8,15 +8,23 @@ class Capybara::Selenium::Node driver.execute_script MOUSEDOWN_TRACKER scroll_if_needed { browser_action.click_and_hold(native).perform } if driver.evaluate_script('window.capybara_mousedown_prevented || !arguments[0].draggable', self) - element.scroll_if_needed { browser_action.move_to(element.native).release.perform } + perform_regular_drag(element) else - driver.evaluate_async_script HTML5_DRAG_DROP_SCRIPT, self, element, delay * 1000 - browser_action.release.perform + perform_html5_drag(element, delay) end end private + def perform_regular_drag(element) + element.scroll_if_needed { browser_action.move_to(element.native).release.perform } + end + + def perform_html5_drag(element, delay) + driver.evaluate_async_script HTML5_DRAG_DROP_SCRIPT, self, element, delay * 1000 + browser_action.release.perform + end + def html5_drop(*args) if args[0].is_a? String input = driver.evaluate_script ATTACH_FILE diff --git a/lib/capybara/selenium/node.rb b/lib/capybara/selenium/node.rb index 7d49eafe..4e39fd8f 100644 --- a/lib/capybara/selenium/node.rb +++ b/lib/capybara/selenium/node.rb @@ -188,6 +188,21 @@ protected yield end + def scroll_to_center + script = <<-'JS' + try { + arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); + } catch(e) { + arguments[0].scrollIntoView(true); + } + JS + begin + driver.execute_script(script, self) + rescue StandardError # rubocop:disable Lint/HandleExceptions + # Swallow error if scrollIntoView with options isn't supported + end + end + private def sibling_index(parent, node, selector) @@ -241,21 +256,6 @@ private end end - def scroll_to_center - script = <<-'JS' - try { - arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); - } catch(e) { - arguments[0].scrollIntoView(true); - } - JS - begin - driver.execute_script(script, self) - rescue StandardError # rubocop:disable Lint/HandleExceptions - # Swallow error if scrollIntoView with options isn't supported - end - end - def set_date(value) # rubocop:disable Naming/AccessorMethodName value = SettableValue.new(value) return set_text(value) unless value.dateable? diff --git a/lib/capybara/selenium/nodes/chrome_node.rb b/lib/capybara/selenium/nodes/chrome_node.rb index f89687c8..4dadcaf6 100644 --- a/lib/capybara/selenium/nodes/chrome_node.rb +++ b/lib/capybara/selenium/nodes/chrome_node.rb @@ -57,6 +57,15 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node private + def perform_regular_drag(element) + return super unless (browser_version < 77.0) && w3c? && !element.obscured? + + # W3C Chrome/chromedriver < 77 doesn't maintain mouse button state across actions API performs + # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2981 + browser_action.release.perform + browser_action.click_and_hold(native).move_to(element.native).release.perform + end + def file_errors @file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do [::Selenium::WebDriver::Error::ExpectedError] diff --git a/lib/capybara/spec/session/node_spec.rb b/lib/capybara/spec/session/node_spec.rb index 4eb8d0f1..3b52cb67 100644 --- a/lib/capybara/spec/session/node_spec.rb +++ b/lib/capybara/spec/session/node_spec.rb @@ -419,6 +419,16 @@ Capybara::SpecHelper.spec 'node' do expect(@session).to have_xpath('//div[contains(., "Dropped!")]') end + it 'should work with Dragula' do + @session.visit('/with_dragula') + @session.within(:css, '#sortable') do + src = @session.find('div', text: 'Item 1') + target = @session.find('div', text: 'Item 3') + src.drag_to target + expect(@session).to have_content(/Item 2.*Item 1/, normalize_ws: true) + end + end + context 'HTML5', requires: %i[js html5_drag] do it 'should HTML5 drag and drop an object' do @session.visit('/with_js') diff --git a/lib/capybara/spec/views/with_dragula.erb b/lib/capybara/spec/views/with_dragula.erb new file mode 100644 index 00000000..d788e4b9 --- /dev/null +++ b/lib/capybara/spec/views/with_dragula.erb @@ -0,0 +1,22 @@ + + + + with_dragula + + + + +
+
Item 1
+
Item 2
+
Item 3
+
Item 4
+
Item 5
+
+ + + + +