---
title: Upgrading to Selenium 4.
description: How to run Automated WebDriver tests with the new Selenium 4 W3C protocol.
  Learn about what's new with Selenium 4.
source_url:
  html: https://testingbot.com/support/web-automate/selenium/selenium4
  md: https://testingbot.com/support/web-automate/selenium/selenium4/index.md
---
# Selenium 4

This page will help you in upgrading your existing Selenium WebDriver tests to use Selenium 4, with the [WebDriver W3C Protocol](https://www.w3.org/TR/webdriver1/).

TestingBot has full support for this new Selenium version. Below is more information on how to make this transition.

## Updating your tests

With the new W3C protocol, some restrictions apply to using capabilities with your test. Before the W3C protocol (the JSONWP protocol), you could simply add [all our custom TestingBot capabilities](https://testingbot.com/support/web-automate/selenium/test-options) to your test in the `desiredCapabilities`.

With the new W3C protocol, only some W3C WebDriver capabilities are allowed inside the `capabilities`:

- `browserName`
- `browserVersion`
- `platformName`
- `acceptInsecureCerts`
- `pageLoadStrategy`
- `proxy`
- `timeouts`
- `unhandledPromptBehavior`

All other capabilities are treated as [Extension capabilities](https://www.w3.org/TR/webdriver1/#dfn-extension-capability) and should be namespaced (include a vendor prefix).   
 For [TestingBot specific capabilities](https://testingbot.com/support/web-automate/selenium/test-options), you'll need to wrap all capabilities inside a `tb:options` namespace.

Please see the table below on how the capabilities need to be changed:

| Capability JSONWP | Capability W3C |
| --- | --- |
| `browserName` | `browserName` |
| `version` | `browserVersion` |
| `platform` | `platformName` |
| `selenium-version`, `chromedriverVersion`,  
`extra`, `name` [and more](https://testingbot.com/support/web-automate/selenium/test-options) | 

    "tb:options" : {
      "selenium-version": '...',
      "chromedriverVersion": '...',
      "extra": '...',
      "name": '...'
    }

 |

## Example
[Legacy](https://testingbot.com#)[Selenium 4](https://testingbot.com#)
### Ruby

    #!/usr/bin/env ruby
    require 'rubygems'
    require 'selenium-webdriver'
    
    caps = {
      :browserName => "chrome",
      :version => "latest-1",
      :platform => "WIN10",
      :screenrecorder => true,
      :build => "testbuild",
      :name => "testname"
    }
    
    client = Selenium::WebDriver::Remote::Http::Default.new
    client.timeout = 480
    
    driver = Selenium::WebDriver.for(
      :remote,
      :url => "https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub",
      :http_client => client,
      :desired_capabilities => caps)
    driver.navigate.to "https://www.google.com"
    element = driver.find_element(:name, 'q')
    element.send_keys "TestingBot"
    element.submit
    puts driver.title
    driver.quit

#### PHP

    <?php
    require_once('vendor/autoload.php');
    use Facebook\WebDriver\Remote\RemoteWebDriver;
    use Facebook\WebDriver\WebDriverBy;
      
      $capabilities = array(
        'browserName' => "chrome",
        'version' => "latest-1",
        'platform' => "WIN10",
        'screenrecorder' => true,
        'build' => "testbuild",
        'name' => "testname"
      );
      $web_driver = RemoteWebDriver::create(
        "https://api_key:api_secret@hub.testingbot.com/wd/hub",
        $capabilities, 240000
      );
      $web_driver->get("https://google.com");
    
      $element = $web_driver->findElement(WebDriverBy::name("q"));
      if($element) {
          $element->sendKeys("TestingBot");
          $element->submit();
      }
      print $web_driver->getTitle();
      $web_driver->quit();
    ?>

#### Java

    import org.openqa.selenium.By;
    import org.openqa.selenium.Platform;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.remote.DesiredCapabilities;
    import org.openqa.selenium.remote.RemoteWebDriver;
    
    import java.net.URL;
    
    public class JavaSample {
    
      public static final String KEY = "KEY";
      public static final String SECRET = "SECRET";
      public static final String HUB_URL = "https://" + KEY + ":" + SECRET + "@hub.testingbot.com/wd/hub";
    
      public static void main(String[] args) throws Exception {
    
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("browserName", "chrome");
        caps.setCapability("version", "latest-1");
        caps.setCapability("platform", "WIN10");
        caps.setCapability("screenrecorder", true);
        caps.setCapability("build", "testbuild");
        caps.setCapability("name", "testname");
    
        WebDriver driver = new RemoteWebDriver(new URL(HUB_URL), caps);
        driver.get("https://www.google.com/ncr");
        WebElement element = driver.findElement(By.name("q"));
    
        element.sendKeys("TestingBot");
        element.submit();
    
        System.out.println(driver.getTitle());
        driver.quit();
    
      }
    }

#### Python

    import unittest
    import sys
    
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
    from testingbotclient import TestingBotClient
    
    class TestTestingBotClient(unittest.TestCase):
    
        def setUp(self):
          desired_cap = {
            'browserName': 'chrome',
            'version': 'latest-1',
            'platform': 'WIN10',
            'screenrecorder': True,
            'build': 'testbuild',
            'name' : 'testname',
          }
    
          self.driver = webdriver.Remote(
            command_executor='https://key:secret@hub.testingbot.com/wd/hub',
            desired_capabilities=desired_cap)
    
        def test_google_example(self):
          self.driver.get("https://www.google.com")
          if not "Google" in self.driver.title:
              raise Exception("Unable to load google page!")
          elem = self.driver.find_element(By.NAME, "q")
          elem.send_keys("TestingBot")
          elem.submit()
    
        def tearDown(self):
          passed = sys.exc_info() == (None, None, None)
          session_id = self.driver.session_id
          try:
              tb_client = TestingBotClient('key', 'secret')
              tb_client.tests.update_test(session_id, name=self._testMethodName, passed=passed)
          finally:
              self.driver.quit()
    
    if __name__ == ' __main__':
        unittest.main()

#### JavaScript

    var wd = require('wd'),
    testingbotKey = "api_key",
    testingbotSecret = "api_secret"
     
    desiredCaps = {
      'browserName': 'chrome',
      'version': 'latest-1',
      'platform': 'WIN10',
      'screenrecorder': true,
      'build': 'testbuild',
      'name' : 'testname',
    }
    
    driver = wd.remote("https://" + testingbotKey + ":" + testingbotSecret + "@" + "hub.testingbot.com/wd/hub")
    driver.init(desiredCaps, function() {
      driver.get('https://www.google.com', function() {
        driver.title(function(err, title) {
            console.log(title)
            driver.quit()
        })
      })
    })

#### Ruby

    #!/usr/bin/env ruby
    require 'rubygems'
    require 'selenium-webdriver'
    
    options = Selenium::WebDriver::Chrome::Options.new
    options.browser_version = 'latest-1'
    options.platform_name = 'WIN10'
    options.add_option('tb:options', {
      'screenrecorder' => true,
      'build' => 'testbuild',
      'name' => 'testname'
    })
    
    client = Selenium::WebDriver::Remote::Http::Default.new
    client.timeout = 480
    
    driver = Selenium::WebDriver.for(
      :remote,
      url: "https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub",
      http_client: client,
      options: options)
    driver.navigate.to "https://www.google.com"
    element = driver.find_element(:name, 'q')
    element.send_keys "TestingBot"
    element.submit
    puts driver.title
    driver.quit

#### PHP

    <?php
    require_once('vendor/autoload.php');
    use Facebook\WebDriver\Remote\RemoteWebDriver;
    use Facebook\WebDriver\WebDriverBy;
      
      $capabilities = array(
        'browserName' => "chrome",
        'browserVersion' => "latest-1",
        'platformName' => "WIN10",
        'tb:options' => array(
          'screenrecorder' => true,
          'build' => "testbuild",
          'name' => "testname"
        )
      );
      $web_driver = RemoteWebDriver::create(
        "https://api_key:api_secret@hub.testingbot.com/wd/hub",
        $capabilities, 240000
      );
      $web_driver->get("https://google.com");
    
      $element = $web_driver->findElement(WebDriverBy::name("q"));
      if($element) {
          $element->sendKeys("TestingBot");
          $element->submit();
      }
      print $web_driver->getTitle();
      $web_driver->quit();
    ?>

#### 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.RemoteWebDriver;
    
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    
    public class JavaSample {
    
      public static final String KEY = "KEY";
      public static final String SECRET = "SECRET";
      public static final String HUB_URL = "https://" + KEY + ":" + SECRET + "@hub.testingbot.com/wd/hub";
    
      public static void main(String[] args) throws Exception {
        Map<String, Object> tbOptions = new HashMap<>();
        tbOptions.put("screenrecorder", true);
        tbOptions.put("build", "testbuild");
        tbOptions.put("name", "testname");
    
        ChromeOptions options = new ChromeOptions();
        options.setBrowserVersion("latest-1");
        options.setPlatformName("WIN10");
        options.setCapability("tb:options", tbOptions);
    
        WebDriver driver = new RemoteWebDriver(new URL(HUB_URL), options);
        driver.get("https://www.google.com/ncr");
        WebElement element = driver.findElement(By.name("q"));
    
        element.sendKeys("TestingBot");
        element.submit();
    
        System.out.println(driver.getTitle());
        driver.quit();
    
      }
    }

#### Python

    import unittest
    import sys
    
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options
    from testingbotclient import TestingBotClient
    
    class TestTestingBotClient(unittest.TestCase):
    
        def setUp(self):
          options = Options()
          options.browser_version = 'latest-1'
          options.platform_name = 'WIN10'
          options.set_capability('tb:options', {
            'screenrecorder': True,
            'build': 'testbuild',
            'name': 'testname'
          })
    
          self.driver = webdriver.Remote(
            command_executor='https://key:secret@hub.testingbot.com/wd/hub',
            options=options)
    
        def test_google_example(self):
          self.driver.get("https://www.google.com")
          if not "Google" in self.driver.title:
              raise Exception("Unable to load google page!")
          elem = self.driver.find_element(By.NAME, "q")
          elem.send_keys("TestingBot")
          elem.submit()
    
        def tearDown(self):
          passed = sys.exc_info() == (None, None, None)
          session_id = self.driver.session_id
          try:
              tb_client = TestingBotClient('key', 'secret')
              tb_client.tests.update_test(session_id, name=self._testMethodName, passed=passed)
          finally:
              self.driver.quit()
    
    if __name__ == ' __main__':
        unittest.main()

#### JavaScript

    const { Builder, By } = require('selenium-webdriver');
    
    const testingbotKey = "api_key";
    const testingbotSecret = "api_secret";
    
    const capabilities = {
      'browserName': 'chrome',
      'browserVersion': 'latest-1',
      'platformName': 'WIN10',
      'tb:options': {
        'screenrecorder': true,
        'build': 'testbuild',
        'name': 'testname'
      }
    };
    
    (async function example() {
      const driver = await new Builder()
        .usingServer(`https://${testingbotKey}:${testingbotSecret}@hub.testingbot.com/wd/hub`)
        .withCapabilities(capabilities)
        .build();
    
      try {
        await driver.get('https://www.google.com');
        console.log(await driver.getTitle());
      } finally {
        await driver.quit();
      }
    })();

## New Selenium 4 Features

Below are some of the new features introduced by Selenium 4 which you can use in your TestingBot tests.

### New Window

Create or switch to a new (blank) tab or window.

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

    @driver.switch_to.new_window(:window)
    @driver.manage.window.position = Selenium::WebDriver::Point.new(200, 400)
    
    @driver.switch_to.new_window(:tab)

    driver.switchTo().newWindow(WindowType.WINDOW);
    driver.manage().window().setPosition(new Point(200, 400));
    
    driver.switchTo().newWindow(WindowType.TAB);

    driver.switch_to.new_window('window')
    driver.set_window_position(200, 400)
    
    driver.switch_to.new_window('tab')

    Driver.SwitchTo().NewWindow(WindowType.Window);
    Driver.Manage().Window.Position = new Point(200, 400);
    
    Driver.SwitchTo().NewWindow(WindowType.Tab);

### Print Page

Print the current page as a `PDF`, available in Chrome, Edge and Firefox.   
 You can pass additional options to customize the `PDF`, such as `page size`, `range`, `margins`, `background` and shrink to fit.

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

    driver.navigate.to url_for("https://testingbot.com")
    path = "PrintPage.pdf"
    driver.save_print_page path

    Path printPage = Paths.get(directory + "PrintPage.pdf");
    Pdf print = driver.print(new PrintOptions());
    
    Files.write(printPage, OutputType.BYTES.convertFromBase64Png(print.getContent()));

    from base64 import b64decode
    
    driver.get("https://testingbot.com")
    
    pdf = b64decode(driver.print_page())
    
    with open("print_page.pdf", 'wb') as f:
        f.write(pdf)

    Driver.Navigate().GoToUrl("https://testingbot.com");
    var parentFullName = Directory.GetParent(Environment.CurrentDirectory)?.Parent?.Parent?.FullName;
    
    ((ISupportsPrint) Driver).Print(new PrintOptions()).SaveAsFile(parentFullName + "/PrintPage.pdf");

For Chrome and Edge, you need to use `headless` mode to print pages.

### Relative Locators

With Relative Locators, you can identify DOM elements in relationship to other DOM elements.   
 You can use a more natural syntax such as:

- above
- below
- left of
- right of
- near

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

    @driver.get("https://testingbot.com")
    element = @driver.find_element(relative: {tag_name: 'div', left: {id: 'outer-orbit'}, below: {id: 'circle-orbit-container'}})

    driver.get("https://testingbot.com");
    WebElement element = driver.findElement(with(By.tagName("div"))
            .toLeftOf(By.id("outer-orbit"))
            .below(By.id("circle-orbit-container")));

    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.relative_locator import locate_with
    
    driver.get('https://testingbot.com')
    anchor = driver.find_element(By.ID, "outer-orbit")
    below_of = driver.find_element(By.ID, "circle-orbit-container")
    element = driver.find_element(locate_with(By.TAG_NAME, "div")
                                  .to_left_of(anchor)
                                  .below(below_of))

    Driver.Navigate().GoToUrl("https://testingbot.com");
    IWebElement element = Driver.FindElement(RelativeBy.WithLocator(By.TagName("div"))
        .LeftOf(By.Id("outer-orbit"))
        .Below(By.Id("circle-orbit-container")));

### Retrieve Timeouts

Selenium 4 now allows you to query the driver for the current timeout values.   
 Please see the examples below on how to do this, for example with implicit waits, pageload timeouts or script timeout.

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

    @driver.manage.timeouts.implicit_wait = 1
    p @driver.manage.timeouts.implicit_wait
    p @driver.manage.timeouts.page_load
    p @driver.manage.timeouts.script

    WebDriver.Timeouts timeouts = driver.manage().timeouts();
    
    timeouts.pageLoadTimeout(Duration.ofSeconds(10));
    timeouts.implicitlyWait(Duration.ofMillis(11));
    timeouts.scriptTimeout(Duration.ofSeconds(12));
    timeouts.getPageLoadTimeout();
    Assertions.assertEquals(Duration.ofSeconds(10), timeouts.getPageLoadTimeout());
    Assertions.assertEquals(Duration.ofMillis(11), timeouts.getImplicitWaitTimeout());
    Assertions.assertEquals(Duration.ofSeconds(12), timeouts.getScriptTimeout());

    from selenium.webdriver.common.timeouts import Timeouts
    
    timeouts = Timeouts()
    timeouts.implicit_wait = 1
    driver.timeouts = timeouts
    
    assert driver.timeouts.implicit_wait == 1
    assert driver.timeouts.page_load == 20
    assert driver.timeouts.script == 20

    Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0.4);
    Assert.AreEqual(TimeSpan.FromSeconds(0.4), Driver.Manage().Timeouts().ImplicitWait);
    Assert.AreEqual(TimeSpan.FromSeconds(4), Driver.Manage().Timeouts().PageLoad);
    Assert.AreEqual(TimeSpan.FromSeconds(40), Driver.Manage().Timeouts().AsynchronousJavaScript);

### Full Page Screenshots
Firefox Only

Firefox implemented a feature to take screenshots of the entire page, not just the viewport, which is the default setting for other browsers or for Selenium 3.

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

    @driver.get('https://testingbot.com')
    @driver.save_screenshot('FullScreenshotOnFirefox.png', full_page: true)

    WebDriver augmentedDriver = new Augmenter().augment(driver);
    File file = ((HasFullPageScreenshot) augmentedDriver).getFullPageScreenshotAs(OutputType.FILE);
    
    Path fullPageScreenshot = Paths.get(directory + "FullScreenshotOnFirefox.png");
    Files.move(file.toPath(), fullPageScreenshot);

    driver.get('https://testingbot.com')
    driver.get_full_page_screenshot_as_file('FullScreenshotOnFirefox.png')

    IHasCommandExecutor hasCommandExecutor = Driver as IHasCommandExecutor;
    var addFullPageScreenshotCommandInfo = new HttpCommandInfo(HttpCommandInfo.GetCommand,
        "/session/{sessionId}/moz/screenshot/full");
    hasCommandExecutor.CommandExecutor.TryAddCommand("fullPageScreenshot", addFullPageScreenshotCommandInfo);
    
    SessionId sessionId = ((RemoteWebDriver)Driver).SessionId;
    var fullPageScreenshotCommand = new Command(sessionId, "fullPageScreenshot", null);
    
    Driver.Navigate().GoToUrl("https://testingbot.com");
    var screenshotResponse = hasCommandExecutor.CommandExecutor.Execute(fullPageScreenshotCommand);
    string base64 = screenshotResponse.Value.ToString();
    Screenshot image = new Screenshot(base64);
    
    var parentFullName = Directory.GetParent(Environment.CurrentDirectory)?.Parent?.Parent?.FullName;
    image.SaveAsFile(parentFullName + "/FullScreenshotOnFirefox.png", ScreenshotImageFormat.Png);

### Network Conditions
Chrome and Edge only

Selenium 4 offers the possibility to modify network conditions for your tests, such as:

- Setting latency 
- Modifying upstream/downstream throughput 
- Going into offline mode 

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

    @driver.network_conditions = {offline: true, latency: 1, throughput: 10000}

    WebDriver augmentedDriver = new Augmenter().augment(driver);
    ChromiumNetworkConditions networkConditions = new ChromiumNetworkConditions();
    networkConditions.setOffline(true);
    ((HasNetworkConditions) augmentedDriver).setNetworkConditions(networkConditions);
    
    try {
        driver.get("https://testingbot.com");
    } catch (WebDriverException ex) {
        ((HasNetworkConditions) augmentedDriver).setNetworkConditions(new ChromiumNetworkConditions());
    }

    driver.set_network_conditions(
        offline=True,
        latency=1,
        throughput=10000
    )

    INetworkConditions networkConditions = new NetworkConditions()
    {
        IsOffline = true,
        Latency = TimeSpan.FromMilliseconds(1),
        DownloadThroughput = 10000,
        UploadThroughput = 10000
    };
    ((IChromiumNetworkConditions)driver).NetworkConditions = networkConditions;

TestingBot has been offering [throttling, mocking and intercepting](https://testingbot.com/support/web-automate/selenium/network) network requests before Selenium 4.

### Change Preferences During Session
Firefox only

Before Selenium 4, it was only possible to set preferences (through capabilities) at the start of a test.   
 Firefox now provides the option to change preferences during a session. You can do this by toggling the context between `chrome` and `content`.

Make sure to switch back to the correct context, or some commands might fail.

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

    @driver.get("https://www.google.com")
    
    language = @driver.find_element(id: "gws-output-pages-elements-homepage_additional_languages__als")
    p language
    @driver.context = 'chrome'
    @driver.execute_script("Services.prefs.setStringPref('intl.accept_languages', 'es-ES')")
    @driver.context = 'content'
    @driver.navigate.refresh
    language = @driver.find_element(id: "gws-output-pages-elements-homepage_additional_languages__als")
    p language
    @driver.quit

    driver.get("https://www.google.com");
    
    String lang1 = driver.findElement(By.id("gws-output-pages-elements-homepage_additional_languages__als")).getText();
    Assertions.assertTrue(lang1.contains("offered in"));
    
    WebDriver augmentedDriver = new Augmenter().augment(driver);
    ((HasContext) augmentedDriver).setContext(FirefoxCommandContext.CHROME);
    
    ((JavascriptExecutor) driver).executeScript("Services.prefs.setStringPref('intl.accept_languages', 'es-ES')");
    
    ((HasContext) augmentedDriver).setContext(FirefoxCommandContext.CONTENT);
    driver.navigate().refresh();
    
    String lang2 = driver.findElement(By.id("gws-output-pages-elements-homepage_additional_languages__als")).getText();
    Assertions.assertTrue(lang2.contains("Ofrecido por"));

 This feature is not yet supported in the Python bindings. 

    IHasCommandExecutor hasCommandExecutor = Driver as IHasCommandExecutor;
    var getContextCommandInfo = new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/context");
    var setContextCommandInfo = new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/context");
    hasCommandExecutor.CommandExecutor.TryAddCommand("getContext", getContextCommandInfo);
    hasCommandExecutor.CommandExecutor.TryAddCommand("setContext", setContextCommandInfo);
    
    SessionId sessionId = ((RemoteWebDriver)Driver).SessionId;
    
    Driver.Navigate().GoToUrl("https://www.google.com");
    var element = Driver.FindElement(By.CssSelector("#gws-output-pages-elements-homepage_additional_languages__als"));
    
    Assert.IsTrue(element.Text.Contains("offered in"));
    
    try
    {
        Driver.ExecuteJavaScript("Services.prefs.setStringPref('intl.accept_languages', 'es-ES')");
        Assert.Fail("Can not change Service prefs in content context, so previous method should fail");
    }
    catch (WebDriverException)
    {
        // This is expected
    }
    
    var getContextCommand = new Command(sessionId, "getContext", null);
    Response response = hasCommandExecutor.CommandExecutor.Execute(getContextCommand);
    
    Assert.AreEqual("content", response.Value);
    
    Dictionary<String, Object> payload = new Dictionary<string, object>();
    payload.Add("context", "chrome");
    var setContextCommand = new Command(sessionId, "setContext", payload);
    hasCommandExecutor.CommandExecutor.Execute(setContextCommand);
    
    response = hasCommandExecutor.CommandExecutor.Execute(getContextCommand);
    Assert.AreEqual("chrome", response.Value);
    
    Driver.ExecuteJavaScript("Services.prefs.setStringPref('intl.accept_languages', 'es-ES')");
    
    try
    {
        Driver.Navigate().Refresh();
        Assert.Fail("Can not navigate in chrome context, so previous method should fail");
    }
    catch (WebDriverException)
    {
        // This is expected
    }
    
    payload.Remove("context");
    payload.Add("context", "content");
    setContextCommand = new Command(sessionId, "setContext", payload);
    hasCommandExecutor.CommandExecutor.Execute(setContextCommand);
    
    Driver.Navigate().Refresh();
    element = Driver.FindElement(By.CssSelector("#gws-output-pages-elements-homepage_additional_languages__als"));
    Assert.IsTrue(element.Text.Contains("Ofrecido por"));

### BiDirectional Functionality

The [WebDriver BiDirectional Protocol](https://testingbot.com/support/web-automate/selenium/selenium-bidi) is a new protocol enhancement that should improve the speed and reliability of Webdriver tests. Its mechanism is similar to CDP (Chrome DevTools Protocol), but is implemented as a vendor agnostic protocol.

TestingBot supports [WebDriver BiDi](https://testingbot.com/support/web-automate/selenium/selenium-bidi). Please see the [BiDi documentation examples](https://testingbot.com/support/web-automate/selenium/selenium-bidi) to get started.

## Upgrading dependencies

Please see the examples below on how to upgrade to Selenium 4.

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

    gem 'selenium-webdriver', '~> 4.36'

    <dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.36.0</version>
        </dependency>
    </dependencies>

You can run `npm install selenium-webdriver` to install the latest version, or create/modify your `package.json` file:

    {
      "name": "selenium-tests",
      "version": "1.0.0",
      "dependencies": {
        "selenium-webdriver": "^4.36.0"
      }
    }

    pip install 'selenium>=4.36'

    PM> Install-Package Selenium.WebDriver -Version 4.36.0

## Deprecation Messages and Errors

Below is a list of potential errors or deprecation messages you might see when upgrading to Selenium 4.

### AddAdditionalOption

The `AddAdditionalCapability` capability is deprecated in Selenium 4, it has been replaced by `AddAdditionalOption`.

[Legacy](https://testingbot.com#)[Selenium 4](https://testingbot.com#)

    var browserOptions = new ChromeOptions();
    browserOptions.PlatformName = "Windows 10";
    browserOptions.BrowserVersion = "latest";
    var tbOptions = new Dictionary<string, object>();
    browserOptions.AddAdditionalCapability("tb:options", tbOptions, true);

    var browserOptions = new ChromeOptions();
    browserOptions.PlatformName = "Windows 10";
    browserOptions.BrowserVersion = "latest";
    var tbOptions = new Dictionary<string, object>();
    browserOptions.AddAdditionalOption("tb:options", tbOptions);

### Timeout Parameters

The parameters passed to `Timeout` functions have switched from long (`TimeUnit`) to duration (`Duration`).

[Legacy](https://testingbot.com#)[Selenium 4](https://testingbot.com#)

    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    driver.manage().timeouts().setScriptTimeout(4, TimeUnit.MINUTES);
    driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);

    driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
    driver.manage().timeouts().scriptTimeout(Duration.ofMinutes(4));
    driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(5));

### Wait Parameters

`WebDriverWait`, `withTimeout` and `pollingEvery` now expect `Duration` instead of a long time.

[Legacy](https://testingbot.com#)[Selenium 4](https://testingbot.com#)

    new WebDriverWait(driver, 3)
    .until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));
    
    Wait wait = new FluentWait(driver)
      .withTimeout(30, TimeUnit.SECONDS)
      .pollingEvery(5, TimeUnit.SECONDS)
      .ignoring(NoSuchElementException.class);

    new WebDriverWait(driver, Duration.ofSeconds(3))
    .until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));
    
    Wait wait = new FluentWait(driver)
      .withTimeout(Duration.ofSeconds(30))
      .pollingEvery(Duration.ofSeconds(5))
      .ignoring(NoSuchElementException.class);

### Merging Capabilities

Before Selenium 4 you could merge capabilities, mutating the calling object.   
 Since Selenium 4 this is deprecated, you now need to manually assign the result of the merge operation.

[Legacy](https://testingbot.com#)[Selenium 4](https://testingbot.com#)

    MutableCapabilities capabilities = new MutableCapabilities();
    capabilities.setCapability("platformVersion", "Windows 10");
    FirefoxOptions options = new FirefoxOptions();
    options.addArguments("--headless");
    options.merge(capabilities);

    MutableCapabilities capabilities = new MutableCapabilities();
    capabilities.setCapability("platformVersion", "Windows 10");
    FirefoxOptions options = new FirefoxOptions();
    options.addArguments("--headless");
    options = options.merge(capabilities);

### Looking for more help?

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

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