mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Only enable HTML5 dragging if default is not prevented on the mousedown event
This commit is contained in:
parent
5ca9b25685
commit
195930d900
7 changed files with 91 additions and 53 deletions
60
lib/capybara/selenium/extensions/html5_drag.rb
Normal file
60
lib/capybara/selenium/extensions/html5_drag.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Capybara::Selenium::Node
|
||||
module Html5Drag
|
||||
private
|
||||
|
||||
def html5_drag_to(element)
|
||||
driver.execute_script MOUSEDOWN_TRACKER
|
||||
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
||||
if driver.evaluate_script('window.capybara_mousedown_prevented')
|
||||
element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
|
||||
else
|
||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||
end
|
||||
end
|
||||
|
||||
def draggable?
|
||||
# Workaround https://github.com/SeleniumHQ/selenium/issues/6396
|
||||
driver.evaluate_script('arguments[0]["draggable"]', self) == true
|
||||
end
|
||||
|
||||
MOUSEDOWN_TRACKER = <<~JS
|
||||
if (!window.hasOwnProperty('capybara_mousedown_prevented')){
|
||||
document.addEventListener('mousedown', function(ev){
|
||||
window.capybara_mousedown_prevented = ev.defaultPrevented;
|
||||
})
|
||||
}
|
||||
JS
|
||||
|
||||
HTML5_DRAG_DROP_SCRIPT = <<~JS
|
||||
var source = arguments[0];
|
||||
var target = arguments[1];
|
||||
|
||||
var dt = new DataTransfer();
|
||||
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
||||
|
||||
if (source.tagName == 'A'){
|
||||
dt.setData('text/uri-list', source.href);
|
||||
dt.setData('text', source.href);
|
||||
}
|
||||
if (source.tagName == 'IMG'){
|
||||
dt.setData('text/uri-list', source.src);
|
||||
dt.setData('text', source.src);
|
||||
}
|
||||
var dragEvent = new DragEvent('dragstart', opts);
|
||||
source.dispatchEvent(dragEvent);
|
||||
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
||||
var dragOverEvent = new DragEvent('dragover', opts);
|
||||
target.dispatchEvent(dragOverEvent);
|
||||
var dragLeaveEvent = new DragEvent('dragleave', opts);
|
||||
target.dispatchEvent(dragLeaveEvent);
|
||||
if (dragOverEvent.defaultPrevented) {
|
||||
var dropEvent = new DragEvent('drop', opts);
|
||||
target.dispatchEvent(dropEvent);
|
||||
}
|
||||
var dragEndEvent = new DragEvent('dragend', opts);
|
||||
source.dispatchEvent(dragEndEvent);
|
||||
JS
|
||||
end
|
||||
end
|
|
@ -1,6 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'capybara/selenium/extensions/html5_drag'
|
||||
|
||||
class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
||||
include Html5Drag
|
||||
|
||||
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
||||
super(value)
|
||||
rescue ::Selenium::WebDriver::Error::ExpectedError => err
|
||||
|
@ -11,10 +15,8 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|||
end
|
||||
|
||||
def drag_to(element)
|
||||
return super unless self[:draggable] == 'true'
|
||||
|
||||
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||
return super unless draggable?
|
||||
html5_drag_to(element)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -22,26 +24,4 @@ private
|
|||
def bridge
|
||||
driver.browser.send(:bridge)
|
||||
end
|
||||
|
||||
HTML5_DRAG_DROP_SCRIPT = <<~JS
|
||||
var source = arguments[0];
|
||||
var target = arguments[1];
|
||||
|
||||
var dt = new DataTransfer();
|
||||
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
||||
|
||||
var dragEvent = new DragEvent('dragstart', opts);
|
||||
source.dispatchEvent(dragEvent);
|
||||
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
||||
var dragOverEvent = new DragEvent('dragover', opts);
|
||||
target.dispatchEvent(dragOverEvent);
|
||||
var dragLeaveEvent = new DragEvent('dragleave', opts);
|
||||
target.dispatchEvent(dragLeaveEvent);
|
||||
if (dragOverEvent.defaultPrevented) {
|
||||
var dropEvent = new DragEvent('drop', opts);
|
||||
target.dispatchEvent(dropEvent);
|
||||
}
|
||||
var dragEndEvent = new DragEvent('dragend', opts);
|
||||
source.dispatchEvent(dragEndEvent);
|
||||
JS
|
||||
end
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
require 'capybara/selenium/extensions/html5_drag'
|
||||
|
||||
class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
||||
include Html5Drag
|
||||
|
||||
def click(keys = [], **options)
|
||||
super
|
||||
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
||||
|
@ -54,10 +57,8 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|||
end
|
||||
|
||||
def drag_to(element)
|
||||
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
||||
|
||||
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||
return super unless (browser_version >= 62.0) && draggable?
|
||||
html5_drag_to(element)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -116,26 +117,4 @@ private
|
|||
def browser_version
|
||||
driver.browser.capabilities[:browser_version].to_f
|
||||
end
|
||||
|
||||
HTML5_DRAG_DROP_SCRIPT = <<~JS
|
||||
var source = arguments[0];
|
||||
var target = arguments[1];
|
||||
|
||||
var dt = new DataTransfer();
|
||||
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
||||
|
||||
var dragEvent = new DragEvent('dragstart', opts);
|
||||
source.dispatchEvent(dragEvent);
|
||||
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
||||
var dragOverEvent = new DragEvent('dragover', opts);
|
||||
target.dispatchEvent(dragOverEvent);
|
||||
var dragLeaveEvent = new DragEvent('dragleave', opts);
|
||||
target.dispatchEvent(dragLeaveEvent);
|
||||
if (dragOverEvent.defaultPrevented) {
|
||||
var dropEvent = new DragEvent('drop', opts);
|
||||
target.dispatchEvent(dropEvent);
|
||||
}
|
||||
var dragEndEvent = new DragEvent('dragend', opts);
|
||||
source.dispatchEvent(dragEndEvent);
|
||||
JS
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var activeRequests = 0;
|
||||
$(function() {
|
||||
$('#change').text('I changed it');
|
||||
$('#drag, #drag_scroll').draggable();
|
||||
$('#drag, #drag_scroll, #drag_link').draggable();
|
||||
$('#drop, #drop_scroll').droppable({
|
||||
drop: function(event, ui) {
|
||||
ui.draggable.remove();
|
||||
|
|
|
@ -316,6 +316,14 @@ Capybara::SpecHelper.spec 'node' do
|
|||
element.drag_to(target)
|
||||
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
||||
end
|
||||
|
||||
it 'should drag a link' do
|
||||
@session.visit('/with_js')
|
||||
link = @session.find_link('drag_link')
|
||||
target = @session.find(:id, 'drop')
|
||||
link.drag_to target
|
||||
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hover', requires: [:hover] do
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<h1>FooBar</h1>
|
||||
|
||||
<p id="change">This is text</p>
|
||||
<a id="drag_link" href='#'>This link is non-HTML5 draggable</a>
|
||||
<div id="drag">
|
||||
<p>This is a draggable element.</p>
|
||||
</div>
|
||||
|
@ -27,6 +28,7 @@
|
|||
<div id="drag_html5" draggable="true">
|
||||
<p>This is an HTML5 draggable element.</p>
|
||||
</div>
|
||||
<a id="drag_link_html5" href="#">This is an HTML5 draggable link</a>
|
||||
<div id="drop_html5" class="drop">
|
||||
<p>It should be dropped here.</p>
|
||||
</div>
|
||||
|
|
|
@ -313,6 +313,15 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|||
element.drag_to(target)
|
||||
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5_scroll")]')
|
||||
end
|
||||
|
||||
it 'should drag HTML5 default draggable elements' do
|
||||
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if marionette_lt?(62.0, session)
|
||||
session.visit('/with_js')
|
||||
link = session.find_link('drag_link_html5')
|
||||
target = session.find(:id, 'drop_html5')
|
||||
link.drag_to target
|
||||
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
|
||||
end
|
||||
end
|
||||
|
||||
context 'Windows' do
|
||||
|
|
Loading…
Reference in a new issue