Friday, September 24, 2010

HTML5 Drag and Drop Upload Testing

We're building out a testing automation framework at work and one of the most important parts of it involves tests around file uploads. Historically this has been impossible to automate due to security settings in the browser that disallow setting the file path in a file type input element(e.g. to prevent secretly uploading confidential data from client machines). However, some browsers now support configurations to allow disabling this restriction for certain websites. Test automation driver, Selenium, has taken advantage of this and allows a user to set the value of a file type input element. This works great for the simple case of an explicit form and file type input element.

Enter HTML5. Browsers can now respond to the "drop" event and furthermore the XmlHttpRequest specification (http://dev.w3.org/2006/webapi/XMLHttpRequest-2/) has been enhanced to make it possible to programmatically upload a file with javascript (i.e. xhr.send(fileBlob)). Put those two together and an ambitious web developer can now add automatic drag and drop file uploading to their website.

This is great for the look and feel of a website, but presents a challenge for the tester: How do I simulate a file drag and drop event? We were able to solve this issue by skipping the "drag" part and jumping straight to "drop." What does this mean? It means that our test will automatically call the "drop" event handler and provide a mock javascript event to the handler. The handler takes care of creating the xhr and sending it.

There is still one hurdle to overcome. The xhr requires an actual File object (http://dev.w3.org/2006/webapi/FileAPI/). Since it's not possible to create one via standalone javascript, we got around this by creating a new file type input on the page and using Selenium to set its value (a path to a valid file on disk) and then referencing that.

The following Scala snippet uses jQuery for the dirty work:


val s = selenium.getEval("fileUpload1 = window.$j('<input/>').attr({type:'file'}).appendTo('body');")

selenium.text_field("//input[@type='file']").set("path/to/file")


// Trigger the upload event
val mockEventJs = "{originalEvent : {dataTransfer : { files : fileUpload1.get(0).files } } }"

val ss = selenium.getEval("window.uploader.handle_drop(" + mockEventJs + ")")

6 comments:

  1. I tried your example with selenium and php or alternativly in the Selenium IDE.

    After I successfully entered the path to the file in the file-input-field, I have not been able to retrieve the file-object.

    I did not use Jquery to retrieve it, but pure JS.

    Tried this code

    var file = document.getElementById('xyz').files[0];

    but did no fileobject back. But when I selected the file with the browsebutton and then execute the code

    var file = document.getElementById('xyz').files[0];
    with runscript in Selenium, I get the fileobject. Therefore it looks like I have a filepath in the file-field, but the browser did not create a fileobject so far. Does the use of jquery make a difference at this point?

    Thx

    love and light

    marc

    ReplyDelete
  2. ok, now finally I got it. My basic problem has been, that I needed to generate a valid file-object, and pass this on to an ExtJs-function. Both is not so easy:

    1. in selenium-IDE / getEval you can not use window.Ext to get access to Ext (as you would expect). In the IDE you have to use selenium.browserbot.getWrappedWindow().Ext. Which in turn does not work when using selenium RC via PHPUnit_Extensions_SeleniumTestCase (which propably has nothing to do with phpunit). Via the selenium RC you have to use window.Ext

    2. You have to use getEval and not runScript, because with getEval the script runs in chrome-context and therefore with more rights.

    3. the following ist php-code, based on PHPUnit_Extensions_SeleniumTestCase, which does the job for me:

    $this->type("id_of_input_file", "/path/to/file");

    $this->getEval("var x = window.document.getElementById('xyz').files[0]; window.Ext.getCmp('awesomeId1SaveProject').processDragAndDropFileUpload(x);");


    If you use the IDE instead you have to use getEval-command together with the value:

    var x = selenium.browserbot.getWrappedWindow().document.getElementById('xyz').files[0]; selenium.browserbot.getWrappedWindow().Ext.getCmp('awesomeId1SaveProject').processDragAndDropFileUpload(x);

    hth someone

    marc

    ReplyDelete
  3. Hi, this looks very nice tip, but is there any way for Selenium WD with Java, please help,have a scenario to drag file from file system to webpage, like

    http://html5demos.com/dnd-upload

    Thank you

    ReplyDelete
  4. Nice info regarding HTML5 Drag and Drop Upload Testing My sincere thanks for sharing this post Please Conntinue to share This kind of post
    Selenium Training in Chennai

    ReplyDelete
  5. nice blog has been shared by you. before i read this blog i didn't have any knowledge about this but now i got some knowledge. so keep on sharing such kind of an interesting blogs.
    selenium training in chennai

    ReplyDelete
  6. Hi, thanks for sharing such an informative blog. I have read your blog and I gathered some needful information from your blog. Keep update your blog. Awaiting for your next update.


    Selenium Training in Bangalore

    ReplyDelete