这是本节的多页打印视图。
点击此处打印.
返回本页常规视图.
双向协议
Selenium正在与浏览器供应商合作创建
WebDriver双向协议 ,
作为一种提供稳定的跨浏览器API的方法,
该API使用双向协议处理
各种浏览器的通用自动化以及特定测试的需求.
在此之前, 寻求此功能的用户
必须忍受当前实现的全部问题和局限.
严格限制请求响应命令的传统WebDriver模型,
将从user agent转变为基于WebSockets的软件控制,
通过这样完善流事件的能力,
以便更好地匹配浏览器DOM的事件性质.
因为将测试受限于特定浏览器的特定版本是个坏主意,
Selenium项目建议尽可能使用WebDriver BiDi.
然而, 在规范完成之前, CDP提供了许多有用的东西.
为了帮助保持测试的独立性和可移植性,
Selenium提供了一些有用的辅助类.
目前, 这些应用程序使用CDP,
但我们将尽快提供WebDriver Bidi的实现.
1 - RemoteWebDriver BiDirectional API (CDP implementation)
Page being translated from
English to Chinese. Do you speak Chinese? Help us to translate
it by sending us pull requests!
The following examples demonstrate how to leverage BiDi APIs with Remote WebDriver.
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.
AtomicReference<DevTools> devToolsAtomicReference = new AtomicReference<>();
driver = new Augmenter()
.addDriverAugmentation("chrome",
HasAuthentication.class,
(caps, exec) -> (whenThisMatches, useTheseCredentials) -> {
devToolsAtomicReference.get()
.createSessionIfThereIsNotOne();
devToolsAtomicReference.get().getDomains()
.network()
.addAuthHandler(whenThisMatches,
useTheseCredentials);
}).augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devToolsAtomicReference.set(devTools);
((HasAuthentication) driver).
register(UsernameAndPassword.of("admin", "admin"));
Mutation Observation
Mutation Observation is the ability to capture events via
WebDriver BiDi when there are DOM mutations on a specific
element in the DOM.
AtomicReference<DomMutationEvent> seen = new AtomicReference<>();
AtomicReference<WebDriver> augmentedDriver = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
driver = new Augmenter()
.addDriverAugmentation("chrome",
HasLogEvents.class,
(caps, exec) -> new HasLogEvents() {
@Override
public <X> void onLogEvent(EventType<X> kind) {
kind.initializeListener(augmentedDriver.get());
}
}).augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
augmentedDriver.set(driver);
((HasLogEvents) driver).onLogEvent(domMutation(mutation -> {
if ("cheese".equals(mutation.getAttributeName())) {
seen.set(mutation);
latch.countDown();
}
}));
Listen to console.log
events
Listen to the console.log
events and register callbacks to process the event.
CountDownLatch latch = new CountDownLatch(4);
driver = new Augmenter().augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(org.openqa.selenium.devtools.v85.runtime.Runtime.enable());
devTools.send(Log.enable());
devTools.addListener(Log.entryAdded(),
logEntry -> {
System.out.println("log: " + logEntry.getText());
System.out.println("level: " + logEntry.getLevel());
latch.countDown();
});
devTools.addListener(org.openqa.selenium.devtools.v85.runtime.Runtime.consoleAPICalled(),
consoleLog -> System.out.println("Type: " + consoleLog.getType()));
Actions causing JS exceptions
driver = new Augmenter().augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
List<JavascriptException> jsExceptionsList = new ArrayList<>();
devTools.getDomains().events().addJavascriptExceptionListener(jsExceptionsList::add);
CompletableFuture<JavascriptException> futureJsExc = new CompletableFuture<>();
devTools.getDomains().events().addJavascriptExceptionListener(futureJsExc::complete);
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.
driver = new Augmenter().augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
try (NetworkInterceptor interceptor = new NetworkInterceptor(
driver,
Route.matching(req -> req.getUri().contains("google"))
.to(() -> req -> new HttpResponse()
.setStatus(200)
.addHeader("Content-Type", MediaType.HTML_UTF_8.toString())
.setContent(utf8String("Creamy, delicious cheese!"))))) {
2 - Chrome开发工具协议
虽然Selenium 4提供了对Chrome DevTools Protocol (CDP) 的直接访问,
但是仍非常鼓励您使用
WebDriver Bidi APIs
代替.
许多浏览器都提供"开发工具" – 一组与浏览器集成的工具,
开发人员可以用其调试web应用程序并探索其页面的性能.
谷歌浏览器开发工具
使用一种称为Chrome DevTools Protocol (简称"CDP") 的协议.
顾名思义, 这不是为测试而设计的,
而并没有一个稳定的API,
所以它的功能高度依赖于浏览器的版本.
WebDriver Bidi是W3C WebDriver的下一代协议,
旨在提供由所有浏览器实现稳定的API, 但尚未完成.
在此之前, Selenium提供了通过CDP实现的方式
(诸如Google Chrome或Microsoft Edge, 以及Firefox),
允许您以有趣的方式增强测试.
下面给出了实际使用的例子.
模拟地理位置
一些应用程序在不同的位置具有不同的特性和功能.
自动化此类应用程序很难,
因为很难使用Selenium在浏览器中模拟地理位置.
但是在Devtools的帮助下,
我们可以轻易模拟他们.
下面的代码片段演示了这一点.
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),
Optional.of(13.4501),
Optional.of(1)));
driver.get("https://my-location.org/");
driver.quit();
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
def geoLocationTest():
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>")
using System.Threading.Tasks;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
// Replace the version to match the Chrome version
using OpenQA.Selenium.DevTools.V87.Emulation;
namespace dotnet_test {
class Program {
public static void Main(string[] args) {
GeoLocation().GetAwaiter().GetResult();
}
public static async Task GeoLocation() {
ChromeDriver driver = new ChromeDriver();
DevToolsSession devToolsSession = driver.CreateDevToolsSession();
var geoLocationOverrideCommandSettings = new SetGeolocationOverrideCommandSettings();
geoLocationOverrideCommandSettings.Latitude = 51.507351;
geoLocationOverrideCommandSettings.Longitude = -0.127758;
geoLocationOverrideCommandSettings.Accuracy = 1;
await devToolsSession
.GetVersionSpecificDomains<OpenQA.Selenium.DevTools.V87.DevToolsSessionDomains>()
.Emulation
.SetGeolocationOverride(geoLocationOverrideCommandSettings);
driver.Url = "<your site url>";
}
}
}
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
# Latitude and longitude of Tokyo, Japan
coordinates = { latitude: 35.689487,
longitude: 139.691706,
accuracy: 100 }
driver.execute_cdp('Emulation.setGeolocationOverride', coordinates)
driver.get 'https://www.google.com/search?q=selenium'
ensure
driver.quit
end
const { By, Key, Browser} = require('selenium-webdriver');
const { suite } = require('selenium-webdriver/testing');
const assert = require("assert");
suite(function(env) {
describe('Emulate geolocation', function() {
let driver;
before(async function() {
driver = await env.builder().build();
});
after(() => driver.quit());
it('Emulate coordinates of Tokyo', async function() {
const cdpConnection = await driver.createCDPConnection('page');
// Latitude and longitude of Tokyo, Japan
const coordinates = {
latitude: 35.689487,
longitude: 139.691706,
accuracy: 100,
};
await cdpConnection.execute(
"Emulation.setGeolocationOverride",
coordinates
);
await driver.get("https://kawasaki-india.com/dealer-locator/");
});
});
},{ browsers: [Browser.CHROME, Browser.FIREFOX]});
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.devtools.DevTools
fun main() {
val driver = ChromeDriver()
val coordinates : HashMap<String, Any> = HashMap<String, Any> ()
coordinates.put("latitude", 50.2334)
coordinates.put("longitude", 0.2334)
coordinates.put("accuracy", 1)
driver.executeCdpCommand("Emulation.setGeolocationOverride", coordinates)
driver.get("https://www.google.com")
}
通过远程WebDriver模拟地理位置
ChromeOptions chromeOptions = new ChromeOptions();
WebDriver driver = new RemoteWebDriver(new URL("<grid-url>"), chromeOptions);
driver = new Augmenter().augment(driver);
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(Emulation.setGeolocationOverride(Optional.of(52.5043),
Optional.of(13.4501),
Optional.of(1)));
driver.get("https://my-location.org/");
driver.quit();
from selenium import webdriver
#Replace the version to match the Chrome version
import selenium.webdriver.common.devtools.v93 as devtools
async def geoLocationTest():
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Remote(
command_executor='<grid-url>',
options=chrome_options
)
async with driver.bidi_connection() as session:
cdpSession = session.session
await cdpSession.execute(devtools.emulation.set_geolocation_override(latitude=41.8781,longitude=-87.6298,accuracy=100))
driver.get("https://my-location.org/")
driver.quit()
using System.Threading.Tasks;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
// Replace the version to match the Chrome version
using OpenQA.Selenium.DevTools.V87.Emulation;
namespace dotnet_test {
class Program {
public static void Main(string[] args) {
GeoLocation().GetAwaiter().GetResult();
}
public static async Task GeoLocation() {
ChromeOptions chromeOptions = new ChromeOptions();
RemoteWebDriver driver = new RemoteWebDriver(new Uri("<grid-url>"), chromeOptions);
DevToolsSession devToolsSession = driver.CreateDevToolsSession();
var geoLocationOverrideCommandSettings = new SetGeolocationOverrideCommandSettings();
geoLocationOverrideCommandSettings.Latitude = 51.507351;
geoLocationOverrideCommandSettings.Longitude = -0.127758;
geoLocationOverrideCommandSettings.Accuracy = 1;
await devToolsSession
.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, Japan
coordinates = { latitude: 35.689487,
longitude: 139.691706,
accuracy: 100 }
devToolsSession = driver.devtools
devToolsSession.send_cmd('Emulation.setGeolocationOverride', coordinates)
driver.get 'https://my-location.org/'
puts res
ensure
driver.quit
end
const webdriver = require('selenium-webdriver');
const BROWSER_NAME = webdriver.Browser.CHROME;
async function getDriver() {
return new webdriver.Builder()
.usingServer('<grid-url>')
.forBrowser(BROWSER_NAME)
.build();
}
async function executeCDPCommands () {
let driver = await getDriver();
await driver.get("<your site url>");
const cdpConnection = await driver.createCDPConnection('page');
//Latitude and longitude of Tokyo, Japan
const coordinates = {
latitude: 35.689487,
longitude: 139.691706,
accuracy: 100,
};
await cdpConnection.execute(
"Emulation.setGeolocationOverride",
coordinates
);
await driver.quit();
}
executeCDPCommands();
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.devtools.HasDevTools
// Replace the version to match the Chrome version
import org.openqa.selenium.devtools.v91.emulation.Emulation
import org.openqa.selenium.remote.Augmenter
import org.openqa.selenium.remote.RemoteWebDriver
import java.net.URL
import java.util.Optional
fun main() {
val chromeOptions = ChromeOptions()
var driver: WebDriver = RemoteWebDriver(URL("<grid-url>"), chromeOptions)
driver = Augmenter().augment(driver)
val devTools = (driver as HasDevTools).devTools
devTools.createSession()
devTools.send(
Emulation.setGeolocationOverride(
Optional.of(52.5043),
Optional.of(13.4501),
Optional.of(1)
)
)
driver["https://my-location.org/"]
driver.quit()
}
覆盖设备模式
使用Selenium与CDP的集成,
可以覆盖当前设备模式并模拟新模式.
Width, height, mobile和deviceScaleFactor是必需的参数.
可选参数包括scale, screenWidth,
screenHeight, positionX, positionY,
dontSetVisible, screenOrientation, viewport和displayFeature.
ChromeDriver driver = new ChromeDriver();
DevTools devTools = 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();
from selenium import webdriver
driver = webdriver.Chrome()
// iPhone 11 Pro dimensions
set_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>")
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
using System.Threading.Tasks;
using OpenQA.Selenium.DevTools.V91.Emulation;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using DevToolsSessionDomains = OpenQA.Selenium.DevTools.V91.DevToolsSessionDomains;
namespace Selenium4Sample {
public class ExampleDevice {
protected IDevToolsSession session;
protected IWebDriver driver;
protected DevToolsSessionDomains devToolsSession;
public async Task DeviceModeTest() {
new DriverManager().SetUpDriver(new ChromeConfig());
ChromeOptions chromeOptions = new ChromeOptions();
//Set ChromeDriver
driver = new ChromeDriver();
//Get DevTools
IDevTools devTools = driver as IDevTools;
//DevTools Session
session = devTools.GetDevToolsSession();
var deviceModeSetting = new SetDeviceMetricsOverrideCommandSettings();
deviceModeSetting.Width = 600;
deviceModeSetting.Height = 1000;
deviceModeSetting.Mobile = true;
deviceModeSetting.DeviceScaleFactor = 50;
await session
.GetVersionSpecificDomains < OpenQA.Selenium.DevTools.V91.DevToolsSessionDomains > ()
.Emulation
.SetDeviceMetricsOverride(deviceModeSetting);
driver.Url = "<your site url>";
}
}
}
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
metrics = { width: 300,
height: 200,
mobile: true,
deviceScaleFactor: 50 }
driver.execute_cdp('Emulation.setDeviceMetricsOverride', metrics)
driver.get 'https://www.google.com'
ensure
driver.quit
end
const {Builder} = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');
const options = new firefox.Options();
// enable debugger for CDP
options.enableDebugger();
(async function example() {
try {
let driver = await new Builder().forBrowser('firefox').setFirefoxOptions(options).build();
const pageCdpConnection = await driver.createCDPConnection('page');
const metrics = {
width: 300,
height: 200,
deviceScaleFactor: 50,
mobile: true,
};
await pageCdpConnection.execute(
"Emulation.setDeviceMetricsOverride",
metrics
);
await driver.get("https://www.google.com");
await driver.quit();
} catch (e) {
console.log(e);
}
})();
fun kotlinOverridDeviceMode() {
val driver = ChromeDriver()
val deviceMetrics: Map<String, Any> = object : HashMap<String, Any>() {
init {
put("width", 600)
put("height", 1000)
put("mobile", true)
put("deviceScaleFactor", 50)
}
}
driver.executeCdpCommand("Emulation.setDeviceMetricsOverride", deviceMetrics)
driver.get("https://www.google.com")
driver.quit()
}
Collect various performance metrics while navigating the application.
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
public void performanceMetricsExample() {
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
devTools.send(Performance.enable(Optional.empty()));
List<Metric> metricList = devTools.send(Performance.getMetrics());
driver.get("https://google.com");
driver.quit();
for(Metric m : metricList) {
System.out.println(m.getName() + " = " + m.getValue());
}
}
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.duckduckgo.com')
driver.execute_cdp_cmd('Performance.enable', {})
t = driver.execute_cdp_cmd('Performance.getMetrics', {})
print(t)
driver.quit()
// File must contain the following using statements
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
// We must use a version-specific set of domains
using OpenQA.Selenium.DevTools.V94.Performance;
public async Task PerformanceMetricsExample()
{
IWebDriver driver = new ChromeDriver();
IDevTools devTools = driver as IDevTools;
DevToolsSession session = devTools.GetDevToolsSession();
await session.SendCommand<EnableCommandSettings>(new EnableCommandSettings());
var metricsResponse =
await session.SendCommand<GetMetricsCommandSettings, GetMetricsCommandResponse>(
new GetMetricsCommandSettings());
driver.Navigate().GoToUrl("http://www.google.com");
driver.Quit();
var metrics = metricsResponse.Metrics;
foreach (Metric metric in metrics)
{
Console.WriteLine("{0} = {1}", metric.Name, metric.Value);
}
}
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get 'https://www.duckduckgo.com'
driver.execute_cdp('Performance.enable', {})
metrics = driver.execute_cdp('Performance.getMetrics', {})
puts metrics
ensure
driver.quit
end
await driver.get("https://www.duckduckgo.com");
await driver.sendAndGetDevToolsCommand('Performance.enable')
let result = await driver.sendAndGetDevToolsCommand('Performance.getMetrics')
console.log(result)
await driver.quit();
val driver = ChromeDriver()
val devTools = driver.devTools
devTools.createSession()
devTools.send(Performance.enable(Optional.empty()))
val metricList: List<Metric> = devTools.send(Performance.getMetrics())
driver["https://google.com"]
driver.quit()
for (m in metricList) {
println(m.name.toString() + " = " + m.value)
}
3 - BiDirectional API (CDP implementation)
Page being translated from
English to Chinese. Do you speak Chinese? Help us to translate
it by sending us pull requests!
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.
Predicate<URI> uriPredicate = uri -> uri.getHost().contains("your-domain.com");
((HasAuthentication) driver).register(uriPredicate, UsernameAndPassword.of("admin", "password"));
driver.get("https://your-domain.com/login");
NetworkAuthenticationHandler handler = new NetworkAuthenticationHandler()
{
UriMatcher = (d) => d.Host.Contains("your-domain.com"),
Credentials = new PasswordCredentials("admin", "password")
};
INetwork networkInterceptor = driver.Manage().Network;
networkInterceptor.AddAuthenticationHandler(handler);
await networkInterceptor.StartMonitoring();
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.devtools.new
driver.register(username: 'username', password: 'password')
driver.get '<your site url>'
ensure
driver.quit
end
const {Builder} = require('selenium-webdriver');
(async function example() {
try {
let driver = await new Builder()
.forBrowser('chrome')
.build();
const pageCdpConnection = await driver.createCDPConnection('page');
await driver.register('username', 'password', pageCdpConnection);
await driver.get('https://the-internet.herokuapp.com/basic_auth');
await driver.quit();
}catch (e){
console.log(e)
}
}())
val uriPredicate = Predicate { uri: URI ->
uri.host.contains("your-domain.com")
}
(driver as HasAuthentication).register(uriPredicate, UsernameAndPassword.of("admin", "password"))
driver.get("https://your-domain.com/login")
Mutation Observation
Mutation Observation is the ability to capture events via
WebDriver BiDi when there are DOM mutations on a specific
element in the DOM.
ChromeDriver driver = new ChromeDriver();
AtomicReference<DomMutationEvent> seen = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
((HasLogEvents) driver).onLogEvent(domMutation(mutation -> {
seen.set(mutation);
latch.countDown();
}));
driver.get("https://www.google.com");
WebElement span = driver.findElement(By.cssSelector("span"));
((JavascriptExecutor) driver).executeScript("arguments[0].setAttribute('cheese', 'gouda');", span);
assertThat(latch.await(10, SECONDS), is(true));
assertThat(seen.get().getAttributeName(), is("cheese"));
assertThat(seen.get().getCurrentValue(), is("gouda"));
driver.quit();
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
async with driver.log.mutation_events() as event:
pages.load("dynamic.html")
driver.find_element(By.ID, "reveal").click()
WebDriverWait(driver, 5)\
.until(EC.visibility_of(driver.find_element(By.ID, "revealed")))
assert event["attribute_name"] == "style"
assert event["current_value"] == ""
assert event["old_value"] == "display:none;"
List<DomMutationData> attributeValueChanges = new List<DomMutationData>();
DefaultWait<List<DomMutationData>> wait = new DefaultWait<List<DomMutationData>>(attributeValueChanges);
wait.Timeout = TimeSpan.FromSeconds(3);
IJavaScriptEngine monitor = new JavaScriptEngine(driver);
monitor.DomMutated += (sender, e) =>
{
attributeValueChanges.Add(e.AttributeData);
};
await monitor.StartEventMonitoring();
driver.Navigate().GoToUrl("http://www.google.com");
IWebElement span = driver.FindElement(By.CssSelector("span"));
await monitor.EnableDomMutationMonitoring();
((IJavaScriptExecutor) driver).ExecuteScript("arguments[0].setAttribute('cheese', 'gouda');", span);
wait.Until((list) => list.Count > 0);
Console.WriteLine("Found {0} DOM mutation events", attributeValueChanges.Count);
foreach(var record in attributeValueChanges)
{
Console.WriteLine("Attribute name: {0}", record.AttributeName);
Console.WriteLine("Attribute value: {0}", record.AttributeValue);
}
await monitor.DisableDomMutationMonitoring();
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
begin
driver.on_log_event(:mutation) { |mutation| mutations.push(mutation) }
driver.navigate.to url_for('dynamic.html')
driver.find_element(id: 'reveal').click
wait.until { mutations.any? }
mutation = mutations.first
expect(mutation.element).to eq(driver.find_element(id: 'revealed'))
expect(mutation.attribute_name).to eq('style')
expect(mutation.current_value).to eq('')
expect(mutation.old_value).to eq('display:none;')
ensure
driver.quit
end
const {Builder, until} = require('selenium-webdriver');
const assert = require("assert");
(async function example() {
try {
let driver = await new Builder()
.forBrowser('chrome')
.build();
const cdpConnection = await driver.createCDPConnection('page');
await driver.logMutationEvents(cdpConnection, event => {
assert.deepStrictEqual(event['attribute_name'], 'style');
assert.deepStrictEqual(event['current_value'], "");
assert.deepStrictEqual(event['old_value'], "display:none;");
});
await driver.get('dynamic.html');
await driver.findElement({id: 'reveal'}).click();
let revealed = driver.findElement({id: 'revealed'});
await driver.wait(until.elementIsVisible(revealed), 5000);
await driver.quit();
}catch (e){
console.log(e)
}
}())
Listen to console.log
events
Listen to the console.log
events and register callbacks to process the event.
ChromeDriver driver = new ChromeDriver();
DevTools devTools = 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();
import trio
from selenium import webdriver
from selenium.webdriver.common.log import Log
async def printConsoleLogs():
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome()
driver.get("http://www.google.com")
async with driver.bidi_connection() as session:
log = Log(driver, session)
from selenium.webdriver.common.bidi.console import Console
async with log.add_listener(Console.ALL) as messages:
driver.execute_script("console.log('I love cheese')")
print(messages["message"])
driver.quit()
trio.run(printConsoleLogs)
IJavaScriptEngine monitor = new JavaScriptEngine(driver);
List<string> consoleMessages = new List<string>();
monitor.JavaScriptConsoleApiCalled += (sender, e) =>
{
Console.WriteLine("Log: {0}", e.MessageContent);
};
await monitor.StartEventMonitoring();
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get 'http://www.google.com'
logs = []
driver.on_log_event(:console) do |event|
logs.push(event)
puts logs.length
end
driver.execute_script('console.log("here")')
ensure
driver.quit
end
const {Builder} = require('selenium-webdriver');
(async () => {
try {
let driver = new Builder()
.forBrowser('chrome')
.build();
const cdpConnection = await driver.createCDPConnection('page');
await driver.onLogEvent(cdpConnection, function (event) {
console.log(event['args'][0]['value']);
});
await driver.executeScript('console.log("here")');
await driver.quit();
}catch (e){
console.log(e);
}
})()
fun kotlinConsoleLogExample() {
val driver = ChromeDriver()
val devTools = driver.devTools
devTools.createSession()
val logConsole = { c: ConsoleEvent -> print("Console log message is: " + c.messages)}
devTools.domains.events().addConsoleListener(logConsole)
driver.get("https://www.google.com")
val executor = driver as JavascriptExecutor
executor.executeScript("console.log('Hello World')")
val input = 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.
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
public void jsExceptionsExample() {
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
List<JavascriptException> jsExceptionsList = new ArrayList<>();
Consumer<JavascriptException> addEntry = jsExceptionsList::add;
devTools.getDomains().events().addJavascriptExceptionListener(addEntry);
driver.get("<your site url>");
WebElement link2click = 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 (JavascriptException jsException : jsExceptionsList) {
System.out.println("JS exception message: " + jsException.getMessage());
System.out.println("JS exception system information: " + jsException.getSystemInformation());
jsException.printStackTrace();
}
}
async def catchJSException():
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome()
async with driver.bidi_connection() as session:
driver.get("<your site url>")
log = Log(driver, session)
async with log.add_js_error_listener() as messages:
# Operation on the website that throws an JS error
print(messages)
driver.quit()
List<string> exceptionMessages = new List<string>();
IJavaScriptEngine monitor = new JavaScriptEngine(driver);
monitor.JavaScriptExceptionThrown += (sender, e) =>
{
exceptionMessages.Add(e.Message);
};
await monitor.StartEventMonitoring();
driver.Navigate.GoToUrl("<your site url>");
IWebElement link2click = 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 (string message in exceptionMessages)
{
Console.WriteLine("JS exception message: {0}", message);
}
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
begin
driver.get '<your-site-url>'
exceptions = []
driver.on_log_event(:exception) do |event|
exceptions.push(event)
puts exceptions.length
end
#Actions causing JS exceptions
ensure
driver.quit
end
const {Builder, By} = require('selenium-webdriver');
(async () => {
try {
let driver = new Builder()
.forBrowser('chrome')
.build();
const cdpConnection = await driver.createCDPConnection('page')
await driver.onLogException(cdpConnection, function (event) {
console.log(event['exceptionDetails']);
})
await driver.get('https://the-internet.herokuapp.com');
const link = await driver.findElement(By.linkText('Checkboxes'));
await driver.executeScript("arguments[0].setAttribute(arguments[1], arguments[2]);", link, "onclick","throw new Error('Hello, world!')");
await link.click();
await driver.quit();
}catch (e){
console.log(e);
}
})()
fun kotlinJsErrorListener() {
val driver = ChromeDriver()
val devTools = driver.devTools
devTools.createSession()
val logJsError = { j: JavascriptException -> print("Javascript error: '" + j.localizedMessage + "'.") }
devTools.domains.events().addJavascriptExceptionListener(logJsError)
driver.get("https://www.google.com")
val link2click = driver.findElement(By.name("q"))
(driver as JavascriptExecutor).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.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.NetworkInterceptor;
import org.openqa.selenium.remote.http.Contents;
import org.openqa.selenium.remote.http.Filter;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Route;
NetworkInterceptor interceptor = new NetworkInterceptor(
driver,
Route.matching(req -> true)
.to(() -> req -> new HttpResponse()
.setStatus(200)
.addHeader("Content-Type", MediaType.HTML_UTF_8.toString())
.setContent(utf8String("Creamy, delicious cheese!"))));
driver.get("https://example-sausages-site.com");
String source = driver.getPageSource();
assertThat(source).contains("delicious cheese!");
Currently unavailable in python due the inability to mix certain async and sync commands
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
driver.intercept do |request, &continue|
uri = URI(request.url)
if uri.path.end_with?('one.js')
uri.path = '/devtools_request_interception_test/two.js'
request.url = uri.to_s
end
continue.call(request)
end
driver.navigate.to url_for('devToolsRequestInterceptionTest.html')
driver.find_element(tag_name: 'button').click
expect(driver.find_element(id: 'result').text).to eq('two')
const connection = await driver.createCDPConnection('page')
let url = fileServer.whereIs("/cheese")
let httpResponse = new HttpResponse(url)
httpResponse.addHeaders("Content-Type", "UTF-8")
httpResponse.body = "sausages"
await driver.onIntercept(connection, httpResponse, async function () {
let body = await driver.getPageSource()
assert.strictEqual(body.includes("sausages"), true, `Body contains: ${body}`)
})
driver.get(url)
val driver = ChromeDriver()
val interceptor = new NetworkInterceptor(
driver,
Route.matching(req -> true)
.to(() -> req -> new HttpResponse()
.setStatus(200)
.addHeader("Content-Type", MediaType.HTML_UTF_8.toString())
.setContent(utf8String("Creamy, delicious cheese!"))))
driver.get(appServer.whereIs("/cheese"))
String source = driver.getPageSource()
4 - BiDirectional API (W3C compliant)
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.
4.1 - Browsing Context
Page being translated from
English to Chinese. Do you speak Chinese? Help us to translate
it by sending us pull requests!
This section contains the APIs related to browsing context commands.
Open a new window
Creates a new browsing context in a new window.
BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.WINDOW);
Open a new tab
Creates a new browsing context in a new tab.
BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);
Use existing window handle
Creates a browsing context for the existing tab/window to run commands.
String id = driver.getWindowHandle();
BrowsingContext browsingContext = new BrowsingContext(driver, id);
Open a window with a reference browsing context
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.
BrowsingContext
browsingContext =
new BrowsingContext(driver, WindowType.WINDOW, driver.getWindowHandle());
Open a tab with a reference browsing context
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.
BrowsingContext
browsingContext =
new BrowsingContext(driver, WindowType.TAB, driver.getWindowHandle());
Navigate to a URL
BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);
NavigationResult info = browsingContext.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
Assertions.assertNotNull(browsingContext.getId());
Assertions.assertNull(info.getNavigationId());
Assertions.assertTrue(info.getUrl().contains("/bidi/logEntryAdded.html"));
Navigate to a URL with readiness state
BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);
NavigationResult info = browsingContext.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html",
ReadinessState.COMPLETE);
Assertions.assertNotNull(browsingContext.getId());
Assertions.assertNull(info.getNavigationId());
Assertions.assertTrue(info.getUrl().contains("/bidi/logEntryAdded.html"));
Get browsing context tree
Provides a tree of all browsing contexts descending from the parent browsing context, including the parent browsing context.
String referenceContextId = driver.getWindowHandle();
BrowsingContext parentWindow = new BrowsingContext(driver, referenceContextId);
parentWindow.navigate("https://www.selenium.dev/selenium/web/iframes.html", ReadinessState.COMPLETE);
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree();
Assertions.assertEquals(1, contextInfoList.size());
BrowsingContextInfo info = contextInfoList.get(0);
Assertions.assertEquals(1, info.getChildren().size());
Assertions.assertEquals(referenceContextId, info.getId());
Assertions.assertTrue(info.getChildren().get(0).getUrl().contains("formPage.html"));
Get browsing context tree with depth
Provides a tree of all browsing contexts descending from the parent browsing context, including the parent browsing context upto the depth value passed.
String referenceContextId = driver.getWindowHandle();
BrowsingContext parentWindow = new BrowsingContext(driver, referenceContextId);
parentWindow.navigate("https://www.selenium.dev/selenium/web/iframes.html", ReadinessState.COMPLETE);
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(0);
Assertions.assertEquals(1, contextInfoList.size());
BrowsingContextInfo info = contextInfoList.get(0);
Assertions.assertNull(info.getChildren()); // since depth is 0
Assertions.assertEquals(referenceContextId, info.getId());
Get All Top level browsing contexts
BrowsingContext window1 = new BrowsingContext(driver, driver.getWindowHandle());
BrowsingContext window2 = new BrowsingContext(driver, WindowType.WINDOW);
List<BrowsingContextInfo> contextInfoList = window1.getTopLevelContexts();
Assertions.assertEquals(2, contextInfoList.size());
Close a tab/window
BrowsingContext window1 = new BrowsingContext(driver, WindowType.WINDOW);
BrowsingContext window2 = new BrowsingContext(driver, WindowType.WINDOW);
window2.close();
Assertions.assertThrows(BiDiException.class, window2::getTree);
4.2 - BiDirectional API (W3C compliant)
Page being translated from
English to Chinese. Do you speak Chinese? Help us to translate
it by sending us pull requests!
This section contains the APIs related to logging.
Listen to console.log
events
Listen to the console.log
events and register callbacks to process the event.
try (LogInspector logInspector = new LogInspector(driver)) {
CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();
logInspector.onConsoleLog(future::complete);
driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
driver.findElement(By.id("consoleLog")).click();
ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS);
Assertions.assertEquals("Hello, world!", logEntry.getText());
Assertions.assertNull(logEntry.getRealm());
Assertions.assertEquals(1, logEntry.getArgs().size());
Assertions.assertEquals("console", logEntry.getType());
Assertions.assertEquals("log", logEntry.getMethod());
Assertions.assertNull(logEntry.getStackTrace());
}
const inspector = await LogInspector(driver)
await inspector.onConsoleEntry(function (log) {
logEntry = log
})
await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')
await driver.findElement({ id: 'consoleLog' }).click()
assert.equal(logEntry.text, 'Hello, world!')
assert.equal(logEntry.realm, null)
assert.equal(logEntry.type, 'console')
assert.equal(logEntry.level, 'info')
assert.equal(logEntry.method, 'log')
assert.equal(logEntry.stackTrace, null)
assert.equal(logEntry.args.length, 1)
Listen to JS Exceptions
Listen to the JS Exceptions
and register callbacks to process the exception details.
try (LogInspector logInspector = new LogInspector(driver)) {
CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();
logInspector.onJavaScriptException(future::complete);
driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
driver.findElement(By.id("jsException")).click();
JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);
Assertions.assertEquals("Error: Not working", logEntry.getText());
Assertions.assertEquals("javascript", logEntry.getType());
}
const inspector = await LogInspector(driver)
await inspector.onJavascriptException(function (log) {
logEntry = log
})
await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')
await driver.findElement({ id: 'jsException' }).click()
assert.equal(logEntry.text, 'Error: Not working')
assert.equal(logEntry.type, 'javascript')
assert.equal(logEntry.level, 'error')
Listen to JS Logs
Listen to all JS logs at all levels and register callbacks to process the log.
try (LogInspector logInspector = new LogInspector(driver)) {
CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();
logInspector.onJavaScriptLog(future::complete);
driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
driver.findElement(By.id("jsException")).click();
JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);
Assertions.assertEquals("Error: Not working", logEntry.getText());
Assertions.assertEquals("javascript", logEntry.getType());
Assertions.assertEquals(LogLevel.ERROR, logEntry.getLevel());
}