Selenium is working with browser vendors to create the
WebDriver BiDirectional Protocol
as a means to provide a stable, cross-browser API that uses the bidirectional
functionality useful for both browser automation generally and testing specifically.
Before now, users seeking this functionality have had to rely on CDP (Chrome DevTools Protocol)
with all of its frustrations and limitations.
The traditional WebDriver model of strict request/response commands will be supplemented
with the ability to stream events from the user agent to the controlling software via WebSockets,
better matching the evented nature of the browser DOM.
As it is not a good idea to tie your tests to a specific version of any browser, the
Selenium project recommends using WebDriver BiDi wherever possible.
While the specification is in works, the browser vendors are parallely implementing
the WebDriver BiDirectional Protocol.
Refer web-platform-tests dashboard
to see how far along the browser vendors are.
Selenium is trying to keep up with the browser vendors and has started implementing W3C BiDi APIs.
The goal is to ensure APIs are W3C compliant and uniform among the different language bindings.
However, until the specification and corresponding Selenium implementation is complete there are many useful things that
CDP offers. Selenium offers some useful helper classes that use CDP.
1 - BiDirectional API (CDP implementation)
The following list of APIs will be growing as the Selenium
project works through supporting real world use cases. If there
is additional functionality you’d like to see, please raise a
feature request.
Register Basic Auth
Some applications make use of browser authentication to secure pages.
With Selenium, you can automate the input of basic auth credentials whenever they arise.
require'selenium-webdriver'driver=Selenium::WebDriver.for:chromebegindriver.devtools.newdriver.register(username:'username',password:'password')driver.get'<your site url>'ensuredriver.quitend
Listen to the console.log events and register callbacks to process the event.
ChromeDriverdriver=newChromeDriver();DevToolsdevTools=driver.getDevTools();devTools.createSession();devTools.send(Log.enable());devTools.addListener(Log.entryAdded(),logEntry->{System.out.println("log: "+logEntry.getText());System.out.println("level: "+logEntry.getLevel());});driver.get("http://the-internet.herokuapp.com/broken_images");// Check the terminal output for the browser console messages.
driver.quit();
importtriofromseleniumimportwebdriverfromselenium.webdriver.common.logimportLogasyncdefprintConsoleLogs():chrome_options=webdriver.ChromeOptions()driver=webdriver.Chrome()driver.get("http://www.google.com")asyncwithdriver.bidi_connection()assession:log=Log(driver,session)fromselenium.webdriver.common.bidi.consoleimportConsoleasyncwithlog.add_listener(Console.ALL)asmessages:driver.execute_script("console.log('I love cheese')")print(messages["message"])driver.quit()trio.run(printConsoleLogs)
funkotlinConsoleLogExample(){valdriver=ChromeDriver()valdevTools=driver.devToolsdevTools.createSession()vallogConsole={c:ConsoleEvent->print("Console log message is: "+c.messages)}devTools.domains.events().addConsoleListener(logConsole)driver.get("https://www.google.com")valexecutor=driverasJavascriptExecutorexecutor.executeScript("console.log('Hello World')")valinput=driver.findElement(By.name("q"))input.sendKeys("Selenium 4")input.sendKeys(Keys.RETURN)driver.quit()}
Listen to JS Exceptions
Listen to the JS Exceptions
and register callbacks to process the exception details.
importorg.openqa.selenium.*;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.devtools.DevTools;publicvoidjsExceptionsExample(){ChromeDriverdriver=newChromeDriver();DevToolsdevTools=driver.getDevTools();devTools.createSession();List<JavascriptException>jsExceptionsList=newArrayList<>();Consumer<JavascriptException>addEntry=jsExceptionsList::add;devTools.getDomains().events().addJavascriptExceptionListener(addEntry);driver.get("<your site url>");WebElementlink2click=driver.findElement(By.linkText("<your link text>"));((JavascriptExecutor)driver).executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);",link2click,"onclick","throw new Error('Hello, world!')");link2click.click();for(JavascriptExceptionjsException:jsExceptionsList){System.out.println("JS exception message: "+jsException.getMessage());System.out.println("JS exception system information: "+jsException.getSystemInformation());jsException.printStackTrace();}}
asyncdefcatchJSException():chrome_options=webdriver.ChromeOptions()driver=webdriver.Chrome()asyncwithdriver.bidi_connection()assession:driver.get("<your site url>")log=Log(driver,session)asyncwithlog.add_js_error_listener()asmessages:# Operation on the website that throws an JS errorprint(messages)driver.quit()
List<string>exceptionMessages=newList<string>();usingIJavaScriptEnginemonitor=newJavaScriptEngine(driver);monitor.JavaScriptExceptionThrown+=(sender,e)=>{exceptionMessages.Add(e.Message);};awaitmonitor.StartEventMonitoring();driver.Navigate.GoToUrl("<your site url>");IWebElementlink2click=driver.FindElement(By.LinkText("<your link text>"));((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].setAttribute(arguments[1], arguments[2]);",link2click,"onclick","throw new Error('Hello, world!')");link2click.Click();foreach(stringmessageinexceptionMessages){Console.WriteLine("JS exception message: {0}",message);}
const{Builder,By}=require('selenium-webdriver');(async()=>{try{letdriver=newBuilder().forBrowser('chrome').build();constcdpConnection=awaitdriver.createCDPConnection('page')awaitdriver.onLogException(cdpConnection,function(event){console.log(event['exceptionDetails']);})awaitdriver.get('https://the-internet.herokuapp.com');constlink=awaitdriver.findElement(By.linkText('Checkboxes'));awaitdriver.executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);",link,"onclick","throw new Error('Hello, world!')");awaitlink.click();awaitdriver.quit();}catch(e){console.log(e);}})()
funkotlinJsErrorListener(){valdriver=ChromeDriver()valdevTools=driver.devToolsdevTools.createSession()vallogJsError={j:JavascriptException->print("Javascript error: '"+j.localizedMessage+"'.")}devTools.domains.events().addJavascriptExceptionListener(logJsError)driver.get("https://www.google.com")vallink2click=driver.findElement(By.name("q"))(driverasJavascriptExecutor).executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);",link2click,"onclick","throw new Error('Hello, world!')")link2click.click()driver.quit()}
Network Interception
If you want to capture network events coming into the browser and you want manipulate them you are able to do
it with the following examples.
While Selenium 4 provides direct access to the Chrome DevTools Protocol (CDP), it is
highly encouraged that you use the WebDriver Bidi APIs instead.
Many browsers provide “DevTools” – a set of tools that are integrated with the browser that
developers can use to debug web apps and explore the performance of their pages. Google Chrome’s
DevTools make use of a protocol called the Chrome DevTools Protocol (or “CDP” for short).
As the name suggests, this is not designed for testing, nor to have a stable API, so functionality
is highly dependent on the version of the browser.
WebDriver Bidi is the next generation of the W3C WebDriver protocol and aims to provide a stable API
implemented by all browsers, but it’s not yet complete. Until it is, Selenium provides access to
the CDP for those browsers that implement it (such as Google Chrome, or Microsoft Edge, and
Firefox), allowing you to enhance your tests in interesting ways. Some examples of what you can
do with it are given below.
Emulate Geo Location
Some applications have different features and functionalities across different
locations. Automating such applications is difficult because it is hard to emulate
the geo-locations in the browser using Selenium. But with the help of Devtools,
we can easily emulate them. Below code snippet demonstrates that.
fromseleniumimportwebdriverfromselenium.webdriver.chrome.serviceimportServicedefgeoLocationTest():driver=webdriver.Chrome()Map_coordinates=dict({"latitude":41.8781,"longitude":-87.6298,"accuracy":100})driver.execute_cdp_cmd("Emulation.setGeolocationOverride",Map_coordinates)driver.get("<your site url>")
usingSystem.Threading.Tasks;usingOpenQA.Selenium.Chrome;usingOpenQA.Selenium.DevTools;// Replace the version to match the Chrome versionusingOpenQA.Selenium.DevTools.V87.Emulation;namespacedotnet_test{classProgram{publicstaticvoidMain(string[]args){GeoLocation().GetAwaiter().GetResult();}publicstaticasyncTaskGeoLocation(){ChromeDriverdriver=newChromeDriver();DevToolsSessiondevToolsSession=driver.CreateDevToolsSession();vargeoLocationOverrideCommandSettings=newSetGeolocationOverrideCommandSettings();geoLocationOverrideCommandSettings.Latitude=51.507351;geoLocationOverrideCommandSettings.Longitude=-0.127758;geoLocationOverrideCommandSettings.Accuracy=1;awaitdevToolsSession.GetVersionSpecificDomains<OpenQA.Selenium.DevTools.V87.DevToolsSessionDomains>().Emulation.SetGeolocationOverride(geoLocationOverrideCommandSettings);driver.Url="<your site url>";}}}
require'selenium-webdriver'driver=Selenium::WebDriver.for:chromebegin# Latitude and longitude of Tokyo, Japancoordinates={latitude:35.689487,longitude:139.691706,accuracy:100}driver.execute_cdp('Emulation.setGeolocationOverride',coordinates)driver.get'https://www.google.com/search?q=selenium'ensuredriver.quitend
const{By,Key,Browser}=require('selenium-webdriver');const{suite}=require('selenium-webdriver/testing');constassert=require("assert");suite(function(env){describe('Emulate geolocation',function(){letdriver;before(asyncfunction(){driver=awaitenv.builder().build();});after(()=>driver.quit());it('Emulate coordinates of Tokyo',asyncfunction(){constcdpConnection=awaitdriver.createCDPConnection('page');// Latitude and longitude of Tokyo, Japan
constcoordinates={latitude:35.689487,longitude:139.691706,accuracy:100,};awaitcdpConnection.execute("Emulation.setGeolocationOverride",coordinates);awaitdriver.get("https://kawasaki-india.com/dealer-locator/");});});},{browsers:[Browser.CHROME,Browser.FIREFOX]});
fromseleniumimportwebdriver#Replace the version to match the Chrome versionimportselenium.webdriver.common.devtools.v93asdevtoolsasyncdefgeoLocationTest():chrome_options=webdriver.ChromeOptions()driver=webdriver.Remote(command_executor='<grid-url>',options=chrome_options)asyncwithdriver.bidi_connection()assession:cdpSession=session.sessionawaitcdpSession.execute(devtools.emulation.set_geolocation_override(latitude=41.8781,longitude=-87.6298,accuracy=100))driver.get("https://my-location.org/")driver.quit()
usingSystem.Threading.Tasks;usingOpenQA.Selenium.Chrome;usingOpenQA.Selenium.DevTools;// Replace the version to match the Chrome versionusingOpenQA.Selenium.DevTools.V87.Emulation;namespacedotnet_test{classProgram{publicstaticvoidMain(string[]args){GeoLocation().GetAwaiter().GetResult();}publicstaticasyncTaskGeoLocation(){ChromeOptionschromeOptions=newChromeOptions();RemoteWebDriverdriver=newRemoteWebDriver(newUri("<grid-url>"),chromeOptions);DevToolsSessiondevToolsSession=driver.CreateDevToolsSession();vargeoLocationOverrideCommandSettings=newSetGeolocationOverrideCommandSettings();geoLocationOverrideCommandSettings.Latitude=51.507351;geoLocationOverrideCommandSettings.Longitude=-0.127758;geoLocationOverrideCommandSettings.Accuracy=1;awaitdevToolsSession.GetVersionSpecificDomains<OpenQA.Selenium.DevTools.V87.DevToolsSessionDomains>().Emulation.SetGeolocationOverride(geoLocationOverrideCommandSettings);driver.Url="https://my-location.org/";}}}
driver=Selenium::WebDriver.for(:remote,:url=>"<grid-url>",:capabilities=>:chrome)begin# Latitude and longitude of Tokyo, Japancoordinates={latitude:35.689487,longitude:139.691706,accuracy:100}devToolsSession=driver.devtoolsdevToolsSession.send_cmd('Emulation.setGeolocationOverride',coordinates)driver.get'https://my-location.org/'putsresensuredriver.quitend
constwebdriver=require('selenium-webdriver');constBROWSER_NAME=webdriver.Browser.CHROME;asyncfunctiongetDriver(){returnnewwebdriver.Builder().usingServer('<grid-url>').forBrowser(BROWSER_NAME).build();}asyncfunctionexecuteCDPCommands(){letdriver=awaitgetDriver();awaitdriver.get("<your site url>");constcdpConnection=awaitdriver.createCDPConnection('page');//Latitude and longitude of Tokyo, Japan
constcoordinates={latitude:35.689487,longitude:139.691706,accuracy:100,};awaitcdpConnection.execute("Emulation.setGeolocationOverride",coordinates);awaitdriver.quit();}executeCDPCommands();
importorg.openqa.selenium.WebDriverimportorg.openqa.selenium.chrome.ChromeOptionsimportorg.openqa.selenium.devtools.HasDevTools// Replace the version to match the Chrome version
importorg.openqa.selenium.devtools.v91.emulation.Emulationimportorg.openqa.selenium.remote.Augmenterimportorg.openqa.selenium.remote.RemoteWebDriverimportjava.net.URLimportjava.util.Optionalfunmain(){valchromeOptions=ChromeOptions()vardriver:WebDriver=RemoteWebDriver(URL("<grid-url>"),chromeOptions)driver=Augmenter().augment(driver)valdevTools=(driverasHasDevTools).devToolsdevTools.createSession()devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),Optional.of(13.4501),Optional.of(1)))driver["https://my-location.org/"]driver.quit()}
Override Device Mode
Using Selenium’s integration with CDP, one can override the current device
mode and simulate a new mode. Width, height, mobile, and deviceScaleFactor
are required parameters. Optional parameters include scale, screenWidth,
screenHeight, positionX, positionY, dontSetVisible, screenOrientation, viewport, and displayFeature.
ChromeDriverdriver=newChromeDriver();DevToolsdevTools=driver.getDevTools();devTools.createSession();// iPhone 11 Pro dimensions
devTools.send(Emulation.setDeviceMetricsOverride(375,812,50,true,Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty(),Optional.empty()));driver.get("https://selenium.dev/");driver.quit();
fromseleniumimportwebdriverdriver=webdriver.Chrome()//iPhone11Prodimensionsset_device_metrics_override=dict({"width":375,"height":812,"deviceScaleFactor":50,"mobile":True})driver.execute_cdp_cmd('Emulation.setDeviceMetricsOverride',set_device_metrics_override)driver.get("<your site url>")
usingOpenQA.Selenium;usingOpenQA.Selenium.Chrome;usingOpenQA.Selenium.DevTools;usingSystem.Threading.Tasks;usingOpenQA.Selenium.DevTools.V91.Emulation;usingDevToolsSessionDomains=OpenQA.Selenium.DevTools.V91.DevToolsSessionDomains;namespaceSelenium4Sample{publicclassExampleDevice{protectedIDevToolsSessionsession;protectedIWebDriverdriver;protectedDevToolsSessionDomainsdevToolsSession;publicasyncTaskDeviceModeTest(){ChromeOptionschromeOptions=newChromeOptions();//Set ChromeDriverdriver=newChromeDriver();//Get DevToolsIDevToolsdevTools=driverasIDevTools;//DevTools Sessionsession=devTools.GetDevToolsSession();vardeviceModeSetting=newSetDeviceMetricsOverrideCommandSettings();deviceModeSetting.Width=600;deviceModeSetting.Height=1000;deviceModeSetting.Mobile=true;deviceModeSetting.DeviceScaleFactor=50;awaitsession.GetVersionSpecificDomains<OpenQA.Selenium.DevTools.V91.DevToolsSessionDomains>().Emulation.SetDeviceMetricsOverride(deviceModeSetting);driver.Url="<your site url>";}}}
// File must contain the following using statementsusingOpenQA.Selenium;usingOpenQA.Selenium.Chrome;usingOpenQA.Selenium.DevTools;// We must use a version-specific set of domainsusingOpenQA.Selenium.DevTools.V94.Performance;publicasyncTaskPerformanceMetricsExample(){IWebDriverdriver=newChromeDriver();IDevToolsdevTools=driverasIDevTools;DevToolsSessionsession=devTools.GetDevToolsSession();awaitsession.SendCommand<EnableCommandSettings>(newEnableCommandSettings());varmetricsResponse=awaitsession.SendCommand<GetMetricsCommandSettings,GetMetricsCommandResponse>(newGetMetricsCommandSettings());driver.Navigate().GoToUrl("http://www.google.com");driver.Quit();varmetrics=metricsResponse.Metrics;foreach(Metricmetricinmetrics){Console.WriteLine("{0} = {1}",metric.Name,metric.Value);}}
The following list of APIs will be growing as the WebDriver BiDirectional Protocol grows
and browser vendors implement the same.
Additionally, Selenium will try to support real-world use cases that internally use a combination of W3C BiDi protocol APIs.
If there is additional functionality you’d like to see, please raise a
feature request.
3.1 - Browsing Context
This section contains the APIs related to browsing context commands.
A reference browsing context is a top-level browsing context.
The API allows to pass the reference browsing context, which is used to create a new window. The implementation is operating system specific.
A reference browsing context is a top-level browsing context.
The API allows to pass the reference browsing context, which is used to create a new tab. The implementation is operating system specific.
Provides a tree of all browsing contexts descending from the parent browsing context, including the parent browsing context upto the depth value passed.
constinspector=awaitLogInspector(driver)awaitinspector.onJavascriptException(function(log){logEntry=log})awaitdriver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')awaitdriver.findElement({id:'jsException'}).click()assert.equal(logEntry.text,'Error: Not working')assert.equal(logEntry.type,'javascript')assert.equal(logEntry.level,'error')