File upload tests fail when browser automation clicks the native file picker instead of assigning the file to the page's file input. Selenium avoids that dialog by sending an absolute path to an input[type=file] element, which lets the test run in headless browsers and CI jobs without desktop interaction.
A passing upload check should observe the file input accepting the path, the form being submitted, and a page state that names the expected file. Checking only that the input exists can miss broken submit handlers, server-side validation failures, or frontend status text that never updates after the upload.
Remote WebDriver and Selenium Grid add a filesystem boundary because the test code and browser node may run on different machines. Keep the uploaded file on the test runner, send an absolute path, and use a local file detector for remote sessions when the browser node needs Selenium to transfer the file before submission.
from pathlib import Path import shutil import textwrap from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait fixture_dir = Path("upload-fixture") fixture_dir.mkdir(exist_ok=True) sample_file = fixture_dir / "sample-upload.txt" sample_file.write_text("Selenium upload fixture\n", encoding="utf-8") html_file = fixture_dir / "upload.html" html_file.write_text( textwrap.dedent( """ <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Selenium upload fixture</title> </head> <body> <form id="upload-form"> <label for="file-upload">Upload file</label> <input id="file-upload" name="file" type="file"> <button id="submit" type="submit">Upload</button> </form> <p id="result" aria-live="polite"></p> <script> document.getElementById("upload-form").addEventListener("submit", event => { event.preventDefault(); const file = document.getElementById("file-upload").files[0]; document.getElementById("result").textContent = file ? `uploaded: ${file.name} (${file.size} bytes)` : "no file selected"; }); </script> </body> </html> """ ).strip(), encoding="utf-8", ) options = Options() options.add_argument("--headless=new") options.add_argument("--window-size=1280,720") for browser_name in ("google-chrome", "chromium", "chromium-browser"): browser_path = shutil.which(browser_name) if browser_path: options.binary_location = browser_path break driver_path = shutil.which("chromedriver") service = Service(driver_path) if driver_path else None driver = webdriver.Chrome(service=service, options=options) try: driver.get(html_file.resolve().as_uri()) file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']") file_input.send_keys(str(sample_file.resolve())) driver.find_element(By.ID, "submit").click() WebDriverWait(driver, 5).until( EC.text_to_be_present_in_element((By.ID, "result"), "uploaded: sample-upload.txt") ) selected_value = file_input.get_attribute("value") selected_name = selected_value.replace("\\", "/").split("/")[-1] result_text = driver.find_element(By.ID, "result").text print(f"selected: {selected_name}") print(f"result: {result_text}") finally: driver.quit()
The fixture writes its own HTML page and sample file so the upload path can be checked without depending on a live application.
$ python3 upload_file_test.py selected: sample-upload.txt result: uploaded: sample-upload.txt (24 bytes)
If Chrome does not launch, install Selenium for Python or configure the browser driver first.
Related: How to install Selenium WebDriver for Python
Related: How to configure ChromeDriver for Selenium
driver.get("https://app.example.net/uploads")
file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']")
file_input.send_keys(str(Path("sample-upload.txt").resolve()))
driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
Use the real input[type=file] element. Selenium sends the path to the file input instead of typing into the operating system file picker.
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, ".upload-status"), "sample-upload.txt")
)
For Remote WebDriver or Selenium Grid, set driver.file_detector = LocalFileDetector() before send_keys() when the local test runner must transfer the file to the browser node.
Related: How to use explicit waits in Selenium
Related: How to connect to a remote Selenium WebDriver
$ rm -r upload-fixture