#browser-automation #cross-browser-testing
## Primer
Access to [[Chrome DevTools Protocol (CDP)]] in WebDriver was announced to land in Selenium with version 4. If I understand it correctly, the CDP integration can be seen as a first step into the direction of [[WebDriver BiDi]]. The aim is to take CDP features and standardize them in WebDriver BiDi specification.
CDP in WebDriver works like so: The WebDriver client finds out the `websocketDebuggerUrl` provided by Chrome and establishes a direct connection to it (that‘s exactly what Puppeteer does). So, Chrome DevTools handles two debugger connections: One with Chromedriver and another with the WebDriver client.
## How to work with CDP in Selenium WebDriver
To apply CDP commands in Selenium WebDriver, it is not really helpful to look at Puppeteer examples, but at the raw CDP documentation instead.
As an example, to listen to events emitted by the browser via CDP, this is what needs to happen:
- Establish a Websocket connection to the browser's debugger
- Enable the desired domain (`Network` for introspecting network-related stuff, `Runtime` for introspecting e.g. the browser console, etc.)
- Set up a listener for desired events (e.g. `Network.requestWillBeSent` for outgoing requests)
As far as I understood, the plan is that WebDriver (or better: [[+Selenium architecture#WebDriver BiDi|WebDriver BiDi]] at a later stage) abstracts most of that stuff away behind stable APIs. As of 2021, though, I can only find broad and easy-to-use support for **all** CDP domains in the Java bindings. In JavaScript, things are a bit different.
## CDP in the JavaScript bindings
### How to use
For the stuff that is supported in the JavaScript bindings ([**selenium-webdriver**](https://www.npmjs.com/package/selenium-webdriver)) currently (console log events, console error events, DOM mutation listener and basic auth), the usage looks as follows.
First, create a WebSocket connection:
```js
const cdpConnection = await driver.createCDPConnection('page');
```
Second, call the desired function. It takes as arguments the `cdpConnection` and a callback function that is to be executed when the desired event occurs.
As of 2023, the Selenium documentation has examples for all the things possible, also with explainers:
- [BiDirectional API (CDP implementation)](https://www.selenium.dev/documentation/webdriver/bidirectional/bidi_api/)
- [Chrome DevTools](https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/)
### Listen to console log events
> [!info]
> As of 2023, this is already possible via WebDriver BiDi: See [Selenium docs](https://www.selenium.dev/documentation/webdriver/bidirectional/bidirectional_w3c/log/#listen-to-consolelog-events)
For console log events, this is:
```js
let consoleLogMessage;
await driver.onLogEvent(cdpConnection, function(event) {
consoleLogMessage = event['args'][0]['value'];
})
```
### Listen to mutation events
It is possible to listen to changes in the DOM with `driver.logMutationEvents`. Whenever a change in the DOM happens, the defined callback function is executed. The argument for that function is handed over an "event" which consists of the respective `WebElement` as well as additional information:
- `attribute_name`
- `current_value`
- `old_value`
With the `WebElement`, it is possible to do the usual stuff, such as getting tag name, text, CSS value and so on.
See [Selenium docs](https://www.selenium.dev/documentation/webdriver/bidirectional/bidi_api/#mutation-observation).
### One-off commands
With `createCDPConnection` and then `cdpConnection.execute()` or `cdpConnection.send` , one can send one-off commands to the browser via CDP. However, this approach is not future-proof, since the CDP API can break with a browser version upgrade. Also see [this tweet by David Burns](https://twitter.com/AutomatedTester/status/1435377070440333318)).
```js
const cdpConnection = await driver.createCDPConnection('page');
await cdpConnection.send('Page.startScreencast', {
format: 'jpeg',
quality: 50,
maxWidth: 1920,
maxHeight: 1080,
everyNthFrame: 1,
});
```
The Node.js bindings also have `sendDevToolsCommand()` and `sendDevToolsCommandAndGetReturn()`, but they are not using `cdpConnection.execute()` or `cdpConnection.send()`, but an API endpoint exposed by chromedriver (`/session/:sessionId/chromium/send_command`).
### Recording video
[[Chrome DevTools Protocol (CDP)]] allows to create a video recording of a browser session. Technically, this works by listening to [`Page.screencastFrame` events]([Page.screencastFrame](https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-screencastFrame)). These events fire whenever something changes on the page and provide a screenshot of the current page. One can then create a video of the recorded screenshots.
I managed to make this work with WebDriver, but it requires patching the selenium-webdriver package. This is because the JavaScript bindings do not offer a generic listener for CDP events and [the Selenium project does not want to add it anymore](https://github.com/SeleniumHQ/selenium/issues/12017) (which I understand).
### Not everything works on a Selenium Grid at version 3
(As of Spring 2021)
I added CDP-using code to my [[#Repo for experiments]]. It is working fine locally in Chrome, but that is a different story when run on Sauce Labs with Selenium 3. There, only the tests using `sendDevtoolsCommand()` and `sendAndGetDevtoolsCommand()` are working. Every test that is establishing a CDP listener did not work (throwing an error related to the debugger connection to chromedriver I guess: `ECONNREFUSED 127.0.0.1:64533`).
### Repo for experiments
[explore-webdriver project (simpleCdpTest)](https://github.com/systemboogie/explore-webdriver/blob/7b05e3aeb0389a1f9c7df8e2a35c7f1f69aeec64/test/cdpTest.js).
## Useful articles
- [Selenium Chrome DevTools Protocol (CDP) API: How Does It Work?](https://applitools.com/blog/selenium-chrome-devtools-protocol-cdp-how-does-it-work/) - Describes how CDP is working in WebDriver. It also lists the available CDP domains together with some examples
- [Selenium 4: Chrome DevTools Protocol \[What’s New\]](https://applitools.com/blog/selenium-4-chrome-devtools/) - Shows how network interception works with CDP in WebDriver
- [Using Chrome DevTools Protocol#Using Puppeteer's CDPSession](https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md#using-puppeteers-cdpsession) - The CDPConnection in Webdriver seems to be the equivalent to CDPSession in Puppeteer
- [Intercepting and Modifying responses with Chrome via the Devtools Protocol](https://securityboulevard.com/2018/09/intercepting-and-modifying-responses-with-chrome-via-the-devtools-protocol/) - For illustration and inspiration, an article describing the interception of network requests by using pure CDP
- StackOverflow has a few articles on CDP, which also can be used as inspiration or starting points
- [Questions tagged with chrome-devtools-Protocol](https://stackoverflow.com/questions/tagged/chrome-devtools-protocol)
- [How to add event listeners in Selenium via CDP](https://stackoverflow.com/questions/66227508/selenium-4-0-0-beta-1-how-add-event-listeners-in-cdp)