Upload a File Locally and Through a Grid
Send a file path to an input[type=file], make it work on a remote Grid or cloud with LocalFileDetector, and handle hidden file inputs behind styled buttons.
Problem
The “Upload” button opens a native file dialog Selenium cannot drive. On a Grid, even the right approach fails because the file exists on the test runner, not on the browser machine.
Solution
Never click the button. Send the absolute file path to the <input type="file"> element. For remote drivers, set LocalFileDetector so Selenium uploads the file to the browser’s host first. If the input is hidden, make it visible with a small style change.
Path file = Path.of("src/test/resources/avatar.png").toAbsolutePath();
// Remote (Grid / cloud): transfer the file to the browser host automaticallyif (driver instanceof RemoteWebDriver remote) { remote.setFileDetector(new LocalFileDetector());}
WebElement input = driver.findElement(By.cssSelector("input[type=file]"));
// Styled uploaders often hide the input; unhide it (does not affect the app's handler)((JavascriptExecutor) driver).executeScript( "arguments[0].style.display='block'; arguments[0].style.visibility='visible'; arguments[0].style.opacity=1;", input);
input.sendKeys(file.toString());
new WebDriverWait(driver, Duration.ofSeconds(10)) .until(ExpectedConditions.textToBePresentInElementLocated(By.cssSelector(".upload-status"), "avatar.png"));from pathlib import Pathfrom selenium.webdriver.remote.file_detector import LocalFileDetector
file = Path("tests/resources/avatar.png").resolve()
# Remote (Grid / cloud)driver.file_detector = LocalFileDetector()
inp = driver.find_element(By.CSS_SELECTOR, "input[type=file]")driver.execute_script( "arguments[0].style.display='block'; arguments[0].style.visibility='visible'; arguments[0].style.opacity=1;", inp)
inp.send_keys(str(file))
WebDriverWait(driver, 10).until( EC.text_to_be_present_in_element((By.CSS_SELECTOR, ".upload-status"), "avatar.png"))const path = require('path');const remote = require('selenium-webdriver/remote');
const file = path.resolve('test/resources/avatar.png');
// Remote (Grid / cloud)driver.setFileDetector(new remote.FileDetector());
const input = await driver.findElement(By.css('input[type=file]'));await driver.executeScript("arguments[0].style.display='block'; arguments[0].style.visibility='visible'; arguments[0].style.opacity=1;", input);
await input.sendKeys(file);
await driver.wait(until.elementTextContains(driver.findElement(By.css('.upload-status')), 'avatar.png'), 10000);var file = Path.GetFullPath("Resources/avatar.png");
// Remote (Grid / cloud)if (driver is RemoteWebDriver remote) remote.FileDetector = new LocalFileDetector();
var input = driver.FindElement(By.CssSelector("input[type=file]"));((IJavaScriptExecutor)driver).ExecuteScript( "arguments[0].style.display='block'; arguments[0].style.visibility='visible'; arguments[0].style.opacity=1;", input);
input.SendKeys(file);
new WebDriverWait(driver, TimeSpan.FromSeconds(10)) .Until(d => d.FindElement(By.CssSelector(".upload-status")).Text.Contains("avatar.png"));Why It Works
Browsers treat sendKeys on a file input as the user choosing that path, and fire change so the app’s upload handler runs. LocalFileDetector intercepts the sendKeys path, POSTs the file to the remote session’s /file endpoint, and substitutes the remote path. Unhiding the input is only necessary because WebDriver refuses to interact with elements that are not displayed.
Gotchas
- Multiple files: join paths with
\nformultipleinputs. - Drag-and-drop-only uploaders without a file input need a JavaScript
DataTransfershim; see the File Uploads lesson. - Do not use
LocalFileDetectoron a local driver; it is harmless but wastes time. The Java check above only sets it onRemoteWebDriver. - Some cloud vendors restrict upload size and file types; check their documentation.