How to send files to a Dropzone.JS element in Selenium

In one of my automation projects, the project used the Dropzone.JS JavaScript extension to handle their file uploads. With Dropzone, it generates a hidden <input> that Selenium cannot .SendKeys() to like a normal <input> field and using programs like AutoIt and the Windows Send Keys are not stable and reliable. After a few hours of trying different methods like sending a HTTP request, sending keys to different elements, and executing JavaScript, I was able to find a solution.

When debugging through the browser console, I found that Dropzone has a method called .forElement('#selectorToUse'). I was able to find the element and store it:

var myZone = Dropzone.forElement('#seletorToUse');  

I tried multiple ideas like passing a local file and a web image, but to no luck. After some tinkering and research, I found that I could generate a file in JavaScript and force the upload. I first needed to create an image in Base64 like:

Black square

base64Image = '/9j/4AAQSkZJRgABAQEASABIAAD/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIABYAFgMBEQACEQEDEQH/xABQAAADAQAAAAAAAAAAAAAAAAAAAQIHEAEBAAAAAAAAAAAAAAAAAAAAEQEBAQEAAAAAAAAAAAAAAAAAAAECEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDH6jR0BQSBgATQMAD/2Q==';  

Once the Base64 image is created, we need to change it into a Blob. Here is the function I used:

function base64toBlob(b64Data, contentType, sliceSize) {  
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    var blob = new Blob(byteArrays, {type: contentType});
    return blob;
}

Once the function has been created, I then created the blob and gave it a name:

var blob = base64toBlob(base64Image, 'image / png');  
blob.name = 'testfile.png';  

After the blob has been created, I could add the file to the Dropzone element by:

myZone.addFile(blob);  

Success!! The file was added correctly. Now how do we get it to work with Selenium? With the JavaScriptExecutor of course! The limiting factor of the JavaScriptExecutor is the need to build the function out in one long string. Here is the example code that I built:

IJavaScriptExecutor js = driver as IJavaScriptExecutor;  
js.ExecuteScript("var myZone, blob, base64Image; myZone = Dropzone.forElement('#receiptDropzone');" +  
  @"base64Image = '/9j/4AAQSkZJRgABAQEASABIAAD/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIABYAFgMBEQACEQEDEQH/xABQAAADAQAAAAAAAAAAAAAAAAAAAQIHEAEBAAAAAAAAAAAAAAAAAAAAEQEBAQEAAAAAAAAAAAAAAAAAAAECEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDH6jR0BQSBgATQMAD/2Q==';" +
  "function base64toBlob(r,e,n){e=e||\"\",n=n||512;for(var t=atob(r),a=[],o=0;o<t.length;o+=n){for(var l=t.slice(o,o+n),h=new Array(l.length),b=0;b<l.length;b++)h[b]=l.charCodeAt(b);var v=new Uint8Array(h);a.push(v)}var c=new Blob(a,{type:e});return c}" +
   "blob = base64toBlob(base64Image, 'image / png');" +
   "blob.name = 'testfile.png';" +
   "myZone.addFile(blob);"
);

Special thanks to Blake Haas for the help