mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Simulate file/external data drop in chrome and firefox and move HTML5 drag/drop tests into shared tests
This commit is contained in:
parent
4c097e4324
commit
94f7f8881a
15 changed files with 228 additions and 58 deletions
|
@ -1,3 +1,10 @@
|
|||
# Version 3.21.0
|
||||
Release date: unreleased
|
||||
|
||||
### Added
|
||||
|
||||
* Chrome and Firefox via the selenium driver support dropping files/data on elements
|
||||
|
||||
# Version 3.20.1
|
||||
Release date: 2019-05-17
|
||||
|
||||
|
|
|
@ -69,6 +69,10 @@ module Capybara
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def drop(*args)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def scroll_by(x, y)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
|
|
@ -392,6 +392,30 @@ module Capybara
|
|||
self
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Drop items on the current element.
|
||||
#
|
||||
# target = page.find('#foo')
|
||||
# target.drop('/some/path/file.csv')
|
||||
#
|
||||
# @overload drop(path, ...)
|
||||
# @param [String, #to_path] path Location of the file to drop on the element
|
||||
#
|
||||
# @overload drop(strings, ...)
|
||||
# @param [Hash] strings A hash of type to data to be dropped - { "text/url" => "https://www.google.com" }
|
||||
#
|
||||
# @return [Capybara::Node::Element] The element
|
||||
def drop(*args)
|
||||
options = args.map do |arg|
|
||||
return arg.to_path if arg.respond_to?(:to_path)
|
||||
|
||||
arg
|
||||
end
|
||||
synchronize { base.drop(*options) }
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Scroll the page or element
|
||||
|
|
|
@ -22,6 +22,69 @@ class Capybara::Selenium::Node
|
|||
native.property('draggable')
|
||||
end
|
||||
|
||||
def html5_drop(*args)
|
||||
if args[0].is_a? String
|
||||
input = driver.evaluate_script ATTACH_FILE
|
||||
input.set_file(args)
|
||||
driver.execute_script DROP_FILE, self, input
|
||||
else
|
||||
items = args.each_with_object([]) do |arg, arr|
|
||||
arg.each_with_object(arr) do |(type, data), arr_|
|
||||
arr_ << { type: type, data: data }
|
||||
end
|
||||
end
|
||||
driver.execute_script DROP_STRING, items, self
|
||||
end
|
||||
end
|
||||
|
||||
DROP_STRING = <<~JS
|
||||
var strings = arguments[0],
|
||||
el = arguments[1],
|
||||
dt = new DataTransfer(),
|
||||
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 el = arguments[0],
|
||||
input = arguments[1],
|
||||
files = input.files,
|
||||
dt = new DataTransfer(),
|
||||
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
||||
input.parentElement.removeChild(input);
|
||||
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);
|
||||
el.dispatchEvent(dropEvent);
|
||||
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;
|
||||
|
|
|
@ -137,6 +137,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
|
||||
end
|
||||
|
||||
def drop(*_)
|
||||
raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
|
||||
end
|
||||
|
||||
def tag_name
|
||||
@tag_name ||= native.tag_name.downcase
|
||||
end
|
||||
|
|
|
@ -34,6 +34,10 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|||
html5_drag_to(element)
|
||||
end
|
||||
|
||||
def drop(*args)
|
||||
html5_drop(*args)
|
||||
end
|
||||
|
||||
def click(*)
|
||||
super
|
||||
rescue ::Selenium::WebDriver::Error::WebDriverError => e
|
||||
|
|
|
@ -52,6 +52,10 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|||
html5_drag_to(element)
|
||||
end
|
||||
|
||||
def drop(*args)
|
||||
html5_drop(*args)
|
||||
end
|
||||
|
||||
def hover
|
||||
return super unless browser_version >= 65.0
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -199,10 +199,4 @@ Capybara::SpecHelper.spec '#attach_file' do
|
|||
expect(extract_results(@session)['hidden_image']).to end_with(File.basename(__FILE__))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_os_path_separators(path)
|
||||
Gem.win_platform? ? path.to_s.tr('/', '\\') : path.to_s
|
||||
end
|
||||
end
|
||||
|
|
|
@ -414,6 +414,87 @@ Capybara::SpecHelper.spec 'node' do
|
|||
link.drag_to target
|
||||
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
||||
end
|
||||
|
||||
context 'HTML5', requires: %i[js html5_drag] do
|
||||
it 'should HTML5 drag and drop an object' do
|
||||
@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 string: text/plain drag_html5")]')
|
||||
end
|
||||
|
||||
it 'should set clientX/Y in dragover events' do
|
||||
@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_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
|
||||
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")]')
|
||||
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 string: text/plain drag_html5_scroll")]')
|
||||
end
|
||||
|
||||
it 'should drag HTML5 default draggable elements' do
|
||||
@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
|
||||
end
|
||||
|
||||
describe 'Element#drop', requires: %i[js html5_drag] do
|
||||
it 'can drop a file' do
|
||||
@session.visit('/with_js')
|
||||
target = @session.find('//div[@id="drop_html5"]')
|
||||
target.drop(
|
||||
with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__)))
|
||||
)
|
||||
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
|
||||
end
|
||||
|
||||
it 'can drop multiple files' do
|
||||
@session.visit('/with_js')
|
||||
target = @session.find('//div[@id="drop_html5"]')
|
||||
target.drop(
|
||||
with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))),
|
||||
with_os_path_separators(File.expand_path('../fixtures/test_file.txt', File.dirname(__FILE__)))
|
||||
)
|
||||
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
|
||||
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: test_file.txt")]')
|
||||
end
|
||||
|
||||
it 'can drop strings' do
|
||||
@session.visit('/with_js')
|
||||
target = @session.find('//div[@id="drop_html5"]')
|
||||
target.drop('text/plain' => '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('text/plain' => 'Some dropped text', 'text/url' => '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 '#hover', requires: [:hover] do
|
||||
|
|
|
@ -124,6 +124,10 @@ module Capybara
|
|||
def be_an_invalid_element_error(session)
|
||||
satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
|
||||
end
|
||||
|
||||
def with_os_path_separators(path)
|
||||
Gem.win_platform? ? path.to_s.tr('/', '\\') : path.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capyba
|
|||
pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
|
||||
when 'Capybara::Session selenium #attach_file with a block can upload by clicking the file input'
|
||||
pending "Geckodriver doesn't allow clicking on file inputs"
|
||||
when /drag_to.*HTML5/
|
||||
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, @session)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -110,6 +110,8 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumIE, 'selenium', capybara_sk
|
|||
pending 'IE treats blank href as a parent request (against HTML spec)'
|
||||
when /#attach_file with a block/
|
||||
skip 'Hangs IE testing for unknown reason'
|
||||
when /drag_to.*HTML5/
|
||||
pending "IE doesn't support a DataTransfer constuctor"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ Capybara::SpecHelper.run_specs TestSessions::Safari, SAFARI_DRIVER.to_s, capybar
|
|||
when 'Capybara::Session selenium_safari #go_back should fetch a response from the driver from the previous page',
|
||||
'Capybara::Session selenium_safari #go_forward should fetch a response from the driver from the previous page'
|
||||
skip 'safaridriver loses the ability to find elements in the document after `go_back`'
|
||||
when /drag_to.*HTML5/
|
||||
pending "Safari doesn't support"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -303,57 +303,6 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Element#drag_to' do
|
||||
before do
|
||||
skip "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, session)
|
||||
skip "IE doesn't support a DataTransfer constuctor" if ie?(session)
|
||||
skip "Safari doesn't support" if safari?(session)
|
||||
end
|
||||
|
||||
it 'should HTML5 drag and drop an object' do
|
||||
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 set clientX/Y in dragover events' do
|
||||
session.visit('/with_js')
|
||||
element = session.find('//div[@id="drag_html5"]')
|
||||
target = session.find('//div[@id="drop_html5"]')
|
||||
element.drag_to(target)
|
||||
session.all(:css, 'div.log').each { |el| puts el.text }
|
||||
expect(session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
|
||||
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 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")]')
|
||||
end
|
||||
|
||||
it 'should drag HTML5 default draggable elements' do
|
||||
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
|
||||
|
||||
describe 'Capybara#Node#attach_file' do
|
||||
it 'can attach a directory' do
|
||||
pending "Geckodriver doesn't support uploading a directory" if firefox?(session)
|
||||
|
|
Loading…
Add table
Reference in a new issue