Easy automation of JavaScript form testing

If you are writing unit tests that provide coverage for HTML forms then there is an easy, pure JavaScript way to automate testing of the underlying code that works on modern browsers. The nice thing about this approach is you don’t have to manually load a file every time you run the tests. You still need to test the HTML interface components but that’s a topic for a different blog post.

The concept is straightforward in that you need to emulate the underlying functionality of an HTML form. The good news is you don’t have to programmatically create an HTML Form or Input element. Here’s the pattern you need to follow:

  1. Create an xhr request to retrieve the file. Be sure to set the response type as blob.
  2. Take the xhr.response and create a new File Object using the File API.
  3. Inject the File Object into a fake Form Object or,
  4. You can also use FormData() to create an actual Form Object.
  5. The fake Form Object is now ready to pass into your unit tests. Cool!

Here’s how you create a JavaScript FormData Object. Depending on what data your code expects you can add additional key/value pairs using append():

var formData = new FormData();
formData.append("files",/* file array */files);

And, here’s what the basic pattern looks like to retrieve the file, process it and then make it available for your unit tests:


var formNode; // Unit tests can access form node via this global

function retrieveFile(){

    var xhr = new XMLHttpRequest();
    xhr.open("GET","images/blue-pin.png",true); //set path to any file
    xhr.responseType = "blob"; 

    xhr.onload = function()
    {
        if( xhr.status === 200)
        {
            var files = []; // This is our files array

            // Manually create the guts of the File
            var blob = new Blob([this.response],{type: this.response.type});
            var bits = [blob,"test", new ArrayBuffer(blob.size)];

            // Put the pieces together to create the File.
            // Typically the raw response Object won't contain the file name
            // so you may have to manually add that as a property.
            var file = new File(bits,"blue-pin.png",{
                lastModified: new Date(0),
                type: this.response.type
            });

            files.push(file);

            // In this pattern we are faking a form object
            // and adding the files array to it.
            formNode = {
                elements:[
                    {type:"file",
                        files:files}
                ]
            };

            // Last, now run your unit tests
            runYourUnitTestsNow();
        }
        else
        {
            console.log("Retrieve file failed");
        }
    };
    xhr.onerror = function(e)
    {
        console.log("Retrieved file failed: " + JSON.stringify(e));
    };

    xhr.send(null);
}