Skip to content

Commit

Permalink
Simulate file/external data drop in chrome and firefox
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole committed May 15, 2019
1 parent 31b74c5 commit bbace80
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 4 deletions.
4 changes: 4 additions & 0 deletions lib/capybara/driver/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ def drag_to(element)
raise NotImplementedError
end

def drop_on(**options)
raise NotImplementedError
end

def scroll_by(x, y)
raise NotImplementedError
end
Expand Down
5 changes: 5 additions & 0 deletions lib/capybara/node/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ def drag_to(node)
self
end

def drop_on(**options)
synchronize { base.drop_on(options) }
self
end

##
#
# Scroll the page or element
Expand Down
59 changes: 59 additions & 0 deletions lib/capybara/selenium/extensions/html5_drag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,65 @@ def html5_draggable?
native.property('draggable')
end

def html5_drop(file: nil, string: nil)
if file
input = driver.evaluate_script ATTACH_FILE
input.set_file(file)
driver.execute_script DROP_FILE, self
# TODO: remove the file input
elsif string
strings = [string].flatten(1)
driver.execute_script DROP_STRINGS, strings, self
end
end

DROP_STRINGS = <<~JS
var strings = arguments[0];
var el = arguments[1];
var dt = new DataTransfer();
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
for (var i=0; i < strings.length; i++){
if (dt.items) {
dt.items.add(strings[i]['data'], strings[i]['type']);
} else {
dt.setData(strings[i]['type'], strings[i]['data']);
}
}
var dropEvent = new DragEvent('drop', opts);
el.dispatchEvent(dropEvent);
JS

DROP_FILE = <<~JS
var input = document.querySelector('#_capybara_drop_file');
var files = input.files;
var dt = new DataTransfer();
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
if (dt.items){
for (var i=0; i<files.length; i++){
dt.items.add(files[i]);
}
} else {
Object.defineProperty(dt, "files", {
value: files,
writable: false
});
}
var dropEvent = new DragEvent('drop', opts);
arguments[0].dispatchEvent(dropEvent);
document.body.removeChild(input);
JS

ATTACH_FILE = <<~JS
(function(){
var input = document.createElement('INPUT');
input.type = "file";
input.id = "_capybara_drop_file";
input.multiple = true;
document.body.appendChild(input);
return input;
})()
JS

MOUSEDOWN_TRACKER = <<~JS
document.addEventListener('mousedown', ev => {
window.capybara_mousedown_prevented = ev.defaultPrevented;
Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/selenium/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def drag_to(element)
element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
end

def drop_on(**_)
raise ArgumentError, 'Blah'
end

def tag_name
@tag_name ||= native.tag_name.downcase
end
Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/selenium/nodes/chrome_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def drag_to(element)
html5_drag_to(element)
end

def drop_on(**options)
html5_drop(options)
end

def click(*)
super
rescue ::Selenium::WebDriver::Error::WebDriverError => e
Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/selenium/nodes/firefox_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def drag_to(element)
html5_drag_to(element)
end

def drop_on(**options)
html5_drop(options)
end

def hover
return super unless browser_version >= 65.0

Expand Down
28 changes: 27 additions & 1 deletion lib/capybara/spec/public/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,33 @@ $(function() {
});
$('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
ev.preventDefault();
$(this).html('HTML5 Dropped ' + ev.originalEvent.dataTransfer.getData("text"));
var oev = ev.originalEvent;
if (oev.dataTransfer.items) {
for (var i = 0; i < oev.dataTransfer.items.length; i++){
var item = oev.dataTransfer.items[i];
if (item.kind === 'file'){
var file = item.getAsFile();
$(this).append('HTML5 Dropped file: ' + file.name);
} else {
var _this = this;
var callback = (function(type){
return function(s){
$(_this).append('HTML5 Dropped string: ' + type + ' ' + s)
}
})(item.type);
item.getAsString(callback);
}
}
} else {
$(this).html('HTML5 Dropped ' + oev.dataTransfer.getData("text"));
for (var i = 0; i < oev.dataTransfer.files.length; i++) {
$(this).append('HTML5 Dropped file: ' + oev.dataTransfer.files[i].name);
}
for (var i = 0; i < oev.dataTransfer.types.length; i++) {
var type = oev.dataTransfer.types[i];
$(this).append('HTML5 Dropped string: ' + type + ' ' + oev.dataTransfer.getData(type));
}
}
});
$('#clickable').click(function(e) {
var link = $(this);
Expand Down
41 changes: 38 additions & 3 deletions spec/shared_selenium_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
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")]')
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
end

it 'should set clientX/Y in dragover events' do
Expand All @@ -334,15 +334,15 @@
target.execute_script("$(this).removeClass('drop');")
element.drag_to(target)
sleep 1
expect(session).not_to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
expect(session).not_to have_xpath('//div[contains(., "HTML5 Dropped")]')
end

it 'should HTML5 drag and drop when scrolling needed' do
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")]')
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5_scroll")]')
end

it 'should drag HTML5 default draggable elements' do
Expand All @@ -354,6 +354,41 @@
end
end

describe 'Element#drop_on' do
it 'can drop a file' do
session.visit('/with_js')
target = session.find('//div[@id="drop_html5"]')
target.drop_on(file: File.expand_path('./fixtures/capybara.csv', File.dirname(__FILE__)))
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.csv")]')
end

it 'can drop multiple files' do
session.visit('/with_js')
target = session.find('//div[@id="drop_html5"]')
target.drop_on(file: [
File.expand_path('./fixtures/capybara.csv', File.dirname(__FILE__)),
File.expand_path('./fixtures/certificate.pem', File.dirname(__FILE__))
])
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.csv")]')
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped file: certificate.pem")]')
end

it 'can drop strings' do
session.visit('/with_js')
target = session.find('//div[@id="drop_html5"]')
target.drop_on(string: { type: 'text/plain', data: 'Some dropped text' })
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
end

it 'can drop multiple strings' do
session.visit('/with_js')
target = session.find('//div[@id="drop_html5"]')
target.drop_on(string: [{ type: 'text/plain', data: 'Some dropped text' }, { type: 'text/url', data: 'http://www.google.com' }])
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/url http://www.google.com")]')
end
end

describe 'Capybara#Node#attach_file' do
it 'can attach a directory' do
pending "Geckodriver doesn't support uploading a directory" if firefox?(session)
Expand Down

0 comments on commit bbace80

Please sign in to comment.