Chrome DevTools Script Features
Script features using CDP.
While Selenium 4 provides direct access to the Chrome DevTools Protocol, these methods will eventually be removed when WebDriver BiDi implemented.
Script Pinning
ScriptKey key = ((JavascriptExecutor) driver).pin("return arguments;");
List<Object> arguments =
(List<Object>) ((JavascriptExecutor) driver).executeScript(key, 1, true, element);
Show full example
package dev.selenium.bidi.cdp;
import static org.openqa.selenium.devtools.events.CdpEventTypes.domMutation;
import dev.selenium.BaseTest;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.logging.HasLogEvents;
import org.openqa.selenium.support.ui.WebDriverWait;
public class ScriptTest extends BaseTest {
@BeforeEach
public void createSession() {
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
@Test
public void pinScript() {
driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html");
WebElement element = driver.findElement(By.id("id1"));
ScriptKey key = ((JavascriptExecutor) driver).pin("return arguments;");
List<Object> arguments =
(List<Object>) ((JavascriptExecutor) driver).executeScript(key, 1, true, element);
Assertions.assertEquals(List.of(1L, true, element), arguments);
}
@Test
public void mutatedElements() {
driver.get("https://www.selenium.dev/selenium/web/dynamic.html");
CopyOnWriteArrayList<WebElement> mutations = new CopyOnWriteArrayList<>();
((HasLogEvents) driver).onLogEvent(domMutation(e -> mutations.add(e.getElement())));
driver.findElement(By.id("reveal")).click();
wait.until(_d -> !mutations.isEmpty());
Assertions.assertEquals(mutations.get(0), driver.findElement(By.id("revealed")));
}
}
var key = await new JavaScriptEngine(driver).PinScript("return arguments;");
var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element);
Show full example
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
namespace SeleniumDocs.BiDi.CDP
{
[TestClass]
public class ScriptTest : BaseChromeTest
{
[TestMethod]
public async Task PinScript()
{
driver.Url = "https://www.selenium.dev/selenium/web/xhtmlTest.html";
var element = driver.FindElement(By.Id("id1"));
var key = await new JavaScriptEngine(driver).PinScript("return arguments;");
var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element);
var expected = new List<object>
{
1L,
true,
element
};
CollectionAssert.AreEqual(expected, (ICollection)arguments);
}
[TestMethod]
public async Task MutatedElements()
{
driver.Url = "https://www.selenium.dev/selenium/web/dynamic.html";
var mutations = new List<IWebElement>();
using IJavaScriptEngine monitor = new JavaScriptEngine(driver);
monitor.DomMutated += (_, e) =>
{
var locator = By.CssSelector($"*[data-__webdriver_id='{e.AttributeData.TargetId}']");
mutations.Add(driver.FindElement(locator));
};
await monitor.StartEventMonitoring();
await monitor.EnableDomMutationMonitoring();
driver.FindElement(By.Id("reveal")).Click();
new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !mutations.IsNullOrEmpty());
await monitor.DisableDomMutationMonitoring();
monitor.StopEventMonitoring();
var revealed = driver.FindElement(By.Id("revealed"));
Assert.AreEqual(revealed, mutations[0]);
}
}
}
key = driver.pin_script('return arguments;')
arguments = driver.execute_script(key, 1, true, element)
Show full example
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Script' do
let(:driver) { start_session }
it 'pins script' do
driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html')
element = driver.find_element(id: 'id1')
key = driver.pin_script('return arguments;')
arguments = driver.execute_script(key, 1, true, element)
expect(arguments).to eq([1, true, element])
end
it 'gets mutated elements' do
driver.get 'https://www.selenium.dev/selenium/web/dynamic.html'
mutations = []
driver.on_log_event(:mutation) { |mutation| mutations << mutation.element }
driver.find_element(id: 'reveal').click
Selenium::WebDriver::Wait.new(timeout: 30).until { mutations.any? }
expect(mutations).to include(driver.find_element(id: 'revealed'))
end
end
DOM Mutation Handlers
Show full example
package dev.selenium.bidi.cdp;
import com.google.common.net.MediaType;
import dev.selenium.BaseTest;
import java.net.*;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.NetworkInterceptor;
import org.openqa.selenium.devtools.v134.browser.Browser;
import org.openqa.selenium.devtools.v134.network.Network;
import org.openqa.selenium.devtools.v134.performance.Performance;
import org.openqa.selenium.devtools.v134.performance.model.Metric;
import org.openqa.selenium.remote.http.*;
import org.openqa.selenium.support.ui.WebDriverWait;
public class NetworkTest extends BaseTest {
@BeforeEach
public void createSession() {
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
@Test
public void basicAuthentication() {
Predicate<URI> uriPredicate = uri -> uri.toString().contains("herokuapp.com");
Supplier<Credentials> authentication = UsernameAndPassword.of("admin", "admin");
((HasAuthentication) driver).register(uriPredicate, authentication);
driver.get("https://the-internet.herokuapp.com/basic_auth");
String successMessage = "Congratulations! You must have the proper credentials.";
WebElement elementMessage = driver.findElement(By.tagName("p"));
Assertions.assertEquals(successMessage, elementMessage.getText());
}
@Test
public void recordResponse() {
CopyOnWriteArrayList<String> contentType = new CopyOnWriteArrayList<>();
try (NetworkInterceptor ignored =
new NetworkInterceptor(
driver,
(Filter)
next ->
req -> {
HttpResponse res = next.execute(req);
contentType.add(res.getHeader("Content-Type"));
return res;
})) {
driver.get("https://www.selenium.dev/selenium/web/blank.html");
wait.until(_d -> contentType.size() > 1);
}
Assertions.assertEquals("text/html; charset=utf-8", contentType.get(0));
}
@Test
public void transformResponses() {
try (NetworkInterceptor ignored =
new NetworkInterceptor(
driver,
Route.matching(req -> true)
.to(
() ->
req ->
new HttpResponse()
.setStatus(200)
.addHeader("Content-Type", MediaType.HTML_UTF_8.toString())
.setContent(Contents.utf8String("Creamy, delicious cheese!"))))) {
driver.get("https://www.selenium.dev/selenium/web/blank.html");
}
WebElement body = driver.findElement(By.tagName("body"));
Assertions.assertEquals("Creamy, delicious cheese!", body.getText());
}
@Test
public void interceptRequests() {
AtomicBoolean completed = new AtomicBoolean(false);
try (NetworkInterceptor ignored =
new NetworkInterceptor(
driver,
(Filter)
next ->
req -> {
if (req.getUri().contains("one.js")) {
req =
new HttpRequest(
HttpMethod.GET, req.getUri().replace("one.js", "two.js"));
}
completed.set(true);
return next.execute(req);
})) {
driver.get("https://www.selenium.dev/selenium/web/devToolsRequestInterceptionTest.html");
driver.findElement(By.tagName("button")).click();
}
Assertions.assertEquals("two", driver.findElement(By.id("result")).getText());
}
@Test
public void performanceMetrics() {
driver.get("https://www.selenium.dev/selenium/web/frameset.html");
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(Performance.enable(Optional.empty()));
List<Metric> metricList = devTools.send(Performance.getMetrics());
Map<String, Number> metrics = new HashMap<>();
for (Metric metric : metricList) {
metrics.put(metric.getName(), metric.getValue());
}
Assertions.assertTrue(metrics.get("DevToolsCommandDuration").doubleValue() > 0);
Assertions.assertEquals(12, metrics.get("Frames").intValue());
}
@Test
public void setCookie() {
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(
Network.setCookie(
"cheese",
"gouda",
Optional.empty(),
Optional.of("www.selenium.dev"),
Optional.empty(),
Optional.of(true),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty()));
driver.get("https://www.selenium.dev");
Cookie cheese = driver.manage().getCookieNamed("cheese");
Assertions.assertEquals("gouda", cheese.getValue());
}
@Test
public void waitForDownload() {
driver.get("https://www.selenium.dev/selenium/web/downloads/download.html");
DevTools devTools = ((HasDevTools) driver).getDevTools();
devTools.createSession();
devTools.send(
Browser.setDownloadBehavior(
Browser.SetDownloadBehaviorBehavior.ALLOWANDNAME,
Optional.empty(),
Optional.of(""),
Optional.of(true)));
AtomicBoolean completed = new AtomicBoolean(false);
devTools.addListener(
Browser.downloadProgress(),
e -> completed.set(Objects.equals(e.getState().toString(), "completed")));
driver.findElement(By.id("file-2")).click();
Assertions.assertDoesNotThrow(() -> wait.until(_d -> completed));
}
}
async with driver.bidi_connection() as session:
async with Log(driver, session).mutation_events() as event:
Show full example
import pytest
import trio
from selenium.webdriver.common.by import By
from selenium.webdriver.common.log import Log
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.mark.trio
async def test_mutation(driver):
async with driver.bidi_connection() as session:
async with Log(driver, session).mutation_events() as event:
await trio.to_thread.run_sync(lambda: driver.get('https://www.selenium.dev/selenium/web/dynamic.html'))
await trio.to_thread.run_sync(lambda: WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "reveal"))))
await trio.to_thread.run_sync(lambda: driver.find_element(By.ID, "reveal").click())
assert event["element"] == driver.find_element(By.ID, "revealed")
using IJavaScriptEngine monitor = new JavaScriptEngine(driver);
monitor.DomMutated += (_, e) =>
{
var locator = By.CssSelector($"*[data-__webdriver_id='{e.AttributeData.TargetId}']");
mutations.Add(driver.FindElement(locator));
};
await monitor.StartEventMonitoring();
await monitor.EnableDomMutationMonitoring();
Show full example
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
namespace SeleniumDocs.BiDi.CDP
{
[TestClass]
public class ScriptTest : BaseChromeTest
{
[TestMethod]
public async Task PinScript()
{
driver.Url = "https://www.selenium.dev/selenium/web/xhtmlTest.html";
var element = driver.FindElement(By.Id("id1"));
var key = await new JavaScriptEngine(driver).PinScript("return arguments;");
var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element);
var expected = new List<object>
{
1L,
true,
element
};
CollectionAssert.AreEqual(expected, (ICollection)arguments);
}
[TestMethod]
public async Task MutatedElements()
{
driver.Url = "https://www.selenium.dev/selenium/web/dynamic.html";
var mutations = new List<IWebElement>();
using IJavaScriptEngine monitor = new JavaScriptEngine(driver);
monitor.DomMutated += (_, e) =>
{
var locator = By.CssSelector($"*[data-__webdriver_id='{e.AttributeData.TargetId}']");
mutations.Add(driver.FindElement(locator));
};
await monitor.StartEventMonitoring();
await monitor.EnableDomMutationMonitoring();
driver.FindElement(By.Id("reveal")).Click();
new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !mutations.IsNullOrEmpty());
await monitor.DisableDomMutationMonitoring();
monitor.StopEventMonitoring();
var revealed = driver.FindElement(By.Id("revealed"));
Assert.AreEqual(revealed, mutations[0]);
}
}
}
driver.on_log_event(:mutation) { |mutation| mutations << mutation.element }
Show full example
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Script' do
let(:driver) { start_session }
it 'pins script' do
driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html')
element = driver.find_element(id: 'id1')
key = driver.pin_script('return arguments;')
arguments = driver.execute_script(key, 1, true, element)
expect(arguments).to eq([1, true, element])
end
it 'gets mutated elements' do
driver.get 'https://www.selenium.dev/selenium/web/dynamic.html'
mutations = []
driver.on_log_event(:mutation) { |mutation| mutations << mutation.element }
driver.find_element(id: 'reveal').click
Selenium::WebDriver::Wait.new(timeout: 30).until { mutations.any? }
expect(mutations).to include(driver.find_element(id: 'revealed'))
end
end