Selenium File Upload
These are the "advanced" Selenium tasks that come up in real projects and interviews: uploading files, finding broken links, waiting for Ajax content, dealing with hidden elements and working with dynamic tables. Each section gives the approach, Selenium 4 code, and the edge cases that trip people up. The broken-link checker was compiled and run against a local test server — the output is real.
1. File Upload
The standard way: sendKeys on the file input ⭐
Path file = Paths.get("src/test/resources/testdata/invoice.pdf").toAbsolutePath();
driver.findElement(By.cssSelector("input[type='file']")).sendKeys(file.toString());
driver.findElement(By.id("upload-button")).click();
new WebDriverWait(driver, Duration.ofSeconds(15))
.until(ExpectedConditions.textToBePresentInElementLocated(By.id("uploaded-files"), "invoice.pdf"));
Selenium can't click through the operating system's file dialog, so you don't open it at all — you type the file's absolute path into the <input type="file"> element. Practice page: The Internet – File Upload.
Edge cases
- Hidden file input — many styled upload buttons hide the real input.
sendKeysusually still works on it; if the page throws "element not interactable", make it visible with JavaScript first (arguments[0].style.display='block') — only for the input, never to bypass a real UI bug. - Multiple files — if the input has the
multipleattribute, join absolute paths with a newline:sendKeys(path1 + "\n" + path2). - Selenium Grid or cloud — the file must reach the remote machine. Set
((RemoteWebDriver) driver).setFileDetector(new LocalFileDetector())and Selenium uploads the local file to the node before typing the path. - Drag-and-drop upload zones — usually there's still a hidden file input underneath; find it and use
sendKeys.
When there's no file input: AutoIt and Robot
If an application opens a native dialog without a file input (rare in modern web apps), Selenium can't reach it. Older workarounds are AutoIt (a Windows-only scripting tool) and Java's Robot class (types keys at the OS level; cross-platform but fragile, and it doesn't work headless or on remote grids). Prefer asking developers for a proper file input, or testing the upload through the API.
2. Finding Broken Links ⭐
Selenium collects the links; an HTTP client checks them. Java's built-in HttpClient is enough:
static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NEVER)
.build();
static int status(String url) throws Exception {
HttpRequest head = HttpRequest.newBuilder(URI.create(url))
.method("HEAD", HttpRequest.BodyPublishers.noBody()).timeout(Duration.ofSeconds(10)).build();
int code = CLIENT.send(head, HttpResponse.BodyHandlers.discarding()).statusCode();
if (code == 405 || code == 501) { // server doesn't support HEAD → retry with GET
HttpRequest get = HttpRequest.newBuilder(URI.create(url)).GET().timeout(Duration.ofSeconds(10)).build();
code = CLIENT.send(get, HttpResponse.BodyHandlers.discarding()).statusCode();
}
return code;
}
// In the test
List<String> broken = new ArrayList<>();
for (WebElement a : driver.findElements(By.tagName("a"))) {
String href = a.getDomProperty("href"); // absolute URL, resolved by the browser
if (href == null || !href.startsWith("http")) continue; // skip mailto:, javascript:, #anchors
try {
int code = status(href);
if (code >= 400) broken.add(href + " -> " + code);
} catch (Exception e) {
broken.add(href + " -> " + e.getClass().getSimpleName()); // DNS failure, timeout…
}
}
Assert.assertTrue(broken.isEmpty(), "Broken links: " + broken);
Output from a run against a local server returning different statuses:
200 http://127.0.0.1:8770/ok
404 http://127.0.0.1:8770/missing
500 http://127.0.0.1:8770/error
301 http://127.0.0.1:8770/moved
200 http://127.0.0.1:8770/no-head ← HEAD returned 405, the GET fallback succeeded
skip mailto:qa@example.com
skip javascript:void(0)
skip #top
Broken: [.../missing -> 404, .../error -> 500]
- HEAD first — it returns only headers, so it's much faster than downloading every page. Some servers reject HEAD with 405, hence the GET fallback.
- Use
getDomProperty("href")— it gives the absolute URL even when the HTML has a relative link like/about. - Collect, then assert once — so one run reports every broken link.
- Be polite — for large sites, deduplicate URLs, run checks in parallel with a limit, and only check your own domains.
The same approach checks broken images: collect img elements and test their src, or check naturalWidth == 0 with JavaScript.
3. Ajax Content
Ajax updates part of the page without reloading it, so the element you want may not exist — or may still show old data — when the action finishes. Wait for the specific condition:
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.findElement(By.id("search")).sendKeys("laptop");
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector(".spinner")));
wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".result-row"), 0));
Interview answer: "Ajax loads content asynchronously, so I use an explicit wait for the exact condition — the spinner disappearing, results appearing, or a text change — rather than a fixed sleep. That makes the test as fast as the page allows and reliable when it's slow."
In Selenium 4, new WebDriverWait(driver, Duration.ofSeconds(10)) replaces the old new WebDriverWait(driver, 10), and implicitlyWait(Duration.ofSeconds(…)) replaces the TimeUnit version. More: Selenium Waits.
4. Hidden Elements
Selenium imitates a real user, so it refuses to click or type into elements a user couldn't see — ElementNotInteractableException. The right fix is to do what a user would do to reveal it: open the menu, expand the section, scroll, or wait for it to become visible.
// Is it there but hidden?
boolean present = !driver.findElements(By.id("promo")).isEmpty();
boolean visible = present && driver.findElement(By.id("promo")).isDisplayed();
// Reading text: getText() returns "" for hidden elements
WebElement hidden = driver.findElement(By.id("tracking-code"));
String text = hidden.getDomProperty("textContent"); // works whether visible or not
textContent returns the raw text even when hidden; innerText (via JavaScript) returns only rendered text. Clicking hidden elements with JavaScript is possible but proves nothing about what users can do — reserve it for setup steps, never for the behavior under test.
5. Dynamic Web Tables
Row order and count change, so never hard-code //tr[2]/td[3]. Find the row by a value you know, then move within it:
// Click the checkbox in John's row
driver.findElement(By.xpath("//td[text()='John']/following-sibling::td/input")).click();
// Read the salary from the row whose Name is "John"
String salary = driver.findElement(By.xpath("//tr[td[normalize-space()='John']]/td[3]")).getText();
// Find a column by its header name instead of its position
List<WebElement> headers = driver.findElements(By.cssSelector("#employees thead th"));
int salaryCol = 1;
for (int i = 0; i < headers.size(); i++) {
if (headers.get(i).getText().equals("Salary")) { salaryCol = i + 1; break; } // XPath indexes start at 1
}
List<WebElement> salaries = driver.findElements(By.xpath("//table[@id='employees']/tbody/tr/td[" + salaryCol + "]"));
Finding the column by header text keeps the test working when columns are reordered — a common follow-up question. For paginated tables, loop through the pages until the row is found or the "Next" button is disabled. Locator reference: Selenium Locators Cheat Sheet.
findElement vs findElements
findElement() |
findElements() |
|---|---|
| Returns the first matching element | Returns all matching elements as a List |
Throws NoSuchElementException if none |
Returns an empty list if none |
| Use to act on one element | Use to count, loop, or check absence |
See how each command — find, sendKeys, click — travels from your test to the browser in the Selenium WebDriver Visualizer.
From Real Projects
In my automation roles I created, modified and enhanced Selenium WebDriver scripts in Java, organized with POM classes and a business library, and ran them with TestNG on Chrome and Firefox. Exercises like these build exactly the muscle memory you need when a real application — for me, Canolog's sales, inventory and service screens — throws a new kind of element at you. Wait for the AJAX result itself, not for a fixed amount of time.
FAQs
How do you upload a file in Selenium?
Call sendKeys() with the file's absolute path on the <input type="file"> element — don't open the OS dialog. On a remote grid, set a LocalFileDetector.
Can Selenium handle the Windows file dialog?
No — it's outside the browser. Use the file input; AutoIt (Windows only) and Robot are last-resort workarounds.
How do you find broken links?
Collect href values with findElements(By.tagName("a")), send an HTTP HEAD request to each (falling back to GET), and report every link with status 400 or above.
Why use HEAD instead of GET for link checks?
HEAD returns only headers, so it's faster; fall back to GET when a server rejects HEAD with 405.
How do you handle Ajax in Selenium?
With explicit waits for the specific condition — a spinner disappearing, elements appearing, or text changing — instead of fixed sleeps.
How do you read text from a hidden element?
getText() returns an empty string for hidden elements; read getDomProperty("textContent") instead.
Related
- Selenium Actions Class
- Alerts, Frames & Windows
- Selenium Scenario-Based Questions
- The Complete Selenium WebDriver Guide