{"id":22360346,"url":"https://github.com/sergueik/selenium_cdp","last_synced_at":"2026-03-05T13:02:51.366Z","repository":{"id":39955409,"uuid":"200976311","full_name":"sergueik/selenium_cdp","owner":"sergueik","description":"Selenium 4x, executing Chrome DevTools Protocol commands","archived":false,"fork":false,"pushed_at":"2026-02-22T06:01:55.000Z","size":4040,"stargazers_count":51,"open_issues_count":2,"forks_count":14,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-02-22T13:06:10.518Z","etag":null,"topics":["cdp","chrome","chromium","edge","java","selenium"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sergueik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-08-07T05:09:23.000Z","updated_at":"2026-02-22T06:02:01.000Z","dependencies_parsed_at":"2023-02-17T13:45:58.278Z","dependency_job_id":"3f56e537-a24e-4649-be97-978e2bbd66fd","html_url":"https://github.com/sergueik/selenium_cdp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sergueik/selenium_cdp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergueik%2Fselenium_cdp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergueik%2Fselenium_cdp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergueik%2Fselenium_cdp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergueik%2Fselenium_cdp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergueik","download_url":"https://codeload.github.com/sergueik/selenium_cdp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergueik%2Fselenium_cdp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30127218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T12:40:50.676Z","status":"ssl_error","status_checked_at":"2026-03-05T12:39:32.209Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cdp","chrome","chromium","edge","java","selenium"],"created_at":"2024-12-04T16:15:45.399Z","updated_at":"2026-03-05T13:02:51.339Z","avatar_url":"https://github.com/sergueik.png","language":"Java","readme":"### Info\r\n\r\nThe project practices Java Selenium __4.0.x__ release\r\n[ChromiumDriver](https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/chromium/ChromiumDriver.java)\r\nto execute the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) a.k.a.\r\n__cdp__ commands - an entirely different set of API communicated to the Chrome browser family via `POST` [requests](https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/chromium/ChromiumDriverCommandExecutor.java) to `/session/$sessionId/goog/cdp/execute` with API-specific payload) feature (many of the cdp methods e.g. the [DOM](https://chromedevtools.github.io/devtools-protocol/tot/DOM) ones like\r\n\r\n  * `performSearch`,\r\n  * `getSearchResults`\r\n  * `getNodeForLocation`\r\n  * `getOuterHTML`\r\n  * `querySelectorAll`\r\n  * `querySelector`\r\n  * `getAttributes`\r\n\r\noverlap with classic Selenium in Classic Javascript\r\nand there are few specific ones like:\r\n  * `addCustomHeaders`\r\n  * `getFrameTree`\r\n  * `setGeolocationOverride`\r\n  * `setDownloadBehavior`\r\n\r\nto name a few, and various event listeners\r\n\r\nThis functionality is named in official Selenium Developer Documentation as [BiDirectional functionality](https://www.selenium.dev/documentation/webdriver/bidirectional/)\r\nand [BiDi API](https://www.selenium.dev/documentation/webdriver/bidirectional/bidi_api/)\r\n\r\nThe project also exercised other new Selenium 4 API e.g. [relative nearby locators](https://dzone.com/articles/how-selenium-4-relative-locator-can-change-the-way) whidh did not apear powerful enough yet.\r\n\r\nFor accessing the __Chrome Devtools API__ with Selenium driver __3.x__ see [cdp_webdriver](https://github.com/sergueik/cdp_webdriver) project\r\n\r\n\r\n### Examples\r\n\r\n#### Async Code Execution by XHR Fetch events in the Browser\r\n\r\n![xhr_test_capture.png](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/xhr_test_capture.png)\r\n\r\nThis test is opening Wikipedia page and hovers over few links using \"classic\" Selenium `Actions` class:\r\n```java\r\ndriver.findElement(By.id(\"mw-content-text\")).findElements(By.tagName(\"a\")).stream().forEach( (WebElement element ) -\u003e {\r\n    new Actions(driver).moveToElement(element).build().perform();\r\n  }\r\n}\r\n```\r\nTo emphacise that the lambda operates \"classic\" object,the `WebElement` type was entered explicitly.\r\n\r\nIn the `@Before` -annotated method in the test class, the `Fetch` API is enabled\r\nfor all requests\r\n```\r\n@Before\r\npublic void beforeTest() throws Exception {\r\nchromeDevTools = ((HasDevTools) driver).getDevTools();\r\n\r\nList\u003cRequestPattern\u003e reqPattern = new ArrayList\u003c\u003e();\r\nreqPattern.add(new RequestPattern(Optional.of(\"*\"), Optional.of(ResourceType.XHR), Optional.of(RequestStage.RESPONSE)));\r\nchromeDevTools.send(Fetch.enable(Optional.of(reqPattern), Optional.of(false)));\r\n```\r\n(If necessary one can limit to subset of reuests via match pattern).\r\nThen in the test method callback is set up:\r\n\r\n```java\r\n@Test\r\npublic void test() {\r\n\tchromeDevTools.addListener(Fetch.requestPaused(),\r\n\t\t(RequestPaused event) -\u003e {\r\n      event.getResponseHeaders().get().stream().map((HeaderEntry entry) -\u003e String.format(\"%s: %s\",\r\n              entry.getName(), entry.getValue())).collect(Collectors.toList());\r\n      Fetch.GetResponseBodyResponse response = chromeDevTools.send(Fetch.getResponseBody(event.getRequestId()));\r\n        String body = new String(Base64.decodeBase64(response.getBody().getBytes(\"UTF8\")));\r\n\tSystem.err.println(\"response body:\\n\" + body);\r\n      }\r\n});\r\n// he mouse hover actions to follow\r\n```\r\nThis allows capture every Ajax request response headers,\r\n```java\r\nList\u003cHeaderEntry\u003e headerEntries = event.getResponseHeaders().isPresent() ? event.getResponseHeaders().get() : new ArrayList\u003c\u003e();\r\nList\u003cString\u003e headers = headerEntries.stream().map(entry -\u003e String.format(\"%s: %s\", entry.getName(), entry.getValue())) .collect(Collectors.toList());\r\n```\r\nalong with response status\r\n```java\r\nevent.getResponseStatusCode().get()\r\n```\r\nand body which is usually a base64 encoded JSON with multiple details, processed by browser\r\n![xhr_logged_capture.png](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/xhr_logged_capture.png)\r\n```java\r\nFetch.GetResponseBodyResponse response = chromeDevTools.send(Fetch.getResponseBody(event.getRequestId()));\r\nString body = null;\r\nif (response.getBase64Encoded()) {\r\n\ttry {\r\n\t\tbody = new String( Base64.decodeBase64(response.getBody().getBytes(\"UTF8\")));\r\n\t} catch (UnsupportedEncodingException e) {\r\n\t\tSystem.err.println(\"Exception (ignored): \" + e.toString());\r\n\t}\r\n} else {\r\n\tbody = response.getBody();\r\n}\r\n```\r\nfinally the test continues default processing  of the request:\r\n```java\r\nchromeDevTools.send(Fetch.continueRequest(\r\n\tevent.getRequestId(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty()));\r\n```\r\n\r\n- the arguments to the Java adapter method match the Javascript `Fetch.continueResponse` [parameter definition](https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse):\r\n\r\n```text\r\nrequestId\r\nRequestId\r\nAn id the client received in requestPaused event.\r\nresponseCode\r\ninteger\r\nAn HTTP response code. If absent, original response code will be used.\r\nresponsePhrase\r\nstring\r\nA textual representation of responseCode. If absent, a standard phrase matching responseCode is used.\r\nresponseHeaders\r\narray[ HeaderEntry ]\r\nResponse headers. If absent, original response headers will be used.\r\nbinaryResponseHeaders\r\nstring\r\nAlternative way of specifying response headers as a \\0-separated series of name: value pairs. Prefer the above method unless you need to represent some non-UTF8 values that can't be transmitted over the protocol as text. (Encoded as a base64 string when passed over JSON)\r\n```\r\n#### Access Browser Console Logs\r\nBrowser console logs may accessed asynchronuosly in asimilar fashion:\r\n```java\r\n@Before\r\npublic void beforeTest() throws Exception {\r\n\tchromeDevTools.send(Log.enable());\r\n\tchromeDevTools.addListener(Log.entryAdded(),\r\n\t\t(LogEntry event) -\u003e System.err.println(\r\n\t\t\tString.format( \"time stamp: %s line number: %s url: \\\"%s\\\" text: %s\",\r\n\tformatTimestamp(event.getTimestamp()),\r\n\t(event.getLineNumber().isPresent() ? event.getLineNumber().get() : \"\"),\r\n\t(event.getUrl().isPresent() ? event.getUrl().get() : \"\"),\r\n\tevent.getText())));\r\n}\r\n```\r\nThe properties of the event are taken from `Log entry` object [specification](https://chromedevtools.github.io/devtools-protocol/tot/Log/#event-entryAdded)\r\nOne can also confirm the logging event to have expected properties, e.g. message:\r\n\r\n```java\r\n\r\n@Test\r\npublic void test() {\r\n\tfinal String consoleMessage = \"Lorem ipsum\";\r\n\tchromeDevTools.addListener(Log.entryAdded(),\r\n\t\t(LogEntry event) -\u003e assertThat(event.getText(), containsString(consoleMessage)));\r\n\tif (driver instanceof JavascriptExecutor) {\r\n\t\tJavascriptExecutor executor = JavascriptExecutor.class.cast(driver);\t\t\r\n\t\texecutor.executeScript(\"console.log(arguments[0]);\", consoleMessage);\r\n\t}\r\n}\r\n```\r\n#### Print to PDF\r\n\r\nThis API uses CDP command:\r\n```java\r\npublic void test1() {\r\n\tPrintToPDFResponse response;\r\n\tboolean landscape = false;\r\n\tboolean displayHeaderFooter = false;\r\n\tboolean printBackground = false;\r\n\tPage.PrintToPDFTransferMode transferMode = Page.PrintToPDFTransferMode.RETURNASBASE64;\r\n\tint scale = 1;\r\n\r\n\t// Act\r\n\tresponse = chromeDevTools.send(Page.printToPDF(\r\n\tOptional.of(landscape),\r\n\tOptional.of(displayHeaderFooter),\r\n\tOptional.of(printBackground),\r\n\tOptional.of(scale),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.empty(),\r\n\tOptional.of(transferMode)));\r\n\tassertThat(response, notNullValue());\r\n\tString body = new String(Base64.decodeBase64(response.getData().getBytes(\"UTF8\")));\r\n\tassertThat(body, notNullValue());\r\n\tString magic = body.substring(0, 9);\r\n\tassertThat(magic, containsString(\"%PDF\"));\r\n```\r\nthe browser needs to run headless mode for the call to succeed\r\nthe alternative call signature is\r\n```java\r\nresponse = chromeDevTools.send(new Command\u003cPrintToPDFResponse\u003e(\"Page.printToPDF\", ImmutableMap.of(\"landscape\", landscape), o -\u003e o.read(PrintToPDFResponse.class)));\r\nassertThat(response, notNullValue());\r\n\r\n\r\n```\r\nfor some calls (but not specifically for `Page.printToPDF`) yet anoher alternavie signature via static method exists\r\n\r\n```java\r\nresponse = chromeDevTools.send(new Command\u003cPrintToPDFResponse\u003e(\"Page.printToPDF\", ImmutableMap.of(\"landscape\", landscape), ConverterFunctions.map(\"data\", PrintToPDFResponse.class)));\r\n\r\n```\r\n#### Zoom the Browser window\r\n\r\nin additon to *legacy*-like keyboard zoom, the CDP supports `Page.setDeviceMetricsOverride` [method](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-setDeviceMetricsOverride) and `Emulation.setDeviceMetricsOverride` [method](https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride):\r\n\r\n```java\r\n  @Before\r\n  public void before() throws Exception {\r\n    baseURL = \"https://www.wikipedia.org\";\r\n    driver.get(baseURL);\r\n  }\r\n\r\n  @Test\r\n  public void test1() {\r\n    for (int cnt = 0; cnt != deviceScaleFactors.length; cnt++) {\r\n      double deviceScaleFactor = deviceScaleFactors[cnt];\r\n      screenshotFileName = String.format(\"test1_%03d.jpg\",\r\n          (int) (100 * deviceScaleFactor));\r\n      layoutMetrics = chromeDevTools.send(Page.getLayoutMetrics());\r\n      rect = layoutMetrics.getContentSize();\r\n      width = rect.getWidth().intValue();\r\n      height = rect.getHeight().intValue();\r\n      System.err.println(String.format(\"Content size: %dx%d\", width, height));\r\n      chromeDevTools.send(\r\n        // @formatter:off\r\n        Emulation.setDeviceMetricsOverride(\r\n          rect.getWidth().intValue(),\r\n          rect.getHeight().intValue(),\r\n          deviceScaleFactor,\r\n          false,\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty(),\r\n          Optional.empty()\r\n        )\r\n        // @formatter:on\r\n      );\r\n      String dataString = chromeDevTools.send(\r\n        // @formatter:off\r\n        Page.captureScreenshot(\r\n            Optional.of(Page.CaptureScreenshotFormat.JPEG),\r\n            Optional.of(100),\r\n            Optional.empty(),\r\n            Optional.of(true),\r\n            Optional.of(true)\r\n        )\r\n        // @formatter:off\r\n    );\r\n    chromeDevTools.send(Emulation.clearDeviceMetricsOverride());\r\n\r\n    byte[] image = base64.decode(dataString);\r\n    try {\r\n      BufferedImage o = ImageIO.read(new ByteArrayInputStream(image));\r\n      System.err.println(String.format(\"Screenshot dimensions: %dx%d\",\r\n          o.getWidth(), o.getHeight()));\r\n      assertThat((int) (width * deviceScaleFactor) - o.getWidth(),\r\n          not(greaterThan(2)));\r\n      assertThat((int) (height * deviceScaleFactor) - o.getHeight(),\r\n          not(greaterThan(2)));\r\n    } catch (IOException e) {\r\n      System.err.println(\"Exception loading image (ignored): \" + e.toString());\r\n    }\r\n    try {\r\n      FileOutputStream fileOutputStream = new FileOutputStream(\r\n          screenshotFileName);\r\n      fileOutputStream.write(image);\r\n      fileOutputStream.close();\r\n    } catch (IOException e) {\r\n      System.err.println(\"Exception saving image (ignored): \" + e.toString());\r\n    }\r\n    }\r\n  }\r\n\r\n\r\n@After\r\npublic void clearPage() {\r\n  chromeDevTools.send(CSS.disable());\r\n  try {\r\n    chromeDevTools.send(DOM.disable());\r\n  } catch (DevToolsException e) {\r\n    // DOM agent hasn't been enabled\r\n  }\r\n  driver.get(\"about:blank\");\r\n}\r\n\r\n```\r\nthis test gets gradually magnified out page screen shots:\r\n\r\n![capture-multi-zoom.png](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/capture-multi-zoom.png)\r\n\r\n\r\nalternatively use CDP commands for the same:\r\n```java\r\n  @SuppressWarnings(\"unchecked\")\r\n  @Test\r\n  public void test() {\r\n    // Assert\r\n    params = new HashMap\u003c\u003e();\r\n    for (int cnt = 0; cnt != deviceScaleFactors.length; cnt++) {\r\n      double deviceScaleFactor = deviceScaleFactors[cnt];\r\n      filename = String.format(\"test2_%03d.jpg\",\r\n          (int) (100 * deviceScaleFactor));\r\n\r\n      try {\r\n        command = \"Page.getLayoutMetrics\";\r\n        result = driver.executeCdpCommand(command, new HashMap\u003c\u003e());\r\n        System.err\r\n            .println(\"Page.getLayoutMetrics: \" + result.get(\"contentSize\"));\r\n        rect = (Map\u003cString, Long\u003e) result.get(\"contentSize\");\r\n        height = rect.get(\"height\");\r\n        width = rect.get(\"width\");\r\n        command = \"Emulation.setDeviceMetricsOverride\";\r\n        // Act\r\n        System.err.println(String.format(\"Scaling to %02d%% %s\",\r\n            (int) (100 * deviceScaleFactor), filename));\r\n        params.clear();\r\n        params.put(\"deviceScaleFactor\", deviceScaleFactor);\r\n        params.put(\"width\", width);\r\n        params.put(\"height\", height);\r\n        params.put(\"mobile\", false);\r\n        params.put(\"scale\", 1);\r\n        driver.executeCdpCommand(command, params);\r\n\r\n        Utils.sleep(delay);\r\n        command = \"Page.captureScreenshot\";\r\n        // Act\r\n        result = driver.executeCdpCommand(command,\r\n            new HashMap\u003cString, Object\u003e());\r\n\r\n        command = \"Emulation.clearDeviceMetricsOverride\";\r\n        driver.executeCdpCommand(command, new HashMap\u003cString, Object\u003e());\r\n\r\n        // Assert\r\n        assertThat(result, notNullValue());\r\n        assertThat(result, hasKey(\"data\"));\r\n        dataString = (String) result.get(\"data\");\r\n        assertThat(dataString, notNullValue());\r\n\r\n        byte[] image = base64.decode(dataString);\r\n        BufferedImage o = ImageIO.read(new ByteArrayInputStream(image));\r\n        assertThat(o.getWidth(), greaterThan(0));\r\n        assertThat(o.getHeight(), greaterThan(0));\r\n        FileOutputStream fileOutputStream = new FileOutputStream(filename);\r\n        fileOutputStream.write(image);\r\n        fileOutputStream.close();\r\n      } catch (IOException e) {\r\n        System.err.println(\"Exception saving image (ignored): \" + e.toString());\r\n      } catch (JsonSyntaxException e) {\r\n        System.err.println(\"JSON Syntax exception in \" + command\r\n            + \" (ignored): \" + e.toString());\r\n      } catch (WebDriverException e) {\r\n        // willbe thrown if the required arguments are not provided.\r\n        // TODO: add failing test\r\n        System.err.println(\r\n            \"Web Driver exception in \" + command + \" (ignored): \" + Utils\r\n                .processExceptionMessage(e.getMessage() + \"  \" + e.toString()));\r\n      } catch (Exception e) {\r\n        System.err.println(\"Exception in \" + command + \"  \" + e.toString());\r\n        throw (new RuntimeException(e));\r\n      }\r\n    }\r\n  }\r\n\r\n```\r\n#### Filter URL\r\n![xhr_logged_capture.png](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/filtering-on_capture.jpg)\r\nBandwidth improving filtering of certain mask URLs\r\n```java\r\nchromeDevTools.send(Network.enable(Optional.of(100000000), Optional.empty(), Optional.empty()));\r\nchromeDevTools.send(Network.setBlockedURLs(ImmutableList.of(\"*.css\", \"*.png\", \"*.jpg\", \"*.gif\", \"*favicon.ico\")));\r\ndriver.get(\"http://arngren.net\");\r\n```\r\n\r\none can also log the `*.css`, `*.jpg` `*.png` and  `*.ico` blocking in action:\r\n```java\r\n// verify that\r\nchromeDevTools.addListener(Network.loadingFailed(),\r\n\t(LoadingFailed event) -\u003e {\r\n\t\tResourceType resourceType = event.getType();\r\n\t\tif (resourceType.equals(ResourceType.STYLESHEET)\r\n\t\t\t\t|| resourceType.equals(ResourceType.IMAGE)\r\n\t\t\t\t|| resourceType.equals(ResourceType.OTHER)) {\r\n\t\t\tOptional\u003cBlockedReason\u003e blockedReason = event.getBlockedReason();\r\n\t\t\tassertThat(blockedReason.isPresent(), is(true));\r\n\t\t\tassertThat(blockedReason.get(), is(BlockedReason.INSPECTOR));\r\n\t\t}\r\n\tSystem.err.println(\"Blocked event: \" + event.getType());\r\n});\r\n\r\n\r\n```\r\nfinally one can disable filtering:\r\n```java\r\n// set request interception only for css requests\r\nRequestPattern requestPattern = new RequestPattern(Optional.of(\"*.gif\"), Optional.of(ResourceType.IMAGE), Optional.of(InterceptionStage.HEADERSRECEIVED));\r\nchromeDevTools.send(Network.setRequestInterception(ImmutableList.of(requestPattern)));\r\nchromeDevTools.send(Page.navigate(baseURL, Optional.empty(),Optional.empty(), Optional.empty(), Optional.empty()));\r\n```\r\n![xhr_logged_capture.png](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/filtering-off_capture.jpg)\r\n\r\n\r\n#### Override User Agent\r\n\r\nOne can __call__ cdp protocol to invoke [setUserAgentOverride](https://chromedevtools.github.io/devtools-protocol/tot/Network#method-setUserAgentOverride) method and dynmically modify the `user-agent` header during the test:\r\n\r\n```java\r\n  import org.openqa.selenium.chrome.ChromeDriver;\r\n  import org.openqa.selenium.chromium.ChromiumDriver;\r\n\r\n  ChromiumDriver driver = new ChromeDriver();\r\n  driver.get(\"https://www.whoishostingthis.com/tools/user-agent/\");\r\n  By locator = By.cssSelector(\".user-agent\");\r\n  WebElement element = driver.findElement(locato);\r\n  assertThat(element.getAttribute(\"innerText\"), containsString(\"Mozilla\"));\r\n  Map\u003cString, Object\u003e params = new HashMap\u003cString, Object\u003e();\r\n  params.put(\"userAgent\", \"python 2.7\");\r\n  params.put(\"platform\", \"Windows\");\r\n  driver.executeCdpCommand(\"Network.setUserAgentOverride\", params);\r\n  driver.navigate().refresh();\r\n  sleep(100);\r\n\r\n  element = driver.findElement(locator);\r\n  assertThat(element.isDisplayed(), is(true));\r\n  assertThat(element.getAttribute(\"innerText\"), is(\"python 2.7\"));\r\n\r\n```\r\ndemonstrates that the user-agent is indeed changing\r\n#### Cookies\r\nThe example shows alternative API to collect the cookies available to page Javascript\r\n```java\r\n  Map\u003cString, Object\u003e result = driver.executeCdpCommand(\"Page.getCookies\", new HashMap\u003cString, Object\u003e());\r\n  ArrayList\u003cMap\u003cString, Object\u003e\u003e cookies = (ArrayList\u003cMap\u003cString, Object\u003e\u003e) result.get(\"cookies\");\r\n  cookies.stream().limit(100).map(o -\u003e o.keySet()).forEach(System.err::println);\r\n```\r\n#### Capture Screenshot\r\n```java\r\n  String result = driver.executeCdpCommand(\"Page.captureScreenshot\", new HashMap\u003c\u003e());\r\n  String data = (String) result.get(\"data\");\r\n  byte[] image = new (Base64()).decode(data);\r\n  assertThat(ImageIO.read(new ByteArrayInputStream(image)).getWidth(), greaterThan(0));\r\n  (new FileOutputStream(\"temp.png\")).write(image);\r\n```\r\n#### Capture Element Screenshot\r\nimplements the clipping to viewport functioality\r\n```java\r\ncommand = \"Page.captureScreenshot\";\r\nparams = new HashMap\u003cString, Object\u003e();\r\nMap\u003cString, Object\u003e viewport = new HashMap\u003c\u003e();\r\nSystem.err.println(\"Specified viewport: \" + String\r\n    .format(\"x=%d, y=%d, width=%d, height=%d\", x, y, width, height));\r\nviewport.put(\"x\", (double) x);\r\nviewport.put(\"y\", (double) y);\r\nviewport.put(\"width\", (double) width);\r\nviewport.put(\"height\", (double) height);\r\nviewport.put(\"scale\", scale);\r\nparams.put(\"clip\", viewport);\r\nresult = driver.executeCdpCommand(command, params);\r\ndataString = (String) result.get(\"data\");\r\nassertThat(dataString, notNullValue());\r\nBase64 base64 = new Base64();\r\nbyte[] image = base64.decode(dataString);\r\nString screenshotFileName = String.format(\"card%02d.png\", cnt);\r\nFileOutputStream fileOutputStream = new FileOutputStream( screenshotFileName);\r\nfileOutputStream.write(image);\r\nfileOutputStream.close();\r\n```\r\n\r\nNote: some CDP API notably `Page.printToPDF` are not curently implemented:\r\n```sh\r\nunhandled inspector error: {\"code\":-32000,\"message\":\"PrintToPDF is not implemented\"}(..)\r\n```\r\n### Custom Headers\r\n\r\nThis can be done both at the wrapper methods\r\n```java\r\n\r\n    // enable Network\r\n    chromeDevTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));\r\n    headers = new HashMap\u003c\u003e();\r\n    headers.put(\"customHeaderName\", \"customHeaderValue\");\r\n    Headers headersData = new Headers(headers);\r\n    chromeDevTools.send(Network.setExtraHTTPHeaders(headersData));\r\n```\r\nThe validation can be done through hooking assert and log message to the event:\r\n```java\r\n    // add event listener to log that requests are sending with the custom header\r\n    chromeDevTools.addListener(Network.requestWillBeSent(),\r\n        o -\u003e Assert.assertEquals(o.getRequest().getHeaders().get(\"customHeaderName\"), \"customHeaderValue\"));\r\n    chromeDevTools.addListener(Network.requestWillBeSent(), o -\u003e System.err.println(\r\n        \"addCustomHeaders Listener invoked with \" + o.getRequest().getHeaders().get(\"customHeaderName\")));\r\n```\r\nand low level \"commands\":\r\n```java\r\n    String command = \"Network.enable\";\r\n    params = new HashMap\u003c\u003e();\r\n    params.put(\"maxTotalBufferSize\", 0);\r\n    params.put(\"maxPostDataSize\", 0);\r\n    params.put(\"maxPostDataSize\", 0);\r\n    result = driver.executeCdpCommand(command, params);\r\n    command = \"Network.setExtraHTTPHeaders\";\r\n\r\n    params = new HashMap\u003c\u003e();\r\n    Map\u003cString, String\u003e headers = new HashMap\u003c\u003e();\r\n    headers.put(\"customHeaderName\", this.getClass().getName() + \" addCustomHeadersTest\");\r\n    params.put(\"headers\", headers);\r\n    result = driver.executeCdpCommand(command, params);\r\n```\r\n\r\nTo test one can e.g. fire a tomcat server with request header logging and\r\nsend the `GET` request\r\n```java\r\ndriver.get(\"http://127.0.0.1:8080/demo/Demo\");\r\n```\r\nThe actual validation will be done through console logs inspection of the server\r\n\r\n#### DOM Node Navigation\r\n\r\n\r\nThe following somewhat long test exercises steps one has to perform with CDP to get a specific DOM Node focused and act upon:\r\n\r\nIt appears every node search starts with getting the [document](https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument):\r\n\r\n```java\r\n\t@SuppressWarnings(\"unchecked\")\r\n\t@Test\r\n\tpublic void getDocumentTest() {\r\n\t\t// Arrange\r\n\t\tdriver.get(\"https://www.google.com\");\r\n\t\tString command = \"DOM.getDocument\";\r\n\t\ttry {\r\n\t\t\t// Act\r\n\t\t\tresult = driver.executeCdpCommand(command, new HashMap\u003c\u003e());\r\n\t\t\t// Assert\r\n\t\t\tassertThat(result, hasKey(\"root\"));\r\n\t\t\tMap\u003cString, Object\u003e data =  (Map\u003cString, Object\u003e) result.get(\"root\");\r\n\t\t\tassertThat(data, hasKey(\"nodeId\"));\r\n\t\t\tassertTrue(Long.parseLong(data.get(\"nodeId\").toString()) != 0);\r\n\t\t\terr.println(\"Command \" + command + \" return node: \"\r\n\t\t\t\t\t+ new Gson().toJson(data, Map.class));\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\t}\r\n\r\n```\r\n\r\nThis test logs:\r\n```js\r\nCommand DOM.getDocument return node:\r\n{\r\n  \"backendNodeId\": 1,\r\n  \"baseURL\": \"https://www.google.com/\",\r\n  \"childNodeCount\": 2,\r\n  \"children\": [\r\n    {\r\n      \"backendNodeId\": 2,\r\n      \"localName\": \"\",\r\n      \"nodeId\": 10,\r\n      \"nodeName\": \"html\",\r\n      \"nodeType\": 10,\r\n      \"nodeValue\": \"\",\r\n      \"parentId\": 9,\r\n      \"publicId\": \"\",\r\n      \"systemId\": \"\"\r\n    },\r\n    {\r\n      \"attributes\": [\r\n        \"itemscope\",\r\n        \"\",\r\n        \"itemtype\",\r\n        \"http://schema.org/WebPage\",\r\n        \"lang\",\r\n        \"en\"\r\n      ],\r\n      \"backendNodeId\": 3,\r\n      \"childNodeCount\": 2,\r\n      \"children\": [\r\n        {\r\n          \"attributes\": [],\r\n          \"backendNodeId\": 21,\r\n          \"childNodeCount\": 12,\r\n          \"localName\": \"head\",\r\n          \"nodeId\": 12,\r\n          \"nodeName\": \"HEAD\",\r\n          \"nodeType\": 1,\r\n          \"nodeValue\": \"\",\r\n          \"parentId\": 11\r\n        },\r\n        {\r\n          \"attributes\": [\r\n            \"jsmodel\",\r\n            \" \",\r\n            \"class\",\r\n            \"hp vasq\",\r\n            \"id\",\r\n            \"gsr\"\r\n          ],\r\n          \"backendNodeId\": 22,\r\n          \"childNodeCount\": 8,\r\n          \"localName\": \"body\",\r\n          \"nodeId\": 13,\r\n          \"nodeName\": \"BODY\",\r\n          \"nodeType\": 1,\r\n          \"nodeValue\": \"\",\r\n          \"parentId\": 11\r\n        }\r\n      ],\r\n      \"frameId\": \"C3CE739B971DD10AFECA84F6C1554308\",\r\n      \"localName\": \"html\",\r\n      \"nodeId\": 11,\r\n      \"nodeName\": \"HTML\",\r\n      \"nodeType\": 1,\r\n      \"nodeValue\": \"\",\r\n      \"parentId\": 9\r\n    }\r\n  ],\r\n  \"documentURL\": \"https://www.google.com/\",\r\n  \"localName\": \"\",\r\n  \"nodeId\": 9,\r\n  \"nodeName\": \"#document\",\r\n  \"nodeType\": 9,\r\n  \"nodeValue\": \"\",\r\n  \"xmlVersion\": \"\"\r\n}\r\n```\r\n\r\nnow one can\r\n\r\n```java\r\n\t\tcommand = \"DOM.querySelector\";\r\n\t\tparams.clear();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\t\tparams.put(\"selector\", \"img#hplogo\");\r\n\r\n\t\ttry {\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t\tassertThat(result, hasKey(\"nodeId\"));\r\n\t\t\tnodeId = (Long) result.get(\"nodeId\");\r\n\t\t\tassertTrue(nodeId != 0);\r\n\t\t\terr.println(\"Command \" + command + \" returned  nodeId: \" + nodeId);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\t\tcommand = \"DOM.getOuterHTML\";\r\n\t\tparams.clear();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\t\t\r\n\t\ttry {\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t\tassertThat(result, notNullValue());\r\n\t\t\tassertThat(result, hasKey(\"outerHTML\"));\r\n\t\t\tString dataString = (String) result.get(\"outerHTML\");\r\n\t\t\tassertThat(dataString, notNullValue());\r\n\t\t\terr.println(\"Command \" + command + \" return outerHTML: \" + dataString);\r\n\t\t} catch (Exception e) {\r\n\t\t\terr.println(\"Exception in \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\t}\r\n\r\n```\r\n\r\nThis will log:\r\n```shell\r\nCommand DOM.querySelector returned  nodeId: 162\r\n```\r\n```html\r\nCommand DOM.getOuterHTML return outerHTML:\r\n\u003cimg alt=\"Google\" height=\"92\" id=\"hplogo\"  src=\"/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png\"  style=\"padding-top:109px\" width=\"272\" onload=\"typeof google==='object'\u0026amp;\u0026amp;google.aft\u0026amp;\u0026amp;google.aft(this)\" data-iml=\"1576602836994\" data-atf=\"1\"\u003e\r\n```\r\n\r\ncollapsing multiple command calls together will lead to somewhat bloated test method\r\n```java\r\n\t@Test\r\n\tpublic void multiCommandTest() {\r\n\t\t// Arrange\r\n\t\tbaseURL = \"https://www.google.com\";\r\n\t\tdriver.get(baseURL);\r\n\t\tString command = \"DOM.getDocument\";\r\n\t\ttry {\r\n\t\t\t// Act\r\n\t\t\tresult = driver.executeCdpCommand(command, new HashMap\u003c\u003e());\r\n\t\t\t// Assert\r\n\t\t\tassertThat(result, hasKey(\"root\"));\r\n\t\t\t@SuppressWarnings(\"unchecked\")\r\n\t\t\tMap\u003cString, Object\u003e node = (Map\u003cString, Object\u003e) result.get(\"root\");\r\n\t\t\tassertThat(node, hasKey(\"nodeId\"));\r\n\t\t\tnodeId = Long.parseLong(node.get(\"nodeId\").toString());\r\n\t\t\tassertTrue(nodeId != 0);\r\n\t\t\terr.println(\"Command \" + command + \" returned nodeId: \" + nodeId);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\t\tcommand = \"DOM.describeNode\";\r\n\t\tparams = new HashMap\u003c\u003e();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\t\tparams.put(\"depth\", 1);\r\n\t\ttry {\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t\t// Assert\r\n\t\t\tassertThat(result, hasKey(\"node\"));\r\n\t\t\t@SuppressWarnings(\"unchecked\")\r\n\t\t\tMap\u003cString, Object\u003e data = (Map\u003cString, Object\u003e) result.get(\"node\");\r\n\t\t\tfor (String field : Arrays.asList(\r\n\t\t\t\t\tnew String[] { \"nodeType\", \"nodeName\", \"localName\", \"nodeValue\" })) {\r\n\t\t\t\tassertThat(data, hasKey(field));\r\n\t\t\t}\r\n\t\t\tSystem.err.println(\"Command \" + command + \" returned node: \" + data);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\r\n\t\tcommand = \"DOM.querySelector\";\r\n\t\tparams = new HashMap\u003c\u003e();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\t\t// params.put(\"selector\", \"img#hplogo\");\r\n\t\tparams.put(\"selector\", \"input[name='q']\");\r\n\r\n\t\ttry {\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t\t// depth, 1\r\n\t\t\t// Assert\r\n\t\t\tassertThat(result, hasKey(\"nodeId\"));\r\n\t\t\t// @SuppressWarnings(\"unchecked\")\r\n\t\t\tnodeId = Long.parseLong(result.get(\"nodeId\").toString());\r\n\t\t\tassertTrue(nodeId != 0);\r\n\t\t\terr.println(\"Command \" + command + \" returned  nodeId: \" + nodeId);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\r\n\t\tcommand = \"DOM.resolveNode\";\r\n\t\tparams = new HashMap\u003c\u003e();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\r\n\t\ttry {\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t\t// depth, 1\r\n\t\t\t// Assert\r\n\t\t\tassertThat(result, hasKey(\"object\"));\r\n\t\t\t// object\r\n\t\t\t@SuppressWarnings(\"unchecked\")\r\n\t\t\tMap\u003cString, Object\u003e data = (Map\u003cString, Object\u003e) result.get(\"object\");\r\n\t\t\tfor (String field : Arrays.asList(\r\n\t\t\t\t\tnew String[] { \"type\", \"subtype\", \"className\", \"objectId\" })) {\r\n\t\t\t\tassertThat(data, hasKey(field));\r\n\t\t\t}\r\n\t\t\tString objectId = (String) data.get(\"objectId\");\r\n\t\t\tassertThat(objectId, notNullValue());\r\n\t\t\tSystem.err\r\n\t\t\t\t\t.println(\"Command \" + command + \" returned objectId: \" + objectId);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\r\n\t\tcommand = \"DOM.something not defined\";\r\n\t\ttry {\r\n\t\t\t// Act\r\n\t\t\tresult = driver.executeCdpCommand(command, new HashMap\u003c\u003e());\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t\t// wasn't found\r\n\t\t}\r\n\t\t// DOM.removeNode\r\n\t\tcommand = \"DOM.focus\";\r\n\t\tparams = new HashMap\u003c\u003e();\r\n\t\tparams.put(\"nodeId\", nodeId);\r\n\t\ttry {\r\n\t\t\t// Act\r\n\t\t\tresult = driver.executeCdpCommand(command, params);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t\t// : unknown error: unhandled inspector error:\r\n\t\t\t// {\"code\":-32000,\"message\":\"Element is not focusable\"}\r\n\t\t}\r\n\t\tcommand = \"DOM.highlightNode\";\r\n\t\ttry {\r\n\t\t\t// Act\r\n\t\t\tresult = driver.executeCdpCommand(command, new HashMap\u003c\u003e());\r\n\t\t\tUtils.sleep(10000);\r\n\t\t} catch (org.openqa.selenium.WebDriverException e) {\r\n\t\t\terr.println(\r\n\t\t\t\t\t\"Exception in command \" + command + \" (ignored): \" + e.toString());\r\n\t\t}\r\n\t\t// TODO: command = \"Runtime.callFunctionOn\";\r\n\t}\r\n\r\n\r\n```\r\n### Relative Locators\r\n\r\n\r\n### Selenum release dependency\r\n\r\nThe [selenium-chromium-driver](https://jcenter.bintray.com/org/seleniumhq/selenium/selenium-chromium-driver/)\r\nthat is only available for Selenum release 4 is the critical dependency jar of this project.\r\nThe\r\n[selenium-chromium-driver](https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-chromium-driver) repository search page.\r\n\r\nThe [devtools](https://github.com/SeleniumHQ/selenium/tree/master/java/client/src/org/openqa/selenium/devtools) and [chromium](https://github.com/SeleniumHQ/selenium/tree/master/java/client/src/org/openqa/selenium/chromium) subprojects of selenium client of official [seleniumhq/selenium](https://github.com/SeleniumHQ/selenium) project have no dependencies and can be cloned and built locally allowing one to use CDP API with Selenium __3.x__ e.g. Selenium __3.13.0__. This is currently attempted this way in this project. Moving away form default __4.0.0.alpha__ maven profiles is a work in progress.\r\n\r\n### Breaking Changes in Selenium 4.0.0-alpha-7\r\n\r\nWith Selenium driver release __4.0.0-alpha-7__  just to make the project compile changes imported package names need to change all\r\n`org.openqa.selenium.devtools.browser` references with `org.openqa.selenium.devtools.v87.browser` and similar to other packages inside `org.openqa.selenium.devtools` were requied. Without this multiple compile errors like:\r\n```sh\r\npackage org.openqa.selenium.devtools.browser does not exist\r\n```\r\nare observed\r\n\r\nAlso the following run time errors indicate that `selenium-api-4.0.0-alpha-7.jar` was build on JDK 11 and is notloadable in JDK 8.\r\n\r\nThis manifests through the runtime exception\r\n```sh\r\njava.lang.NoClassDefFoundError: Could not initialize class org.openqa.selenium.net.PortProber\r\n  at org.openqa.selenium.remote.service.DriverService$Builder.build(DriverService.java:401)\r\n  at org.openqa.selenium.chrome.ChromeDriverService.createServiceWithConfig(ChromeDriverService.java:133)\r\n```\r\nthe usual classpath scan reveals the jar containing the class in question, to be actually present in classpath\r\n```sh\r\nfind ~/.m2/repository/ -iname 'selenium*jar' |xargs -IX sh -c \"echo X; jar tvf X\" | tee a\r\n```\r\nand method signature exception\r\n```sh\r\njava.lang.NoSuchMethodError: java.io.FileReader.\u003cinit\u003e(Ljava/io/File;Ljava/nio/charset/Charset;)V\r\n  at org.openqa.selenium.net.LinuxEphemeralPortRangeDetector.getInstance(LinuxEphemeralPortRangeDetector.java:36)\r\n  at org.openqa.selenium.net.PortProber.\u003cclinit\u003e(PortProber.java:42)\r\n```\r\nthe method the exception is complainign was [added](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/FileReader.html#%3Cinit%3E(java.io.File,java.nio.charset.Charset)) in Java 11\r\n\r\n### Note\r\n\r\n  * To get Google Chrome updates past version 108,  one needs Windows 10 or later. Some development environment computers are using Windows 8.1\r\n\r\n### Note\r\n\r\n* When Chromium browser installed via snapd on Ubuntu __20.04__, all tests are failing with\r\n\r\n```text\r\norg.openqa.selenium.SessionNotCreatedException: Could not start a new session. Response code 500. Message: unknown error: DevToolsActivePort file doesn't exist\r\nHost info: host: 'lenovoy40-1', ip: '127.0.1.1'\r\nBuild info: version: '4.10.0', revision: 'c14d967899'\r\nSystem info: os.name: 'Linux', os.arch: 'amd64', os.version: '5.4.0-150-generic', java.version: '1.8.0_161'\r\nDriver info: org.openqa.selenium.chrome.ChromeDriver\r\nCommand: [null, newSession {capabilities=[Capabilities {browserName: chrome, goog:chromeOptions: {args: [--remote-allow-origins=*, --allow-insecure-localhost, --allow-running-insecure-co..., --browser.download.folderLi..., --browser.helperApps.neverA..., --disable-blink-features=Au..., --disable-default-app, --disable-dev-shm-usage, --disable-extensions, --disable-gpu, --disable-infobars, --disable-in-process-stack-..., --disable-logging, --disable-notifications, --disable-popup-blocking, --disable-save-password-bubble, --disable-translate, --disable-web-security, --enable-local-file-accesses, --ignore-certificate-errors, --ignore-certificate-errors, --ignore-ssl-errors=true, --log-level=3, --no-proxy-server, --no-sandbox, --output=/dev/null, --ssl-protocol=any, --user-agent=Mozilla/5.0 (W...], extensions: []}}]}]\r\n\tat org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:140)\r\n\tat org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:96)\r\n\tat org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:68)\r\n\tat org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:163)\r\n\tat org.openqa.selenium.remote.service.DriverCommandExecutor.invokeExecute(DriverCommandExecutor.java:196)\r\n\tat org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:171)\r\n\tat org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:531)\r\n\tat org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:227)\r\n\tat org.openqa.selenium.remote.RemoteWebDriver.\u003cinit\u003e(RemoteWebDriver.java:154)\r\n\tat org.openqa.selenium.chromium.ChromiumDriver.\u003cinit\u003e(ChromiumDriver.java:107)\r\n\tat org.openqa.selenium.chrome.ChromeDriver.\u003cinit\u003e(ChromeDriver.java:87)\r\n\tat org.openqa.selenium.chrome.ChromeDriver.\u003cinit\u003e(ChromeDriver.java:82)\r\n\tat org.openqa.selenium.chrome.ChromeDriver.\u003cinit\u003e(ChromeDriver.java:71)\r\n\tat com.github.sergueik.selenium.BaseCdpTest.beforeClass(BaseCdpTest.java:124)\r\n\r\n```\r\n* when Chromium browser installed [via apt](https://askubuntu.com/questions/1204571/how-to-install-chromium-without-snap), from\r\n```sh\r\nsudo add-apt-repository ppa:system76/pop\r\nsudo apt update\r\nsudo apt install -y -q chromium\r\n```\r\nthe tests would work but the browser is quite old version __83__.\r\n\r\nThe latest available version of chromium-broser on `http://archive.ubuntu.com/ubuntu/pool/universe/c/chromium-browser/` is __112__\r\n\r\nNOTE: will have to download a few packages to be able to install `chromium-browser`:\r\n\r\n* `chromium-browser_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb`\r\n* `chromium-browser-l10n_112.0.5615.49-0ubuntu0.18.04.1_all.deb`\r\n* `chromium-codecs-ffmpeg_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb`\r\n* `chromium-codecs-ffmpeg-extra_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb`\r\n\r\nand install them in specific order:\r\n```sh\r\ndpkg -i chromium-codecs-ffmpeg_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb\r\ndpkg -i chromium-codecs-ffmpeg-extra_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb\r\ndpkg -i chromium-browser_112.0.5615.49-0ubuntu0.18.04.1_amd64.deb\r\n```\r\n\r\nSince the version mismatch the test log will contain plenty of\r\n```text\r\nMatch\r\nWARNING: Unable to find CDP implementation matching 112\r\nJun 15, 2023 9:56:53 AM org.openqa.selenium.chromium.ChromiumDriver lambda$new$4\r\nWARNING: Unable to find version of CDP to use for . You may need to include a dependency on a specific version of the CDP using something similar to `org.seleniumhq.selenium:selenium-devtools-v86:4.10.0` where the version (\"v86\") matches the version of the chromium-based browser you're using and the version number of the artifact is the same as Selenium's.\r\n\r\n```\r\n\r\n* when Chrome latest stable deb package is downloaded and chrome is installed via `dpkg`\r\n\r\n```sh\r\ncd ~/Downloads\r\nwget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb\r\nsudo apt install -y -q ./google-chrome-stable_current_amd64.deb\r\n```\r\na longer version\r\n\r\n```sh\r\ncd ~/Downloads\r\nwget -nv \"https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb\"\r\nsudo apt-get install -qqy libxss1 libappindicator1 libindicator7\r\nsudo dpkg -i google-chrome-stable_current_amd64.deb\r\nrm google-chrome-stable_current_amd64.deb\r\nsudo apt-get install -qqy -f google-chrome-stable\r\n```\r\n```sh\r\ndpkg -l google-chrome-stable\r\n```\r\n```text\r\nDesired=Unknown/Install/Remove/Purge/Hold\r\n| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend\r\n|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)\r\n||/ Name           Version      Architecture Description\r\n+++-==============-============-============-=================================\r\nii  google-chrome- 114.0.5735.1 amd64        The web browser from Google\r\n```\r\nDownload latest Chromedriver\r\n```sh\r\nCHROMEDRIVER_VERSION=$(curl -s \"http://chromedriver.storage.googleapis.com/LATEST_RELEASE\")\r\nPACKAGE_ARCHIVE='chromedriver_linux64.zip'\r\nPLATFORM=linux64\r\nURL=\"http://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_${PLATFORM}.zip\"\r\nwget -O $PACKAGE_ARCHIVE -nv $URL\r\nunzip -o $PACKAGE_ARCHIVE\r\n```\r\nNOTE: the latest version of Chrome Driver that can be downloaded this way is `114.0.5735.90`\r\n\r\n\r\nthe tests work\r\n\r\nNOTE: is is better to use\r\n\r\n```sh\r\ndpkg -i ./google-chrome-stable_current_amd64.deb\r\n```\r\nthe `apt` has the warning:\r\n```text\r\nW: Repository is broken: google-chrome-stable:amd64 (= 114.0.5735.133-1) has no Size information\r\n```\r\n### File Download Browser Behavior\r\n\r\none may configure\r\n``\r\nand subscribe to events `Browser.downloadWillBegin` and `Browser.downloadProgress` via CDP:\r\n\r\n```java\r\nchromeDevTools.addListener(Browser.downloadWillBegin(),\r\n\t(DownloadWillBegin o) -\u003e {\r\n\t\tSystem.err.println(\"in Browser.downloadWillBegin listener. url: \" + o.getUrl() + \"\\tfilename: \" + o.getSuggestedFilename());\r\n});\r\nList\u003cDownloadProgress.State\u003e states = new ArrayList\u003c\u003e();\r\nchromeDevTools.addListener(Browser.downloadProgress(),\r\n\t(DownloadProgress o) -\u003e {\r\n\t\tDownloadProgress.State state = o.getState();\r\n\t\tSystem.err.println(\"in Browser.downloadProgress listener. state: \" + state.toString());\r\n\t\tstates.add(state);\r\n});\r\n\r\n```\r\n```text\r\nin Browser.downloadWillBegin listener. url: https://scholar.harvard.edu/files/torman_personal/files/samplepptx.pptx     filename: samplepptx.pptx\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: inProgress\r\nin Browser.downloadProgress listener. state: completed\r\nin Browser.downloadProgress listener. state: completed\r\nInspecting downloaded filename: f5a83cbd-97fb-451e-8651-618f63c1ec59\r\nVerified downloaded file: f5a83cbd-97fb-451e-8651-618f63c1ec59 in /tmp\r\n\r\n```\r\n### Note\r\n\r\nStarting with version __115__ the Chrome browser and ChromeDriver\r\ninformation is located on\r\n__Chrome for Testing availability__\r\n[dashboard page](https://googlechromelabs.github.io/chrome-for-testing/).\r\nA broader listing of Chrome versions can be found in `https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`:\r\n```sh\r\n\r\ncurl -k -O https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json\r\n```\r\n```sh\r\n\r\njq '.versions[] | select(.version | contains( \"114.\")) ' known-good-versions-with-downloads.json  | jq '.downloads[]|.[]|select(.platform |contains(\"linux64\"))'\r\n```\r\n```JSON\r\n{\r\n  \"platform\": \"linux64\",\r\n  \"url\": \"https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/114.0.5696.0/linux64/chrome-linux64.zip\"\r\n}\r\n```\r\n - extract the `chrome` and `chromedriver` links via `jq` (unfinished for chromedriver, save the full JSON locally and finish the query)\r\n\r\nThe instructions of version lookup are provided on [version selection hint page](https://chromedriver.chromium.org/downloads/version-selection). Prior to that the `chromedriver` download links were posted on Chromedriver Downloads [page](https://chromedriver.chromium.org/downloads)\r\n\r\n* With Selenium version __4.14.0__ onwards one has to build it on JDK __11___ or later, even if targeting Java __1.8__ in `pom.xml`  - `* Require Java 11 (#12843)` is noted in the [Selenium Changelog](https://github.com/SeleniumHQ/selenium/commit/a0e04e15f17ed3f12373f61c363a296d4e06a976#diff-44f582ea3c01561650edeea2771d241bf19ceb93eb8f96bc3d199bcd7ca30d3e).\r\n\r\nThe attempt to build with JDK __1.8__ fails with\r\n\r\n```text\r\n[ERROR] bad class file: .m2\\repository\\org\\seleniumhq\\selenium\\selenium-api\\4.14.0\\selenium-api-4.14.0.jar\r\n[ERROR] class file has wrong version 55.0, should be 52.0\r\n```\r\n\r\n### Note\r\n\r\nif seeing the version mismatch error in every test:\r\n```sh\r\nmvn test\r\n```\r\n```text\r\norg.openqa.selenium.SessionNotCreatedException: Could not start a new session. Response code 500. Message: session not created: This version of ChromeDriver only supports Chrome version 122\r\nCurrent browser version is 121.0.6167.85 with binary path /opt/google/chrome/chrome\r\n```\r\n\r\n\r\nmake sure to have the google repository  added:\r\n\r\n\r\n\r\n```sh\r\necho \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" | sudo tee /etc/apt/sources.list.d/google-chrome.list\r\n```\r\n\r\nand the error is cleared:\r\n```text\r\napt-get install google-chrome-stable\r\nReading package lists... Done\r\nBuilding dependency tree\r\nReading state information... Done\r\nPackage google-chrome-stable is not available, but is referred to by another package.\r\nThis may mean that the package is missing, has been obsoleted, or\r\nis only available from another source\r\n\r\nE: Package 'google-chrome-stable' has no installation candidate\r\n```\r\n```text\r\ndpkg-reconfigure google-chrome-stable\r\n/usr/sbin/dpkg-reconfigure: google-chrome-stable is broken or not fully installed\r\n\r\n```\r\nthe install the chrome:\r\n```sh\r\napt-get install google-chrome-stable\r\n```\r\nand confirm the test to pass\r\n\r\n### Debugging File Upload\r\n\r\nA textbook [File Upload Form](https://cgi-lib.berkeley.edu/ex/fup.html) looks like below\r\n\r\n![file upload page](https://github.com/sergueik/selenium_cdp/blob/master/screenshots/capture-file-upload-form.png)\r\n\r\n```html\r\n\u003chtml\u003e\r\n\u003chead\u003e\r\n\u003ctitle\u003eFile Upload Test\u003c/title\u003e\r\n\u003c/head\u003e\r\n\r\n\u003cbody\u003e\r\n\u003ch1\u003eFile Upload Test\u003c/h1\u003e\r\n\u003cform enctype = \"multipart/form-data\" action=\"upload endpoint url\" method=\"POST\"\u003e\r\nupload file path: \u003cinput name=\"upload file path\" type=\"file\"\u003e\r\n\u003cinput type=\"submit\" value=\"send file\"\u003e\r\n\u003c/form\u003e\r\n\u003c/body\u003e\r\n```\r\n\r\nThe requirements for plain browser-driven file upload HTML page are:\r\n\r\n  * form must specify the `POST` method\r\n  * form must specify an enctype of `multipart/form-data`\r\n  * form must contain an `\u003cinput type=\"file\"\u003e` element\r\n\r\nTo examine the upload request genetated by the browser, subscribe to `` [event](https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent):\r\n```java\r\nMap\u003cString, Map\u003cString, Object\u003e\u003e requests = new HashMap\u003c\u003e();\r\nchromeDevTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));\r\nchromeDevTools.addListener(Network.requestWillBeSent(),\r\n  (RequestWillBeSent event) -\u003e\r\n    requests.put(event.getRequest().getUrl(), event.getRequest().getHeaders().toJson())\r\n);\r\n```\r\nand perform the upload:\r\n\r\n```java\r\nwait = new WebDriverWait(driver, Duration.ofSeconds(flexibleWait));\r\nwait.pollingEvery(Duration.ofMillis(pollingInterval));\r\nactions = new Actions(driver);\r\nurl = \"https://ps.uci.edu/~franklin/doc/file_upload.html\";\r\ndriver.get(url);\r\nelement = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(\"input[name='userfile']\")));\r\n\r\nassertThat(element.isDisplayed(), is(true));\r\nUtils.highlight(element);\r\nelement.sendKeys(dummy.getAbsolutePath());\r\n\r\nelement = driver.findElement(By.tagName(\"form\"));\r\nassertThat(element, notNullValue());\r\nassertThat(element.getAttribute(\"action\"), notNullValue());\r\nurl2 = element.getAttribute(\"action\");\r\n\r\nelement = driver.findElement(By.cssSelector(\"input[type='submit']\"));\r\nassertThat(element, notNullValue());\r\nUtils.highlight(element);\r\nrequests.clear();\r\nelement.submit();\r\ntry {\r\n  Thread.sleep(1000);\r\n} catch (InterruptedException e) {\r\n  e.printStackTrace();\r\n}\r\n\r\nSystem.err.println(\"Captured: \");\r\nrequests.keySet().stream().forEach(System.err::println);\r\n\r\n```\r\n\r\nthis will print:\r\n```text\r\ndata:image/png;base64,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\r\ndata:image/png;base64,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\r\ndata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAABlBMVEUAAABTU1OoaSf/AAAAAXRSTlMAQObYZgAAAENJREFUeF7tzbEJACEQRNGBLeAasBCza2lLEGx0CxFGG9hBMDDxRy/72O9FMnIFapGylsu1fgoBdkXfUHLrQgdfrlJN1BdYBjQQm3UAAAAASUVORK5CYII=\r\nhttps://ps.uci.edu/favicon.ico\r\nhttps://www.oac.uci.edu/indiv/franklin/cgi-bin/values\r\n```\r\nthe following confirms there is more than one data chunk:\r\n```java\r\nPattern pattern = Pattern.compile(\"data:image/png;base64\");\r\nSystem.err.println(\"Pattern:\\n\" + pattern.toString());\r\ncnt = 0;\r\nfor (String x : requests.keySet()) {\r\n  Matcher matcher = pattern.matcher(x);\r\n  if (matcher.find()) {\r\n    cnt++;\r\n  }\r\n}\r\nassertThat(cnt, greaterThan(1));\r\n\r\n```\r\nthe following prints the headers sent to file upload endpoint:\r\n```java\r\nSystem.err.println(\"Headers: \" + requests.get(url2).toString());\r\n```\r\n```text\r\nHeaders: {\r\nContent-Type=multipart/form-data;\r\nboundary=----WebKitFormBoundaryhsNWaugzUXymNUzV,\r\nOrigin=https://ps.uci.edu,\r\nReferer=https://ps.uci.edu/,\r\nUpgrade-Insecure-Requests=1,\r\nUser-Agent=Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20120101 Firefox/33.0,\r\nsec-ch-ua=\"Chromium\";v=\"124\",\r\n\"Google Chrome\";v=\"124\",\r\n\"Not-A.Brand\";v=\"99\",\r\nsec-ch-ua-mobile=?0,\r\nsec-ch-ua-platform=\"Linux\"\r\n}\r\n\r\n```\r\n### See Also\r\n\r\n  * [chrome devtools](https://github.com/ChromeDevTools/awesome-chrome-devtools) project\r\n  * [puppeteer online](https://try-puppeteer.appspot.com/)\r\n  * [GoogleChrome/puppeteer](https://github.com/GoogleChrome/puppeteer)\r\n  * [ChromeDevTools/debugger-protocol-viewer](https://github.com/ChromeDevTools/debugger-protocol-viewer)\r\n  * [standalond java cdp client](https://github.com/webfolderio/cdp4j) (commecial)\r\n  * [cdp4j/javadoc](https://webfolder.io/cdp4j/javadoc/index.html)\r\n  * headless chrome devtools based testing [lecture](https://habr.com/ru/company/yandex/blog/353018/) and [video](https://youtu.be/jUycQRFoOww)(in Russan)\r\n  * [HTML to PDF conversion with Chromium devtools and Selenium Python client](https://habr.com/ru/post/459112/) (in Russan)\r\n  * [PDF configuration](https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#pagepdfoptions) with [Puppeteer](https://github.com/GoogleChrome/puppeteer)\r\n  * Selenium 4 [Relative Locator](https://angiejones.tech/selenium-4-relative-locators/) DOM traversal DSL.\r\n  * Selenium 4 [Relatve locator]( https://www.swtestacademy.com/selenium-relative-locators/) examples\r\n  * alternative java websocket client [HubSpot/ChromeDevToolsClient](https://github.com/HubSpot/ChromeDevToolsClient) for the Chrome DevTools Protocol\r\n  * another [chrome-devtools-java-client](https://github.com/kklisura/chrome-devtools-java-client) project featuring annotation-style [builder pattern](https://dzone.com/articles/design-patterns-the-builder-pattern) design for handling chrome commandline arguments.\r\n  * [examples](https://github.com/adiohana/selenium-chrome-devtools-examples) with callback hook example\r\n  * yet another project [chrome-devtools-webdriver-integration](https://github.com/sahajamit/chrome-devtools-webdriver-integration)\r\n  * yet another framework project [sachinguptait/SeleniumAutomation](https://github.com/sachinguptait/SeleniumAutomation) demonstrating Selenium 4 and CDP features\r\n  * yet another project [SrinivasanTarget/selenium4CDPsamples](https://github.com/SrinivasanTarget/selenium4CDPsamples)\r\n  * Python Chrome Devtools Procotol [client](https://github.com/qtacore/chrome_master/blob/master/chrome_master/input_handler.py)\r\n  * yet another [CDP Java client](https://github.com/Fathima704/sample-selenium-cdp) . Note: only works with Selenium 4.alpha-2\r\n  * Python Chrome Devtools Procotol [client](https://github.com/shish/devtools-py)\r\n  * Selenium 3.x CDP Exender [clone project](https://github.com/sergueik/cdp_webdriver) - the original project [sahajamit/chrome-devtools-webdriver-integration](https://github.com/sahajamit/chrome-devtools-webdriver-integration) - is somewhat stale\r\n  * Selenium 4.0x `WindowType` [feature](https://www.facebook.com/2162486093842847/posts/selenium-4-is-providing-a-new-feature-to-launch-and-switch-to-new-tab-or-window-/2253812251376897/)\r\n  * use CDP to switch to winows [stackoverflow](https://stackoverflow.com/questions/48219637/use-devtools-protocol-to-open-new-tab-in-window)\r\n  * JavaScript-style event callback design [jpuppeteer](https://github.com/sunshinex/jpuppeteer)\r\n  * [library inspired by Puppeteer](https://github.com/fanyong920/jvppeteer) to facilitate the use of Chrome DevTools API to control Chrome or Chromium via Java\r\n  * advanced [prevention headless browser detection](https://intoli.com/blog/making-chrome-headless-undetectable/)\r\n  * [recaptcha-3 score](https://antcpt.com/rus/information/demo-form/recaptcha-3-test-score.html)\r\n  * [Puppeteer Documentation](https://devdocs.io/puppeteer/)\r\n  * [difference between WebDriver and DevTool protocol](https://stackoverflow.com/questions/50939116/what-is-the-difference-between-webdriver-and-devtool-protocol/50942942#50942942) explained\r\n  * [using Chrome DevTools Protocol](https://github.com/aslushnikov/getting-started-with-cdp)\r\n  * overview of [DevTools access offered by Selenium 4](https://applitools.com/blog/selenium-4-chrome-devtools/)\r\n  * [Libraries.io](https://libraries.io/maven/org.seleniumhq.selenium:selenium-devtools-v93) - monitors over 2 million open source libraries/packages from 36 package managers\r\n  * [collection of Selenium 4 CDP-specific tests](https://github.com/rookieInTraining)\r\n  * https://medium.com/codex/selenium4-a-peek-into-chrome-devtools-92bca6de55e0\r\n  * BiDirectional WebDriver Protocol [w3c spec](https://w3c.github.io/webdriver-bidi/)\r\n  * BiDi - The future of cross-browser automation [blog](https://developer.chrome.com/blog/webdriver-bidi/)\r\n  * BiDirectional functionality [official documentation](https://www.selenium.dev/documentation/webdriver/bidirectional/)\r\n  * list of bidi APIs [examples](https://www.selenium.dev/documentation/webdriver/bidirectional/bidi_api)\r\n  * [guide To Java 8 Optional](https://www.baeldung.com/java-optional)\r\n  * [translation of the guide To Java 8 Optional](https://habr.com/ru/post/658457/)(in Russian)\r\n  * Ferrum - Ruby gem for \"high-level\" API for CDP backed Chrome browser automation [rubycdp/ferrum](https://github.com/rubycdp/ferrum) repository, [rubygems.org link](https://rubygems.org/gems/ferrum/versions/0.6.2) [documentation translation](https://habr.com/ru/post/681292/) (in Russian)\r\n  * [serg-ty/selenium-tests-logger](https://github.com/serg-ty/selenium-tests-logger) project to enable listeners as part of the logging\r\n\r\n  * [list of existing headless web browsers](https://github.com/dhamaniasad/HeadlessBrowsers)\r\n  * [new headless mode](https://www.selenium.dev/blog/2023/headless-is-going-away/)\r\n\r\n  * [interactive performance analysis with Chrome DevTools](https://www.thisdot.co/blog/performance-analysis-with-chrome-devtools)\r\n  * [replacing request url, postdata and headers](https://github.com/premsundarraj/SeleniumCDP)\r\n  * [getting connection information and cookies from chrome dev tools and use with curl](https://github.com/fipso/ccurl.sh)\r\n  * [How to Install Google Chrome Web Browser on Ubuntu 18.04](https://linuxize.com/post/how-to-install-google-chrome-web-browser-on-ubuntu-18-04/)\r\n  * [Content Security Policy (CSP) Evaluator](https://csp-evaluator.withgoogle.com/)\r\n  * [Testing for Content Security Policy](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/12-Test_for_Content_Security_Policy)\r\n  * [Enable page Content Security Policy by-passing](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-setBypassCSP)\r\n  * [Content Security Policy Examples](https://content-security-policy.com/examples/)\r\n\r\n\r\n\r\n### License\r\nThis project is licensed under the terms of the MIT license.\r\n\r\n### Author\r\n[Serguei Kouzmine](kouzmine_serguei@yahoo.com)\r\n\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergueik%2Fselenium_cdp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergueik%2Fselenium_cdp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergueik%2Fselenium_cdp/lists"}