Uploading with Selenium
With Selenium WebDriver it's possible to test file uploads on remote browsers and devices. There are several ways to handle file uploads depending on your use case:
- Upload a file before your test starts — pre-load a file onto the test VM via a URL
-
Upload a file during your test — send a local file to the remote VM with
LocalFileDetector - Upload with WebdriverIO — file upload examples for the WebdriverIO framework
- TestingBot Storage — use TestingBot's internal storage for faster file transfers
- Tips & Best Practices — common pitfalls and recommendations
Which method should I use?
- Use Upload Before Test if you have a fixed test file at a public URL and want it ready on the VM before your test runs.
- Use Upload During Test if your test needs to interact with a file upload UI and send a file from your local machine to the remote browser.
- Use TestingBot Storage if you have large files, private files, or need fast and consistent transfers across multiple test runs.
1. Upload a file before your test starts
With TestingBot you can specify a URL in your capabilities which links to the file you want to be present on our test VMs. TestingBot will download the file and save it on the test VM before your test begins, so it's ready to use immediately.
Use the following tb:options capabilities:
upload- The URL of the file to download onto the test VM. This can be a public URL or a
tb://URL from TestingBot Storage. uploadFilepath- The absolute file path on the test VM where TestingBot should save the downloaded file (e.g.
C:\test\logo.pngon Windows).
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class JavaSample {
public static final String URL = "https://hub.testingbot.com/wd/hub";
public static void main(String[] args) throws Exception {
ChromeOptions options = new ChromeOptions();
options.setPlatformName("WIN11");
options.setBrowserVersion("latest");
Map<String, Object> tbOptions = new HashMap<>();
tbOptions.put("key", "API_KEY");
tbOptions.put("secret", "API_SECRET");
tbOptions.put("name", "Test File Upload");
tbOptions.put("upload", "https://testingbot.com/assets/logo-head.png");
tbOptions.put("uploadFilepath", "C:\\test\\logo.png");
options.setCapability("tb:options", tbOptions);
RemoteWebDriver driver = new RemoteWebDriver(new URL(URL), options);
// No LocalFileDetector needed — file is already on the VM
driver.get("http://the-internet.herokuapp.com/upload");
driver.findElement(By.id("file-upload")).sendKeys("C:\\test\\logo.png");
driver.findElement(By.id("file-submit")).click();
driver.quit();
}
}
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
options = Options()
options.set_capability('platformName', 'WIN11')
options.set_capability('browserVersion', 'latest')
options.set_capability('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload',
'upload': 'https://testingbot.com/assets/logo-head.png',
'uploadFilepath': 'C:\\test\\logo.png'
})
driver = webdriver.Remote(
command_executor='https://hub.testingbot.com/wd/hub',
options=options)
driver.get('http://the-internet.herokuapp.com/upload')
driver.find_element(By.ID, 'file-upload').send_keys('C:\\test\\logo.png')
driver.find_element(By.ID, 'file-submit').click()
driver.quit()
const { Builder, By } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
async function runTest() {
const options = new chrome.Options();
options.set('platformName', 'WIN11');
options.set('browserVersion', 'latest');
options.set('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload',
'upload': 'https://testingbot.com/assets/logo-head.png',
'uploadFilepath': 'C:\\test\\logo.png'
});
const driver = await new Builder()
.usingServer('https://hub.testingbot.com/wd/hub')
.setChromeOptions(options)
.build();
// No FileDetector needed — file is already on the VM
try {
await driver.get('http://the-internet.herokuapp.com/upload');
await driver.findElement(By.id('file-upload')).sendKeys('C:\\test\\logo.png');
await driver.findElement(By.id('file-submit')).click();
console.log(await driver.getTitle());
} finally {
await driver.quit();
}
}
runTest();
require 'rubygems'
require 'test/unit'
require 'selenium-webdriver'
class UploadTest < Test::Unit::TestCase
def setup
options = Selenium::WebDriver::Chrome::Options.new
options.add_option('platformName', 'WIN11')
options.add_option('browserVersion', 'latest')
options.add_option('tb:options', {
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload',
'upload' => 'https://testingbot.com/assets/logo-head.png',
'uploadFilepath' => 'C:\\test\\logo.png'
})
@driver = Selenium::WebDriver.for(
:remote,
url: 'https://hub.testingbot.com/wd/hub',
options: options
)
end
def test_upload
@driver.navigate.to 'http://the-internet.herokuapp.com/upload'
element = @driver.find_element(:id, 'file-upload')
element.send_keys 'C:\\test\\logo.png'
@driver.find_element(:id, 'file-submit').click
assert_equal 'logo.png', @driver.find_element(:id, 'uploaded-files').text
end
def teardown
@driver.quit
end
end
<?php
require_once('vendor/autoload.php');
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Chrome\ChromeOptions;
$options = new ChromeOptions();
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability('platformName', 'WIN11');
$capabilities->setCapability('browserVersion', 'latest');
$capabilities->setCapability('tb:options', [
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload',
'upload' => 'https://testingbot.com/assets/logo-head.png',
'uploadFilepath' => 'C:\\test\\logo.png'
]);
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create(
'https://hub.testingbot.com/wd/hub',
$capabilities
);
$driver->get('http://the-internet.herokuapp.com/upload');
// No LocalFileDetector needed — file is already on the VM
$file_input = $driver->findElement(WebDriverBy::id('file-upload'));
$file_input->sendKeys('C:\\test\\logo.png');
$driver->findElement(WebDriverBy::id('file-submit'))->click();
$driver->quit();
?>
using System;
using System.Collections.Generic;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
namespace SeleniumTest
{
class Program
{
static void Main(string[] args)
{
ChromeOptions options = new ChromeOptions();
options.PlatformName = "WIN11";
options.BrowserVersion = "latest";
var tbOptions = new Dictionary<string, object>
{
["key"] = "API_KEY",
["secret"] = "API_SECRET",
["name"] = "Test File Upload",
["upload"] = "https://testingbot.com/assets/logo-head.png",
["uploadFilepath"] = "C:\\test\\logo.png"
};
options.AddAdditionalOption("tb:options", tbOptions);
// No LocalFileDetector needed — file is already on the VM
IWebDriver driver = new RemoteWebDriver(
new Uri("https://hub.testingbot.com/wd/hub"), options);
driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/upload");
IWebElement upload = driver.FindElement(By.Id("file-upload"));
upload.SendKeys("C:\\test\\logo.png");
driver.FindElement(By.Id("file-submit")).Click();
driver.Quit();
}
}
}
2. Upload a file during your test
You can use LocalFileDetector to upload a file from your local computer to the remote VM/device during your test.
The file is automatically transferred over the WebDriver protocol before being sent to the file input element.
Important: You must set LocalFileDetector on the driver before calling sendKeys on the file input.
Without it, Selenium will try to find the file path on the remote machine instead of your local machine.
In the example below, we upload an image residing on your own computer (/Users/test/logo.png) via the remote VM/device in the TestingBot cloud to a website.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.LocalFileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class JavaSample {
public static final String URL = "https://hub.testingbot.com/wd/hub";
public static void main(String[] args) throws Exception {
ChromeOptions options = new ChromeOptions();
options.setPlatformName("WIN11");
options.setBrowserVersion("latest");
Map<String, Object> tbOptions = new HashMap<>();
tbOptions.put("key", "API_KEY");
tbOptions.put("secret", "API_SECRET");
tbOptions.put("name", "Test File Upload");
options.setCapability("tb:options", tbOptions);
RemoteWebDriver driver = new RemoteWebDriver(new URL(URL), options);
// Must set LocalFileDetector before uploading
driver.setFileDetector(new LocalFileDetector());
driver.get("http://the-internet.herokuapp.com/upload");
driver.findElement(By.id("file-upload")).sendKeys("/Users/test/logo.png");
driver.findElement(By.id("file-submit")).click();
driver.quit();
}
}
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
options = Options()
options.set_capability('platformName', 'WIN11')
options.set_capability('browserVersion', 'latest')
options.set_capability('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload'
})
driver = webdriver.Remote(
command_executor='https://hub.testingbot.com/wd/hub',
options=options)
# Python's Remote WebDriver uses LocalFileDetector by default
driver.get('http://the-internet.herokuapp.com/upload')
driver.find_element(By.ID, 'file-upload').send_keys('/Users/test/logo.png')
driver.find_element(By.ID, 'file-submit').click()
driver.quit()
const { Builder, By } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const remote = require('selenium-webdriver/remote');
async function runTest() {
const options = new chrome.Options();
options.set('platformName', 'WIN11');
options.set('browserVersion', 'latest');
options.set('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload'
});
const driver = await new Builder()
.usingServer('https://hub.testingbot.com/wd/hub')
.setChromeOptions(options)
.build();
// Must set FileDetector for remote file uploads
driver.setFileDetector(new remote.FileDetector());
try {
await driver.get('http://the-internet.herokuapp.com/upload');
await driver.findElement(By.id('file-upload')).sendKeys('/Users/test/logo.png');
await driver.findElement(By.id('file-submit')).click();
console.log(await driver.getTitle());
} finally {
await driver.quit();
}
}
runTest();
require 'rubygems'
require 'test/unit'
require 'selenium-webdriver'
class UploadTest < Test::Unit::TestCase
def setup
options = Selenium::WebDriver::Chrome::Options.new
options.add_option('platformName', 'WIN11')
options.add_option('browserVersion', 'latest')
options.add_option('tb:options', {
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload'
})
@driver = Selenium::WebDriver.for(
:remote,
url: 'https://hub.testingbot.com/wd/hub',
options: options
)
# Ruby's Remote WebDriver uses LocalFileDetector by default
end
def test_upload
@driver.navigate.to 'http://the-internet.herokuapp.com/upload'
element = @driver.find_element(:id, 'file-upload')
element.send_keys '/Users/test/logo.png'
@driver.find_element(:id, 'file-submit').click
assert_equal 'logo.png', @driver.find_element(:id, 'uploaded-files').text
end
def teardown
@driver.quit
end
end
<?php
require_once('vendor/autoload.php');
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Remote\LocalFileDetector;
use Facebook\WebDriver\Chrome\ChromeOptions;
$options = new ChromeOptions();
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability('platformName', 'WIN11');
$capabilities->setCapability('browserVersion', 'latest');
$capabilities->setCapability('tb:options', [
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload'
]);
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create(
'https://hub.testingbot.com/wd/hub',
$capabilities
);
$driver->get('http://the-internet.herokuapp.com/upload');
$file_input = $driver->findElement(WebDriverBy::id('file-upload'));
// Must set LocalFileDetector on the element before sendKeys
$file_input->setFileDetector(new LocalFileDetector());
$file_input->sendKeys('/Users/test/logo.png');
$driver->findElement(WebDriverBy::id('file-submit'))->click();
$driver->quit();
?>
using System;
using System.Collections.Generic;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
namespace SeleniumTest
{
class Program
{
static void Main(string[] args)
{
ChromeOptions options = new ChromeOptions();
options.PlatformName = "WIN11";
options.BrowserVersion = "latest";
var tbOptions = new Dictionary<string, object>
{
["key"] = "API_KEY",
["secret"] = "API_SECRET",
["name"] = "Test File Upload"
};
options.AddAdditionalOption("tb:options", tbOptions);
IWebDriver driver = new RemoteWebDriver(
new Uri("https://hub.testingbot.com/wd/hub"), options);
// Must set LocalFileDetector before uploading
var allowsDetection = driver as IAllowsFileDetection;
allowsDetection.FileDetector = new LocalFileDetector();
driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/upload");
IWebElement upload = driver.FindElement(By.Id("file-upload"));
upload.SendKeys("/Users/test/logo.png");
driver.FindElement(By.Id("file-submit")).Click();
driver.Quit();
}
}
}
3. Upload with WebdriverIO
WebdriverIO handles file uploads differently from raw Selenium. It provides a built-in uploadFile command on the browser object that transfers a local file to the remote machine and returns the remote path.
Upload a file before your test starts
You can use the upload and uploadFilepath capabilities with WebdriverIO, just like with Selenium.
// wdio.conf.js
exports.config = {
user: 'API_KEY',
key: 'API_SECRET',
capabilities: [{
browserName: 'chrome',
browserVersion: 'latest',
platformName: 'WIN11',
'tb:options': {
name: 'WebdriverIO File Upload',
upload: 'https://testingbot.com/assets/logo-head.png',
uploadFilepath: 'C:\\test\\logo.png'
}
}],
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
protocol: 'https'
};
Then in your test, use the pre-loaded file path:
describe('File Upload', () => {
it('should upload a pre-loaded file', async () => {
await browser.url('http://the-internet.herokuapp.com/upload');
const fileInput = await $('#file-upload');
await fileInput.setValue('C:\\test\\logo.png');
await $('#file-submit').click();
const uploadedFile = await $('#uploaded-files');
await expect(uploadedFile).toHaveText('logo.png');
});
});
Upload a local file during your test
WebdriverIO's browser.uploadFile() method transfers a file from your local machine to the remote VM and returns the remote file path. Use this path with setValue() on the file input.
describe('File Upload', () => {
it('should upload a local file during the test', async () => {
// Transfer file to remote machine, returns remote path
const remotePath = await browser.uploadFile('/Users/test/logo.png');
await browser.url('http://the-internet.herokuapp.com/upload');
const fileInput = await $('#file-upload');
await fileInput.setValue(remotePath);
await $('#file-submit').click();
const uploadedFile = await $('#uploaded-files');
await expect(uploadedFile).toHaveText('logo.png');
});
});
The browser.uploadFile() command is only available when running tests on a remote Selenium grid (like TestingBot). It will not work when running tests locally.
4. TestingBot Storage
With TestingBot Storage, you can upload your files to our servers ahead of time. The advantage is that our test VMs can immediately download your file from our own network, which is much faster than downloading from the public internet.
TestingBot Storage is recommended for large files or when you need consistent upload speeds across test runs. Files are stored securely and available for 60 days.
Step 1: Upload your file to TestingBot Storage
First, upload your file to TestingBot Storage using the REST API. The API returns a tb:// URL that you'll use in your test capabilities.
Upload with curl
curl -X POST "https://api.testingbot.com/v1/storage" \
-u API_KEY:API_SECRET \
-F "file=@/path/to/your/file.png"
The API responds with a JSON object containing the tb:// URL:
{"app_url": "tb://398eijf83i"}
You can also upload a file from a remote URL instead of a local file:
curl -X POST "https://api.testingbot.com/v1/storage" \
-u API_KEY:API_SECRET \
-d "url=https://example.com/myfile.png"
Upload with an API client
import com.testingbot.TestingbotREST;
import com.testingbot.models.TestingbotStorageUploadResponse;
import java.io.File;
TestingbotREST restApi = new TestingbotREST(
"API_KEY",
"API_SECRET");
// Upload a local file
TestingbotStorageUploadResponse response = restApi.uploadToStorage(
new File("/path/to/your/file.png"));
// response.getAppUrl() returns "tb://398eijf83i"
String tbUrl = response.getAppUrl();
import testingbotclient
tb = testingbotclient.TestingBotClient(
'API_KEY',
'API_SECRET')
# Upload a local file
response = tb.storage.upload_local_file('/path/to/your/file.png')
# response['app_url'] returns "tb://398eijf83i"
tb_url = response['app_url']
const TestingBot = require('testingbot-api');
const api = new TestingBot({
api_key: 'API_KEY',
api_secret: 'API_SECRET'
});
// Upload a local file
api.uploadFile('/path/to/your/file.png', function(error, response) {
// response.app_url returns "tb://398eijf83i"
console.log(response.app_url);
});
require 'testingbot'
api = TestingBot::Api.new(
'API_KEY',
'API_SECRET')
# Upload a local file
response = api.upload_local_file('/path/to/your/file.png')
# response['app_url'] returns "tb://398eijf83i"
tb_url = response['app_url']
$api = new TestingBot\TestingBotAPI(
'API_KEY',
'API_SECRET');
// Upload a local file
$response = $api->uploadLocalFileToStorage('/path/to/your/file.png');
// $response['app_url'] returns "tb://398eijf83i"
$tbUrl = $response['app_url'];
Step 2: Use the tb:// URL in your test
Once you have the tb:// URL, use it as the upload value in your tb:options capabilities.
TestingBot will fetch the file from internal storage and place it at the uploadFilepath location on the test VM.
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class StorageUploadTest {
public static void main(String[] args) throws Exception {
ChromeOptions options = new ChromeOptions();
options.setPlatformName("WIN11");
options.setBrowserVersion("latest");
Map<String, Object> tbOptions = new HashMap<>();
tbOptions.put("key", "API_KEY");
tbOptions.put("secret", "API_SECRET");
tbOptions.put("name", "Test File Upload via Storage");
// Use the tb:// URL from Step 1
tbOptions.put("upload", "tb://398eijf83i");
tbOptions.put("uploadFilepath", "C:\\test\\file.png");
options.setCapability("tb:options", tbOptions);
RemoteWebDriver driver = new RemoteWebDriver(
new URL("https://hub.testingbot.com/wd/hub"), options);
driver.get("http://the-internet.herokuapp.com/upload");
driver.findElement(By.id("file-upload")).sendKeys("C:\\test\\file.png");
driver.findElement(By.id("file-submit")).click();
driver.quit();
}
}
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
options = Options()
options.set_capability('platformName', 'WIN11')
options.set_capability('browserVersion', 'latest')
options.set_capability('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload via Storage',
# Use the tb:// URL from Step 1
'upload': 'tb://398eijf83i',
'uploadFilepath': 'C:\\test\\file.png'
})
driver = webdriver.Remote(
command_executor='https://hub.testingbot.com/wd/hub',
options=options)
driver.get('http://the-internet.herokuapp.com/upload')
driver.find_element(By.ID, 'file-upload').send_keys('C:\\test\\file.png')
driver.find_element(By.ID, 'file-submit').click()
driver.quit()
const { Builder, By } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
async function runTest() {
const options = new chrome.Options();
options.set('platformName', 'WIN11');
options.set('browserVersion', 'latest');
options.set('tb:options', {
'key': 'API_KEY',
'secret': 'API_SECRET',
'name': 'Test File Upload via Storage',
// Use the tb:// URL from Step 1
'upload': 'tb://398eijf83i',
'uploadFilepath': 'C:\\test\\file.png'
});
const driver = await new Builder()
.usingServer('https://hub.testingbot.com/wd/hub')
.setChromeOptions(options)
.build();
try {
await driver.get('http://the-internet.herokuapp.com/upload');
await driver.findElement(By.id('file-upload')).sendKeys('C:\\test\\file.png');
await driver.findElement(By.id('file-submit')).click();
} finally {
await driver.quit();
}
}
runTest();
require 'rubygems'
require 'test/unit'
require 'selenium-webdriver'
class UploadTest < Test::Unit::TestCase
def setup
options = Selenium::WebDriver::Chrome::Options.new
options.add_option('platformName', 'WIN11')
options.add_option('browserVersion', 'latest')
options.add_option('tb:options', {
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload via Storage',
# Use the tb:// URL from Step 1
'upload' => 'tb://398eijf83i',
'uploadFilepath' => 'C:\\test\\file.png'
})
@driver = Selenium::WebDriver.for(
:remote,
url: 'https://hub.testingbot.com/wd/hub',
options: options
)
end
def test_upload
@driver.navigate.to 'http://the-internet.herokuapp.com/upload'
element = @driver.find_element(:id, 'file-upload')
element.send_keys 'C:\\test\\file.png'
@driver.find_element(:id, 'file-submit').click
assert_equal 'file.png', @driver.find_element(:id, 'uploaded-files').text
end
def teardown
@driver.quit
end
end
<?php
require_once('vendor/autoload.php');
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Chrome\ChromeOptions;
$options = new ChromeOptions();
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability('platformName', 'WIN11');
$capabilities->setCapability('browserVersion', 'latest');
// Use the tb:// URL from Step 1
$capabilities->setCapability('tb:options', [
'key' => 'API_KEY',
'secret' => 'API_SECRET',
'name' => 'Test File Upload via Storage',
'upload' => 'tb://398eijf83i',
'uploadFilepath' => 'C:\\test\\file.png'
]);
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create(
'https://hub.testingbot.com/wd/hub',
$capabilities
);
$driver->get('http://the-internet.herokuapp.com/upload');
$driver->findElement(WebDriverBy::id('file-upload'))->sendKeys('C:\\test\\file.png');
$driver->findElement(WebDriverBy::id('file-submit'))->click();
$driver->quit();
?>
using System;
using System.Collections.Generic;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
namespace SeleniumTest
{
class Program
{
static void Main(string[] args)
{
ChromeOptions options = new ChromeOptions();
options.PlatformName = "WIN11";
options.BrowserVersion = "latest";
var tbOptions = new Dictionary<string, object>
{
["key"] = "API_KEY",
["secret"] = "API_SECRET",
["name"] = "Test File Upload via Storage",
// Use the tb:// URL from Step 1
["upload"] = "tb://398eijf83i",
["uploadFilepath"] = "C:\\test\\file.png"
};
options.AddAdditionalOption("tb:options", tbOptions);
IWebDriver driver = new RemoteWebDriver(
new Uri("https://hub.testingbot.com/wd/hub"), options);
driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/upload");
driver.FindElement(By.Id("file-upload")).SendKeys("C:\\test\\file.png");
driver.FindElement(By.Id("file-submit")).Click();
driver.Quit();
}
}
}
// wdio.conf.js
exports.config = {
user: 'API_KEY',
key: 'API_SECRET',
capabilities: [{
browserName: 'chrome',
browserVersion: 'latest',
platformName: 'WIN11',
'tb:options': {
name: 'Test File Upload via Storage',
// Use the tb:// URL from Step 1
upload: 'tb://398eijf83i',
uploadFilepath: 'C:\\test\\file.png'
}
}],
hostname: 'hub.testingbot.com',
port: 443,
path: '/wd/hub',
protocol: 'https'
};
Tips & Best Practices
Always set LocalFileDetector for remote uploads
When running tests on a remote grid, sendKeys on a file input will look for the file on the remote machine by default.
Setting LocalFileDetector tells Selenium to transfer the file from your local machine first.
Forgetting to set LocalFileDetector is the most common cause of file upload failures on remote grids. The error message may not clearly indicate this — you'll typically see a "file not found" error on the remote machine.
Handle hidden file inputs
Many modern web applications hide the native <input type="file"> element and use a styled button or drag-and-drop zone instead.
You can still use sendKeys on hidden file inputs — Selenium allows interaction with hidden file input elements.
// The file input may be hidden, but sendKeys still works
WebElement fileInput = driver.findElement(By.cssSelector("input[type='file']"));
fileInput.sendKeys("/path/to/file.png");
Wait for the upload to complete
After triggering a file upload, always wait for a confirmation element or success message before proceeding. File transfers take time, especially for larger files.
// Wait for a success indicator after uploading
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("uploaded-files")));
Use absolute file paths
Always use absolute file paths (e.g. /Users/test/logo.png or C:\test\logo.png) instead of relative paths. Relative paths may resolve differently on the remote machine and your local machine.
Upload multiple files
If the file input accepts multiple files (has the multiple attribute), you can upload several files by sending all paths separated by a newline character (\n).
# Upload multiple files at once
file_input = driver.find_element(By.CSS_SELECTOR, 'input[type="file"]')
file_input.send_keys('/Users/test/file1.png\n/Users/test/file2.png')
Frequently Asked Questions
Why does my file upload fail with "file not found" on the remote machine?
This happens when LocalFileDetector is not set. Without it, Selenium sends the local file path as a string to the remote machine, which then tries to find the file at that path locally. Set LocalFileDetector on the driver (Java, Node.js, C#) or the element (PHP) before calling sendKeys. Python and Ruby remote drivers use LocalFileDetector by default.
Can I upload a file to a drag-and-drop upload zone?
Drag-and-drop upload zones typically have a hidden <input type="file"> element underneath. Locate this hidden input using By.cssSelector("input[type='file']") and call sendKeys on it directly. This bypasses the drag-and-drop interface and triggers the same upload behavior.
What is the difference between uploading before and during a test?
Uploading before the test (via the upload capability) downloads the file from a URL and places it on the VM before your test starts. This is useful for test fixtures and large files. Uploading during the test (via LocalFileDetector) transfers a file from your local machine through the WebDriver protocol as part of the test flow. Use this when you need to test the actual upload UI interaction.
How do I upload files with WebdriverIO on TestingBot?
WebdriverIO provides a built-in browser.uploadFile() command that transfers a local file to the remote machine and returns the remote path. Use this path with setValue() on the file input element. Alternatively, use the upload and uploadFilepath capabilities in your wdio.conf.js to pre-load files before the test. See the WebdriverIO section for full examples.