_python
TL;DR - Show Me The Code
import os
import shutil
import pytest
import tempfile
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
@pytest.fixture
def setup_teardown():
download_dir = tempfile.mkdtemp()
options = webdriver.FirefoxOptions()
options.set_preference("browser.download.dir", download_dir)
options.set_preference("browser.download.folderList", 2)
options.set_preference("browser.helperApps.neverAsk.saveToDisk",
"images/jpeg, application/pdf, application/octet-stream")
options.set_preference("pdfjs.disabled", True)
driver = webdriver.Firefox(options=options)
yield driver, download_dir
driver.quit()
shutil.rmtree(download_dir)
def test_file_download(setup_teardown):
driver, download_dir = setup_teardown
driver.get('https://the-internet.herokuapp.com/download')
download_link = driver.find_element(By.CSS_SELECTOR, '.example a')
download_link.click()
# Wait up to 5 seconds for the file to be present (slow connections)
try:
WebDriverWait(driver, 5).until(lambda d: os.listdir(download_dir))
except TimeoutException:
assert False, "no files were downloaded within 5 seconds"
files = os.listdir(download_dir)
files = [os.path.join(download_dir, f) for f in files] # add directory to each filename
assert len(files) > 0, "no files were downloaded"
assert os.path.getsize(files[0]) > 0, "downloaded file was empty"
Code Walkthrough
Importing Libraries
Lines 1 to 8 are pulling in our requisite libraries for interacting with the operating system (e.g., import os
),
creating a temporary directory and cleaning it up, our testing framework (e.g., import pytest
), driving the browser
with Selenium (e.g., from selenium import webdriver
), locator strategy to find the elements
(e.g. from selenium.webdriver.common.by import By
), and Selenium's wait functionality
(e.g., from selenium.webdriver.support.ui import WebDriverWait
).
Setup and Teardown
Lines 12 to 22 are setting up and tearing down the browser instance, a uniquely named temp directory is also created
(e.g., download_dir = tempfile.mkdtemp()
) to download the file. Firefox is then configured 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.
This object is then passed into our instance of Selenium (e.g., driver = webdriver.Firefox(options=options
).
The @pytest.fixture
decorator is used to create a fixture that sets up and tears down the browser. The yield
keyword is used to pause the fixture until the test is completed. After the test is completed, the fixture resumes
and executes the teardown code (in this case, driver.quit()
and delete the temporal directory).
The Test
Lines 24 to 39 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 created in the @pytest.fixture
. We need to wait for the download to finish, so we use a
WebDriverWait
with a 5 second timeout. We then check that the temp directory isn't empty and that the file isn't
empty. After the file downloads, we perform some rudimentary checks to make sure the unique temp directory isn't empty
and then check the file to see that it isn't empty either.
Executing the Test
Before executing the test, we need to install the required libraries. We can do this by running
pip install -r requirements.txt
from the command-line. The requirements.txt
file contains the libraries we
need to install.
Toggle to see the requirements.txt
file.
selenium==4.26.1
pytest
After installing the required libraries, we can run the test by executing pytest download.py
from the command-line.