Features

Selenium 4

This page will help you in upgrading your existing Selenium WebDriver tests to use Selenium 4, with the WebDriver W3C Protocol.

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 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 and should be namespaced (include a vendor prefix).
For TestingBot specific capabilities, 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
Copy code
"tb:options" : {
  "selenium-version": '...',
  "chromedriverVersion": '...',
  "extra": '...',
  "name": '...'
}

Example

Before

Copy code
#!/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
        
Copy code
<?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();
?>
Copy code
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 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(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();

  }
}
Copy code
import unittest
import sys

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
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='http://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):
      self.driver.quit()
      status = sys.exc_info() == (None, None, None)
      tb_client = TestingBotClient('key', 'secret')
      tb_client.tests.update_test(self.driver.session_id, self._testMethodName, status)

if __name__ == '__main__':
    unittest.main()
Copy code
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()
    })
  })
})

After

Copy code
#!/usr/bin/env ruby
require 'rubygems'
require 'selenium-webdriver'

caps = {
  :browserName => "chrome",
  :browserVersion => "latest-1",
  :platformName => "WIN10",
  "tb:options" => {
    :screenrecorder => true,
    :build => "testbuild",
    :name => "testname",
    "selenium-version" => "3.11.0"
  }
}

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
        
Copy code
<?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' => {
      'screenrecorder' => true,
      'build' => "testbuild",
      'name' => "testname",
      'selenium-version' => "3.11.0"
    }
  );
  $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();
?>
Copy code
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 URL = "https://" + KEY + ":" + SECRET + "@hub.testingbot.com/wd/hub";

  public static void main(String[] args) throws Exception {
    MutableCapabilities tbOpts = new MutableCapabilities();
    tbOpts.setCapability("screenrecorder", true);
    tbOpts.setCapability("build", "testbuild");
    tbOpts.setCapability("name", "testname");
    tbOpts.setCapability("selenium-version", "3.11.0");

    DesiredCapabilities caps = new DesiredCapabilities();
    caps.setCapability("browserName", "chrome");
    caps.setCapability("browserVersion", "latest-1");
    caps.setCapability("platformName", "WIN10");
    caps.setCapability("tb:options", tbOpts);

    WebDriver driver = new RemoteWebDriver(new URL(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();

  }
}
Copy code
import unittest
import sys

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from testingbotclient import TestingBotClient

class TestTestingBotClient(unittest.TestCase):

    def setUp(self):
      desired_cap = {
        'browserName': 'chrome',
        'browserVersion': 'latest-1',
        'platformName': 'WIN10',
        'tb:options' : {
          'screenrecorder': true,
          'build': 'testbuild',
          'name' : 'testname',
          'selenium-version' : '3.11.0'
        }
      }

      self.driver = webdriver.Remote(
        command_executor='http://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):
      self.driver.quit()
      status = sys.exc_info() == (None, None, None)
      tb_client = TestingBotClient('key', 'secret')
      tb_client.tests.update_test(self.driver.session_id, self._testMethodName, status)

if __name__ == '__main__':
    unittest.main()
Copy code
var wd = require('wd'),
testingbotKey = "api_key",
testingbotSecret = "api_secret"
 
desiredCaps = {
  'browserName': 'chrome',
  'browserVersion': 'latest-1',
  'platformName': 'WIN10',
  'tb:options' : {
    'screenrecorder': true,
    'build': 'testbuild',
    'name' : 'testname',
    'selenium-version' : '3.11.0'
  }
}

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()
    })
  })
})

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.

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

@driver.switch_to.new_window(:tab)
Copy code
driver.switchTo().newWindow(WindowType.WINDOW);
driver.manage().window().setPosition(new Point(200, 400));

driver.switchTo().newWindow(WindowType.TAB);
Copy code
driver.switch_to.new_window('window')
driver.set_window_position(200, 400)

driver.switch_to.new_window('tab')
Copy code
Driver.SwitchTo().NewWindow(WindowType.Window);
Driver.Manage().Window.Position = new Point(200, 400);

