Skip to main content

_javascript

TL;DR - Show Me The Code

2-download-a-file/code/javascript/test/download.spec.js
const assert = require("assert");
const {Builder, By} = require("selenium-webdriver");
const firefox = require("selenium-webdriver/firefox");
const fs = require("fs");
const path = require("path");

describe("Download Test", function () {
let driver;
const tmpDir = path.join(__dirname, "tmp");

beforeEach(async function () {
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
let options = new firefox.Options()
.setPreference("browser.download.dir", tmpDir)
.setPreference("browser.download.folderList", 2)
.setPreference("browser.helperApps.neverAsk.saveToDisk",
"images/jpeg, application/pdf, application/octet-stream"
)
.setPreference("pdfjs.disabled", true);

driver = await new Builder()
.forBrowser("firefox")
.setFirefoxOptions(options)
.build();
});

function cleanupTmpDir() {
if (fs.existsSync(tmpDir)) {
const files = fs.readdirSync(tmpDir).map(file => path.join(tmpDir, file));
files.forEach(file => fs.unlinkSync(file));
fs.rmdirSync(tmpDir);
}
}

afterEach(async function () {
await driver.quit();
cleanupTmpDir();
});

it("should automatically download to local disk", async function () {
await driver.get("https://the-internet.herokuapp.com/download");
await driver.findElement(By.css(".example a")).click();

// Wait for the file to be present up to 5 seconds using Selenium's built-in wait mechanism
await driver.wait(async function () {
const files = fs.readdirSync(tmpDir);
return files.length > 0;
}, 5000);

const files = fs.readdirSync(tmpDir).map(file => path.join(tmpDir, file));
assert(files.length);
assert(fs.statSync(files[0]).size);
});
});

Code Walkthrough

Importing Libraries

Lines 1 to 5 are pulling in our requisite classes for interacting with the operating system (e.g., require("path");), our testing framework (e.g., mocha and require("assert")), and driving the browser with Selenium (e.g., const {Builder, By} = require("selenium-webdriver");).

Setup and Teardown

After creating a describe to group our tests (e.g., describe("Download Test")...), lines 11 to 38 contain the setup and teardown, and then we are declaring two variables. One for the Selenium instance (e.g., driver) and the other for the temporary directory where we'll want to automatically download files to (e.g., tmpDir). In it we're storing the absolute path to the current working directory, plus the name tmp.

In the setup method (e.g., beforeEach) we create this directory if it's not already there, and then create a new browser options object (for Firefox in this case), specifying the necessary configuration parameters to make it automatically download the file where we want (e.g., in the newly created temp directory).

Here's a breakdown of each of the browser preferences being set:

  • browser.download.dir accepts a string. This is how we set the custom download path. It needs to be an absolute path.
  • browser.download.folderList takes a number. It tells Firefox which download directory to use. 2 tells it to use a custom download path, whereas 1 would use the browser's default path, and 0 would place them on the Desktop.
  • browser.helperApps.neverAsk.saveToDisk tells Firefox when not to prompt for a file download. It accepts a string of the file's MIME type. If you want to specify more than one, you do it with a comma-separated string (which we've done).
  • pdfjs.disabled is for when downloading PDFs. This overrides the sensible default in Firefox that previews PDFs in the browser. It accepts a boolean.

We then hand the options object on to Selenium as part of the incantation to create a new browser instance.

In the teardown (e.g., beforeEach) we close the browser instance and then clean up the temp directory by deleting its contents, and then the directory itself.

The Test

Lines 48 to 53 are the test itself.

After visiting the page we find the first download link and click it. The click triggers an automatic download to the temp directory. After the file downloads, we wait using Selenium's wait method to make sure the file is completely downloaded before we check its size. Then, we perform some rudimentary checks to make sure the temp directory contains files and the first file in the directory is not empty.

Executing the Test

Before executing the test, we need to make sure the required dependencies are declared on the package.json file.

Toggle to see the package.json file.
2-download-a-file/code/javascript/package.json
{
"dependencies": {
"selenium-webdriver": "4.26.0"
},
"description": "A project to showcase downloading a file using official WebdriverJs",
"devDependencies": {
"mocha": "10.8.2"
},
"license": "MIT",
"name": "download-a-file",
"scripts": {
"test": "npx mocha test/**/*.spec.js --timeout 60000"
},
"version": "1.0.0"
}

Finally, we can run the test by executing npm test from the command-line.