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
"tb:options" : {
"selenium-version" : '...' ,
"chromedriverVersion" : '...' ,
"extra" : '...' ,
"name" : '...'
}
Example
Before
#!/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 "http://www.google.com"
element = driver . find_element ( :name , 'q' )
element . send_keys "TestingBot"
element . submit
puts driver . title
driver . quit
<?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 ( "http://google.com" );
$element = $web_driver -> findElement ( WebDriverBy :: name ( "q" ));
if ( $element ) {
$element -> sendKeys ( "TestingBot" );
$element -> submit ();
}
print $web_driver -> getTitle ();
$web_driver -> quit ();
?>
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 ( "http://www.google.com/ncr" );
WebElement element = driver . findElement ( By . name ( "q" ));
element . sendKeys ( "TestingBot" );
element . submit ();
System . out . println ( driver . getTitle ());
driver . quit ();
}
}
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 ( "http://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 ()
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
#!/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 "http://www.google.com"
element = driver . find_element ( :name , 'q' )
element . send_keys "TestingBot"
element . submit
puts driver . title
driver . quit
<?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 ( "http://google.com" );
$element = $web_driver -> findElement ( WebDriverBy :: name ( "q" ));
if ( $element ) {
$element -> sendKeys ( "TestingBot" );
$element -> submit ();
}
print $web_driver -> getTitle ();
$web_driver -> quit ();
?>
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 ( "http://www.google.com/ncr" );
WebElement element = driver . findElement ( By . name ( "q" ));
element . sendKeys ( "TestingBot" );
element . submit ();
System . out . println ( driver . getTitle ());
driver . quit ();
}
}
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 ( "http://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 ()
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.
Ruby
Python
Java
C#
@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
Python
Java
C#
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 ()));
headless_driver . get ( "https://testingbot.com" )
pdf = b64decode ( headless_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
Python
Java
C#
@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" )));
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 ]
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
Python
Java
C#
@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 ());
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
Python
Java
C#
@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 );
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
Python
Java
C#
@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 ());
}
TestingBot has been offering throtteling, mocking and intercepting network requests before Selenium 4.
Upgrading dependencies
Please see the examples below on how to upgrade to Selenium 4.
Ruby
Python
Java
C#
gem 'selenium-webdriver' , '~> 4.0.0'
< dependencies >
< dependency >
< groupId > org . seleniumhq . selenium </ groupId >
< artifactId > selenium - java </ artifactId >
< version > 4.0 . 0 </ version >
</ dependency >
</ dependencies >
pip install selenium == 4.0 . 0
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
.
Legacy
Selenium 4
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
Selenium 4
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
Selenium 4
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 >
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.
Legacy
Selenium 4
MutableCapabilities capabilities = new MutableCapabilities ();
capabilities . setCapability ( "platformVersion" , "Windows 10" );
FirefoxOptions options = new FirefoxOptions ();
options . setHeadless ( true );
options . merge ( capabilities );
MutableCapabilities capabilities = new MutableCapabilities ();
capabilities . setCapability ( "platformVersion" , "Windows 10" );
FirefoxOptions options = new FirefoxOptions ();
options . setHeadless ( true );
options = options . merge ( capabilities );