Fire HTML5 drag and drop events in selenium driver for Chrome and FF 62+
This commit is contained in:
parent
6755e2b883
commit
0538cb6688
|
@ -10,9 +10,38 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|||
raise
|
||||
end
|
||||
|
||||
def drag_to(element)
|
||||
return super unless self[:draggable] == 'true'
|
||||
|
||||
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -14,7 +14,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|||
|
||||
def disabled?
|
||||
# Not sure exactly what version of FF fixed the below issue, but it is definitely fixed in 61+
|
||||
return super unless driver.browser.capabilities[:browser_version].to_f < 61.0
|
||||
return super unless browser_version < 61.0
|
||||
|
||||
return true if super
|
||||
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
||||
|
@ -27,7 +27,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|||
|
||||
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
||||
native.clear # By default files are appended so we have to clear here
|
||||
return super if driver.browser.capabilities[:browser_version].to_f >= 62.0
|
||||
return super if browser_version >= 62.0
|
||||
|
||||
# Workaround lack of support for multiple upload by uploading one at a time
|
||||
path_names = value.to_s.empty? ? [] : value
|
||||
|
@ -54,6 +54,13 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|||
actions.perform
|
||||
end
|
||||
|
||||
def drag_to(element)
|
||||
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
||||
|
||||
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
||||
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _send_keys(keys, actions, down_keys = nil)
|
||||
|
@ -99,4 +106,30 @@ private
|
|||
result = bridge.http.call(:post, "session/#{bridge.session_id}/file", file: Selenium::WebDriver::Zipper.zip_file(local_file))
|
||||
result['value']
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -8,6 +8,16 @@ $(function() {
|
|||
$(this).html('Dropped!');
|
||||
}
|
||||
});
|
||||
$('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){
|
||||
ev.originalEvent.dataTransfer.setData("text", ev.target.id);
|
||||
});
|
||||
$('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
|
||||
if ($(this).hasClass('drop')) { ev.preventDefault(); }
|
||||
});
|
||||
$('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
|
||||
ev.preventDefault();
|
||||
$(this).html('HTML5 Dropped ' + ev.originalEvent.dataTransfer.getData("text"));
|
||||
});
|
||||
$('#clickable').click(function(e) {
|
||||
var link = $(this);
|
||||
setTimeout(function() {
|
||||
|
|
|
@ -306,7 +306,7 @@ Capybara::SpecHelper.spec 'node' do
|
|||
element = @session.find('//div[@id="drag"]')
|
||||
target = @session.find('//div[@id="drop"]')
|
||||
element.drag_to(target)
|
||||
expect(@session.find('//div[contains(., "Dropped!")]')).not_to be_nil
|
||||
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
||||
end
|
||||
|
||||
it 'should drag and drop if scrolling is needed' do
|
||||
|
@ -314,7 +314,7 @@ Capybara::SpecHelper.spec 'node' do
|
|||
element = @session.find('//div[@id="drag_scroll"]')
|
||||
target = @session.find('//div[@id="drop_scroll"]')
|
||||
element.drag_to(target)
|
||||
expect(@session.find('//div[contains(., "Dropped!")]')).not_to be_nil
|
||||
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
<p>It should be dropped here.</p>
|
||||
</div>
|
||||
|
||||
<div id="drop_html5_scroll" class="drop">
|
||||
<p>It should be dropped here.</p>
|
||||
</div>
|
||||
<div id="drag_html5" draggable="true">
|
||||
<p>This is an HTML5 draggable element.</p>
|
||||
</div>
|
||||
<div id="drop_html5" class="drop">
|
||||
<p>It should be dropped here.</p>
|
||||
</div>
|
||||
|
||||
<p><a href="#" id="clickable">Click me</a></p>
|
||||
<p><a href="#" id="slow-click">Slowly</a></p>
|
||||
|
||||
|
@ -135,6 +145,9 @@
|
|||
<div id="drop_scroll">
|
||||
<p>It should be dropped here.</p>
|
||||
</div>
|
||||
<div id="drag_html5_scroll" draggable="true">
|
||||
<p>This is an HTML5 draggable element.</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
// a javascript comment
|
||||
|
|
|
@ -285,6 +285,36 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Element#drag_to', :focus_ do
|
||||
it 'should HTML5 drag and drop an object' do
|
||||
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if marionette_lt?(62.0, session)
|
||||
session.visit('/with_js')
|
||||
element = session.find('//div[@id="drag_html5"]')
|
||||
target = session.find('//div[@id="drop_html5"]')
|
||||
element.drag_to(target)
|
||||
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
|
||||
end
|
||||
|
||||
it 'should not HTML5 drag and drop on a non HTML5 drop element' do
|
||||
session.visit('/with_js')
|
||||
element = session.find('//div[@id="drag_html5"]')
|
||||
target = session.find('//div[@id="drop_html5"]')
|
||||
target.execute_script("$(this).removeClass('drop');")
|
||||
element.drag_to(target)
|
||||
sleep 1
|
||||
expect(session).not_to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
|
||||
end
|
||||
|
||||
it 'should HTML6 drag and drop when scrolling needed' do
|
||||
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if marionette_lt?(62.0, session)
|
||||
session.visit('/with_js')
|
||||
element = session.find('//div[@id="drag_html5_scroll"]')
|
||||
target = session.find('//div[@id="drop_html5_scroll"]')
|
||||
element.drag_to(target)
|
||||
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5_scroll")]')
|
||||
end
|
||||
end
|
||||
|
||||
context 'Windows' do
|
||||
it "can't close the primary window" do
|
||||
expect do
|
||||
|
|
Loading…
Reference in New Issue