Using Selenium to imitate dragging a file onto an upload element

0
0

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:

  1. How do I build the file object that
    needs to be part of the mock drop
    event?
  2. How do I fire the drop event
    such that it gets picked up as if I
    had dropped a file in the div?
  • You must to post comments
0
0

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
  • You must to post comments
Showing 1 result
Your Answer
Post as a guest by filling out the fields below or if you already have an account.
Name*
E-mail*
Website