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
|
Example
Before
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
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.
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.
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
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.
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());
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 onlyFirefox 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.
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 onlySelenium 4 offers the possibility to modify network conditions for your tests, such as:
- Setting latency
- Modifying upstream/downstream throughput
- Going into offline mode
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());
}
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.
@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"));
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.
You can run npm install selenium-webdriver
to install the latest version, or create/modify your package.json
file:
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
.
Timeout Parameters
The parameters passed to Timeout
functions have switched from long (TimeUnit
) to duration (Duration
).
Wait Parameters
WebDriverWait
, withTimeout
and pollingEvery
now expect Duration
instead of a long time.
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.