Driver.SwitchTo().NewWindow(WindowType.Tab);

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.

Copy code
driver.navigate.to url_for("https://testingbot.com")
path = "PrintPage.pdf"
driver.save_print_page path
Copy code
Path printPage = Paths.get(directory + "PrintPage.pdf");
Pdf print = driver.print(new PrintOptions());

Files.write(printPage, OutputType.BYTES.convertFromBase64Png(print.getContent()));
Copy code
headless_driver.get("https://testingbot.com")

pdf = b64decode(headless_driver.print_page())

with open("print_page.pdf", 'wb') as f:
    f.write(pdf)
Copy code
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
Copy code
@driver.get("https://testingbot.com")
element = @driver.find_element(relative: {tag_name: 'div', left: {id: 'outer-orbit'}, below: {id: 'circle-orbit-container'}})
Copy code
driver.get("https://testingbot.com");
WebElement element = driver.findElement(with(By.tagName("div"))
        .toLeftOf(By.id("outer-orbit"))
        .below(By.id("circle-orbit-container")));
Copy code
driver.get('https://testingbot.com')
element = driver.find_elements(locate_with(By.TAG_NAME, "div")
                               .to_left_of({By.ID: "outer-orbit"})
                               .below({By.ID: "circle-orbit-container"}))[0]
Copy code
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.

Copy code
@driver.manage.timeouts.implicit_wait = 1
p @driver.manage.timeouts.implicit_wait
p @driver.manage.timeouts.page_load
p @driver.manage.timeouts.script
Copy code
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());
Copy code
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
Copy code
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.

Copy code
@driver.get('https://testingbot.com')
@driver.save_screenshot('FullScreenshotOnFirefox.png', full_page: true)
Copy code
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);
Copy code
Copy code
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
Copy code
@driver.network_conditions = {offline: true, latency: 1, throughput: 10000}
Copy code
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());
}
Copy code
Copy code

TestingBot has been offering throtteling, mocking and intercepting 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.

Copy code
@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
Copy code
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.
Copy code
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 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. Please see the BiDi documentation examples to get started.

TestingBot

Upgrading dependencies

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

Copy code
gem 'selenium-webdriver', '~> 4.0.0'
Copy code
<dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.0.0</version>
    </dependency>
</dependencies>

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

Copy code
{
  "name": "selenium-tests",
  "version": "1.0.0",
  "dependencies": {
    "selenium-webdriver": "^4.0.0"
  }
}
Copy code
pip install selenium==4.0.0
Copy code
PM> Install-Package Selenium.WebDriver -Version 4.0.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.

Copy code
var browserOptions = new ChromeOptions();
browserOptions.PlatformName = "Windows 10";
browserOptions.BrowserVersion = "latest";
var tbOptions = new Dictionary<string, object>();
browserOptions.AddAdditionalCapability("tb:options", tbOptions, true);
Copy code
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).

Copy code
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.manage().timeouts().setScriptTimeout(4, TimeUnit.MINUTES);
driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
Copy code
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.

Copy code
new WebDriverWait(driver, 3)
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));

Wait<webdriver> wait = new FluentWait<webdriver>(driver)
  .withTimeout(30, TimeUnit.SECONDS)
  .pollingEvery(5, TimeUnit.SECONDS)
  .ignoring(NoSuchElementException.class);</webdriver></webdriver>
Copy code
new WebDriverWait(driver, Duration.ofSeconds(3))
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#id")));

Wait<webdriver> wait = new FluentWait<webdriver>(driver)
  .withTimeout(Duration.ofSeconds(30))
  .pollingEvery(Duration.ofSeconds(5))
  .ignoring(NoSuchElementException.class);</webdriver></webdriver>

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.

Copy code
MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("platformVersion", "Windows 10");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
options.merge(capabilities);
Copy code
MutableCapabilities capabilities = new MutableCapabilities();
capabilities.setCapability("platformVersion", "Windows 10");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
options = options.merge(capabilities);