---
title: Testing upload with Selenium WebDriver and TestingBot
description: How to test file uploads with Selenium WebDriver and WebdriverIO on TestingBot.
  Code examples in Java, Python, Ruby, C#, PHP and NodeJS for uploading files to remote
  browsers using LocalFileDetector and TestingBot Storage.
source_url:
  html: https://testingbot.com/support/web-automate/selenium/uploads
  md: https://testingbot.com/support/web-automate/selenium/uploads.md
---

# 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](https://testingbot.com#before), pre-load a file onto the test VM via a URL
- [Upload a file during your test](https://testingbot.com#during), send a local file to the remote VM with `LocalFileDetector`
- [Upload with WebdriverIO](https://testingbot.com#webdriverio), file upload examples for the WebdriverIO framework
- [TestingBot Storage](https://testingbot.com#storage), use TestingBot's internal storage for faster file transfers
- [Tips & Best Practices](https://testingbot.com#tips), common pitfalls and recommendations

## Which method should I use?

- **Use [Upload Before Test](https://testingbot.com#before)** 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](https://testingbot.com#during)** 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](https://testingbot.com#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](https://testingbot.com#storage).
- **`uploadFilepath`:** The absolute file path on the test VM where TestingBot should save the downloaded file (e.g. `C:\test\logo.png` on Windows).

[Java](https://testingbot.com#) [Python](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Ruby](https://testingbot.com#) [PHP](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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 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("https://hub.testingbot.com/wd/hub"), 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();
  }
}
```

```python
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()
```

```javascript
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();
```

```ruby
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
<?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();
?>
```

```csharp
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.

[Java](https://testingbot.com#) [Python](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Ruby](https://testingbot.com#) [PHP](https://testingbot.com#) [C#](https://testingbot.com#)

```java
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 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("https://hub.testingbot.com/wd/hub"), 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();
  }
}
```

```python
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()
```

```javascript
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();
```

```ruby
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
<?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
);
// Must set LocalFileDetector on the driver before uploading
$driver->setFileDetector(new LocalFileDetector());

$driver->get('http://the-internet.herokuapp.com/upload');
$file_input = $driver->findElement(WebDriverBy::id('file-upload'));
$file_input->sendKeys('/Users/test/logo.png');

$driver->findElement(WebDriverBy::id('file-submit'))->click();
$driver->quit();
?>
```

```csharp
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.

```javascript
// 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:

```javascript
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.

```javascript
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](https://testingbot.com/support/api/storage#upload), 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](https://testingbot.com/support/api/storage#upload). The API returns a `tb://` URL that you'll use in your test capabilities.

#### Upload with curl

```bash
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:

```json
{"app_url": "tb://398eijf83i"}
```

You can also upload a file from a remote URL instead of a local file:

```bash
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
[Java](https://testingbot.com#) [Python](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Ruby](https://testingbot.com#) [PHP](https://testingbot.com#)

```java
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();
```

```python
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']
```

```javascript
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);
});
```

```ruby
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']
```

```php
$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.

[Java](https://testingbot.com#) [Python](https://testingbot.com#) [NodeJS](https://testingbot.com#) [Ruby](https://testingbot.com#) [PHP](https://testingbot.com#) [C#](https://testingbot.com#) [WebdriverIO](https://testingbot.com#)

```java
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();
  }
}
```

```python
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()
```

```javascript
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();
```

```ruby
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
<?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();
?>
```

```csharp
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();
        }
    }
}
```

```javascript
// 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.

```java
// 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.

```java
// 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`).

```python
# 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 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](https://testingbot.com#webdriverio) for full examples.

### Looking for more help?

Have questions or need more information? Reach out via email or Slack.

[Email us](https://testingbot.com/contact/new) [Join our Slack](https://join.slack.com/t/testingb0t/shared_invite/zt-3bcw9xch-jk19~6XPs_xBrsAgAedkCw)
