I have a web page that opens a div when you click a button. This div allows you to drag a file from your desktop onto its area; the file then gets uploaded to the server. I'm working with the Ruby implementation of Selenium.
By using the JavaScript debugger in Firefox, I can see that an event called "drop" is being passed to some JavaScript code "handleFileDrop(event)". I presume that if I were to create a mock event and fire it somehow that I could trigger this code.
If found an interesting article that seemed to point me in a promising direction, but I'm still short of figuring it all out. I am able to pass JavaScript to the page using Selenium's get_eval method. Calling methods using this.browserbot is getting me the elements I need.
So:
I post an RSpec test that simulate files drag and drop using Selenium webdriver. It use jQuery to make and trigger a fake 'drop' event.
This code simulate drag and drop of a single file. For sake of simplicity I've stripped code that allow multiple files dropping. Tell me if you need it.
describe "when user drop files", :js => true do
before do
page.execute_script("seleniumUpload = window.$('<input/>').attr({id: 'seleniumUpload', type:'file'}).appendTo('body');")
attach_file('seleniumUpload', Rails.root + 'spec/support/pdffile/pdfTest.pdf')
# Trigger the drop event
page.execute_script("e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : seleniumUpload.get(0).files } }; $('#fileDropArea').trigger(e);")
end
it "should ..." do
should have_content '...'
end
P.S.: remember to replace #fileDropArea with ID of your drop area.
P.P.S: don't use evaluate_script in place of execute_script, otherwise selenium get stuck evaluating complex jQuery objects!
UPDATE: I've write a method you can reuse and do the stuff written above.
def drop_files files, drop_area_id
js_script = "fileList = Array();"
files.count.times do |i|
# Generate a fake input selector
page.execute_script("if ($('#seleniumUpload#{i}').length == 0) { seleniumUpload#{i} = window.$('<input/>').attr({id: 'seleniumUpload#{i}', type:'file'}).appendTo('body'); }")
# Attach file to the fake input selector through Capybara
attach_file("seleniumUpload#{i}", files[i])
# Build up the fake js event
js_script = "#{js_script} fileList.push(seleniumUpload#{i}.get(0).files[0]);"
end
# Trigger the fake drop event
page.execute_script("#{js_script} e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : fileList } }; $('##{drop_area_id}').trigger(e);")
end
Usage:
describe "when user drop files", :js => true do
before do
files = [ Rails.root + 'spec/support/pdffile/pdfTest1.pdf',
Rails.root + 'spec/support/pdffile/pdfTest2.pdf',
Rails.root + 'spec/support/pdffile/pdfTest3.pdf' ]
drop_files files, 'fileDropArea'
end
it "should ..." do
should have_content '...'
end
end
As @Shmoopy asked for it, here's a C# translation of the code provided by @micred
private void DropImage(string dropBoxId, string filePath)
{
var javascriptDriver = this.Driver as IJavaScriptExecutor;
var inputId = dropBoxId + "FileUpload";
// append input to HTML to add file path
javascriptDriver.ExecuteScript(inputId + " = window.$('<input id=\"" + inputId + "\"/>').attr({type:'file'}).appendTo('body');");
this.Driver.FindElement(By.Id(inputId)).SendKeys(filePath);
// fire mock event pointing to inserted file path
javascriptDriver.ExecuteScript("e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : " + inputId + ".get(0).files } }; $('#" + dropBoxId + "').trigger(e);");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With