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.

Advertisement

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. sendKeys usually 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 multiple attribute, 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.

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.

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.

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.