_javascript
TL;DR - Show Me The Code
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, whereas1
would use the browser's default path, and0
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.
{
"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.