Uploads and Downloads


If you upload or download files to a website, you will typically see a file dialog appear to allow you to select the file location(s).

Neither webdriver nor node handle local system dialogs, so what can you do?

Handling Uploads

A modern website should be handling uploads using the HTML5 file input element, such as:


    <input id="fileupload" class="fileupload" type="file" name="files[]" multiple=">                
                

What this allows us to do is set the filepath directly on the element, so there is no need for any dialog handler. It really is this simple:


    $("#fileupload").set(filePath);
                

Handling Downloads

Assuming that you click some sort of element to initiate the download, I would suggest using the browser's default download folder and clearing out any matching files before you begin. Then you simply wait for the file to appear:


    //clear all csv files out of the download folder
    try {
        files.wipeFolder(downloadFolder, /\.csv$/);
    } catch (e) {
    }

    //We recognise the download button from the tooltip in this AUT
    $("//li[@tooltip='Export to CSV']/a/i").click(); 

    //now poll the downloadFolder
    var csvCount = false;
    while (!csvCount) {
        Delay(1e3);
        //note the synchronous method - what else can you do here but wait?
        fs.readdirSync(downloadFolder).forEach(function waitForCSVs(download) {
            if (download.match(/\.csv$/)) {
                csvCount = true;
            }
        });    
    }

    //then do something with the file
                

Firefox download confirmation

By default, Firefox will annoyingly ask you to confirm the download location. You can suppress this behaviour when you instantiate the browser:


    var wd = require("webdriver-sync");
    var profile = new FirefoxProfile();
    profile.setPreference("browser.helperApps.neverAsk.saveToDisk", "text/csv");
    var driver = new wd["FirefoxDriver"](profile);                
                

Internet Explorer download confirmation

This browser will pop up an out of process dialog every time you try to download something, so clicking on a link will freeze webdriver as it waits for the download to complete.

This annoyance can be circumvented by using keypresses to activate and confirm the download. Webdriver can handle a key press on an element, but we need another npm module, ROBOTJS to send key presses to the download security theatre dialog.


    var KB = require("node_keyboard");

    //these keypresses are handled by webdriver
    $("a").sendKeys(PTL.WD.Keys.ENTER);
    //sometimes the system gets confused and uses the Alt-S keyboard shortcut to open the start menu
    //so tap the link again to maintain focus
    Delay(1e3);
    $("a").sendKeys(PTL.WD.Keys.ENTER);
    
    Delay(5e3);
    //robotjs handles the download bar
    var robot = require("robotjs");
    robot.keyToggle("s", "down", "alt");
    robot.keyToggle("s", "up", "alt");
    //depending on IE's settings, you may require an additional Alt-Q shortcut to close the dialog
                

But what if there is no download button?

Say you have a website that allows you to upload images. How can you check that the image you see on the site is the same one you uploaded?

Compare an uploaded image to the original file

Using the sh function to run wget locally to download the image as a file.

Compare the downloaded file to the original that was uploaded earlier.

This is a good example of using the correct tool for the job:

  • Webdriver to get the image properties and manage cookies.
  • wget to grab a file from the remote server.
  • node.js to handle the file locally.

    function verifyImageUpload(img, original) {
        var imgSrc = $(img).getAttribute("src");
        // wget needs session cookie for authentication
        var cookie = browser.manage().getCookies().toString().split(/;/)[0].split(/=/);
        sh.run("wget -O " + testing.TEST_HOME + "topology/layout.png " 
            + "--no-cookies --header \"Cookie: " + cookie[0] + "=" + cookie[1] + "\""
            + " " + imgSrc);
        var original64 = fs.readFileSync(original).toString("base64");
        var download64 = fs.readFileSync(testing.TEST_HOME + "topology/layout.png").toString("base64");
        //delete the download to keep the folder clean
        fs.unlinkSync(testing.TEST_HOME + "topology/layout.png");
        return (original64 == download64);
    }
                

Zipped downloads

Sometimes you are not downloading single files but .zip (or equivalent) archives.

The adm-zip npm module will allow you to interrogate individual files in the archive.

You can check the integrity of the archive by checking the file names, sizes, CRC values, etc. of a known good archive and using these as a basis for comparison.

Then your test could do something like this:



    var AdmZip = require("adm-zip");

    function checkDownloadedZip(expectedCRCs) {
        files.wipeFolder(testing.DOWNLOADS, /\.zip$/);
        $("'Download'").click();
        Delay(25e3);//or run a specific loop to check the download is complete
        var unzippedModel = new AdmZip(testing.DOWNLOADS + "Downloaded.zip");
        var zipContents = unzippedModel.getEntries();
        zipContents.forEach(function (zipFile, i) {
            var expectedCRC = expectedCRCs[i];
            console.log("CHECKING " + expectedCRC.name);
            assert(expectedCRC.name === zipFile.entryName,
                "File is missing from the downloaded zip");
            assert(zipFile.header.size, 
                "file is empty when the zip is downloaded");
            assert(zipFile.header.crc === expectedCRC.crc,
                "CRC mismatch " + zipFile.header.crc);
        });
        
    }
    checkDownloadedZip(
        [
            {"name": "input.xml", "crc": 1325479318},
            {"name": "smLoad.csv", "crc": 197218698},
            {"name": "smDraw.csv", "crc": 1454915604},
            {"name": "utilityPower.csv", "crc": 2137376351},
            {"name": "weather.csv", "crc": 3328012098}
        ]
    );
                
Next: Browser Logs