这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

BiDirectional functionality

BiDirectional means that communication is happening in two directions simultaneously. The traditional WebDriver model involves strict request/response commands which only allows for communication to happen in one direction at any given time. In most cases this is what you want; it ensures that the browser is doing the expected things in the right order, but there are a number of interesting things that can be done with asynchronous interactions.

This functionality is currently available in a limited fashion with the [Chrome DevTools Protocol] (CDP), but to address some of its drawbacks, the Selenium team, along with the major browser vendors, have worked to create the new WebDriver BiDi Protocol. This specification aims to create a stable, cross-browser API that leverages bidirectional communication for enhanced browser automation and testing functionality, including streaming events from the user agent to the controlling software via WebSockets. Users will be able to listen for and record or manipulate events as they happen during the course of a Selenium session.

Enabling BiDi in Selenium

In order to use WebDriver BiDi, setting the capability in the browser options will enable the required functionality:

options.setCapability("webSocketUrl", true);
options.enable_bidi = True
UseWebSocketUrl = true,
options.web_socket_url = true
Options().enableBidi();
options.setCapability("webSocketUrl", true);

This enables the WebSocket connection for bidirectional communication, unlocking the full potential of the WebDriver BiDi protocol.

Note that Selenium is updating its entire implementation from WebDriver Classic to WebDriver BiDi (while maintaining backwards compatibility as much as possible), but this section of documentation focuses on the new functionality that bidirectional communication allows. The low-level BiDi domains will be accessible in the code to the end user, but the goal is to provide high-level APIs that are straightforward methods of real-world use cases. As such, the low-level components will not be documented, and this section will focus only on the user-friendly features that we encourage users to take advantage of.

If there is additional functionality you’d like to see, please raise a feature request.

1 - WebDriver BiDi Logging Features

These features are related to logging. Because “logging” can refer to so many different things, these methods are made available via a “script” namespace.

Remember that to use WebDriver BiDi, you must enable it in Options. For more details, see Enabling BiDi

Console Message Handlers

Record or take actions on console.log events.

Add Handler

    driver.script.add_console_message_handler(log_entries.append)
    driver.script.add_console_message_handler { |log| log_entries << log }

Remove Handler

You need to store the ID returned when adding the handler to delete it.

    id = driver.script.add_console_message_handler(log_entries.append)
    driver.script.remove_console_message_handler(id)
    id = driver.script.add_console_message_handler { |log| log_entries << log }
    driver.script.remove_console_message_handler(id)

JavaScript Exception Handlers

Record or take actions on JavaScript exception events.

Add Handler

    driver.script.add_javascript_error_handler(log_entries.append)
    driver.script.add_javascript_error_handler { |error| log_entries << error }

Remove Handler

You need to store the ID returned when adding the handler to delete it.

    id = driver.script.add_javascript_error_handler(log_entries.append)
    driver.script.remove_javascript_error_handler(id)
    id = driver.script.add_javascript_error_handler { |error| log_entries << error }
    driver.script.remove_javascript_error_handler(id)

2 - WebDriver BiDi Network Features

These features are related to networking, and are made available via a “network” namespace.

The implementation of these features is being tracked here: #13993

Remember that to use WebDriver BiDi, you must enable it in Options. For more details, see Enabling BiDi

Authentication Handlers

Request Handlers

Response Handlers

3 - WebDriver BiDi Script Features

These features are related to scripts, and are made available via a “script” namespace.

The implementation of these features is being tracked here: #13992

Remember that to use WebDriver BiDi, you must enable it in Options. For more details, see Enabling BiDi

Script Pinning

Execute Script

DOM Mutation Handlers

4 - Chrome DevTools 协议

使用 Selenium 操作 Chrome DevTools 协议的示例。 CDP 的支持是临时的,直到 WebDriver BiDi 实现为止。

许多浏览器提供“开发者工具”(DevTools),这是与浏览器集成的一组工具,开发人员可以使用它们来调试网页应用程序并探索网页的性能。Google Chrome 的开发者工具使用一种称为 Chrome DevTools 协议(简称 “CDP”)的协议。顾名思义,该协议并非为测试设计,也没有稳定的 API,因此功能很大程度上取决于浏览器的版本。

Selenium 正在致力于实现一种基于标准的、跨浏览器的、稳定的 CDP 替代方案,称为 [WebDriver BiDi]。在对该新协议的支持完成之前,Selenium 计划在适用的地方提供对 CDP 功能的访问。

在 Selenium 中使用 Chrome DevTools 协议

Chrome 和 Edge 提供了发送基本 CDP 命令的方法。 但对于需要双向通信的功能,这种方法无效。你需要知道在何时启用哪些域,以及域、方法和参数的确切名称和类型。

    Map<String, Object> cookie = new HashMap<>();
    cookie.put("name", "cheese");
    cookie.put("value", "gouda");
    cookie.put("domain", "www.selenium.dev");
    cookie.put("secure", true);
    ((HasCdp) driver).executeCdpCommand("Network.setCookie", cookie);
    cookie = {'name': 'cheese',
              'value': 'gouda',
              'domain': 'www.selenium.dev',
              'secure': True}

    driver.execute_cdp_cmd('Network.setCookie', cookie)
            var cookie = new Dictionary<string, object>
            {
                { "name", "cheese" },
                { "value", "gouda" },
                { "domain", "www.selenium.dev" },
                { "secure", true }
            };
            ((ChromeDriver)driver).ExecuteCdpCommand("Network.setCookie", cookie);
    driver.execute_cdp('Network.setCookie',
                       name: 'cheese',
                       value: 'gouda',
                       domain: 'www.selenium.dev',
                       secure: true)

为简化 CDP 的使用并提供对更高级功能的访问,Selenium 绑定会自动为最常见的域生成类和方法。
不过,CDP 方法和实现可能会因版本而异,因此你需要确保 Chrome 版本和 DevTools 版本相匹配。
Selenium 在任何时间点支持 Chrome 的最近三个版本,并且尽量同步发布以确保可以访问最新版本。

这种限制给一些绑定带来了额外的挑战,动态生成的 CDP 支持要求用户定期更新代码,以引用正确版本的 CDP。
在某些情况下,已创建了一个理想化的实现,它应该适用于任何版本的 CDP,而无需用户更改代码,但这并非总是可用。

关于如何在 Selenium 测试中使用 CDP 的示例可以在以下页面找到,但我们想提到一些常被引用但实际价值有限的例子:

  • 地理位置 ——几乎所有网站都使用 IP 地址来确定物理位置,因此设置模拟地理位置很少能达到预期效果。
  • 覆盖设备指标 ——Chrome 提供了一个很棒的 API 来在 Options 类中设置移动模拟,这通常比尝试使用 CDP 更优越。

4.1 - Chrome DevTools Logging Features

Logging features using CDP.

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

While Selenium 4 provides direct access to the Chrome DevTools Protocol, these methods will eventually be removed when WebDriver BiDi implemented.

Console Logs

    ((HasLogEvents) driver).onLogEvent(consoleEvent(e -> messages.add(e.getMessages().get(0))));
    async with driver.bidi_connection() as session:
        async with Log(driver, session).add_listener(Console.ALL) as messages:
            using IJavaScriptEngine monitor = new JavaScriptEngine(driver);
            var messages = new List<string>();
            monitor.JavaScriptConsoleApiCalled += (_, e) =>
            {
                messages.Add(e.MessageContent);
            };
            await monitor.StartEventMonitoring();
    driver.on_log_event(:console) { |log| logs << log.args.first }

JavaScript Exceptions

    async with driver.bidi_connection() as session:
        async with Log(driver, session).add_js_error_listener() as messages:
            using IJavaScriptEngine monitor = new JavaScriptEngine(driver);
            var messages = new List<string>();
            monitor.JavaScriptExceptionThrown += (_, e) =>
            {
                messages.Add(e.Message);
            };
            await monitor.StartEventMonitoring();
    driver.on_log_event(:exception) { |exception| exceptions << exception }

4.2 - Chrome DevTools Network Features

Network features using CDP.

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

While Selenium 4 provides direct access to the Chrome DevTools Protocol, these methods will eventually be removed when WebDriver BiDi implemented.

Basic authentication

Some applications make use of browser authentication to secure pages. It used to be common to handle them in the URL, but browsers stopped supporting this. With this code you can insert the credentials into the header when necessary

    Predicate<URI> uriPredicate = uri -> uri.toString().contains("herokuapp.com");
    Supplier<Credentials> authentication = UsernameAndPassword.of("admin", "admin");
    ((HasAuthentication) driver).register(uriPredicate, authentication);
        credentials = base64.b64encode("admin:admin".encode()).decode()
        auth = {'authorization': 'Basic ' + credentials}
        await connection.session.execute(connection.devtools.network.set_extra_http_headers(Headers(auth)))
            var handler = new NetworkAuthenticationHandler()
            {
                UriMatcher = uri => uri.AbsoluteUri.Contains("herokuapp"),
                Credentials = new PasswordCredentials("admin", "admin")
            };
            var networkInterceptor = driver.Manage().Network;
            networkInterceptor.AddAuthenticationHandler(handler);
            await networkInterceptor.StartMonitoring();
    driver.register(username: 'admin',
                    password: 'admin',
                    uri: /herokuapp/)

Network Interception

Both requests and responses can be recorded or transformed.

Response information

    try (NetworkInterceptor ignored =
        new NetworkInterceptor(
            driver,
            (Filter)
                next ->
                    req -> {
                      HttpResponse res = next.execute(req);
                      contentType.add(res.getHeader("Content-Type"));
                      return res;
                    })) {
            INetwork networkInterceptor = driver.Manage().Network;
            networkInterceptor.NetworkResponseReceived += (_, e)  =>
            {
                contentType.Add(e.ResponseHeaders["content-type"]);
            };
            await networkInterceptor.StartMonitoring();
    driver.intercept do |request, &continue|
      continue.call(request) do |response|
        content_type << response.headers['content-type']
      end
    end

Response transformation

    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!"))))) {
            var handler = new NetworkResponseHandler()
            {
                ResponseMatcher = _ => true,
                ResponseTransformer = _ => new HttpResponseData
                {
                    StatusCode = 200,
                    Body = "Creamy, delicious cheese!"
                }
            };
            INetwork networkInterceptor = driver.Manage().Network;
            networkInterceptor.AddResponseHandler(handler);
            await networkInterceptor.StartMonitoring();
    driver.intercept do |request, &continue|
      continue.call(request) do |response|
        response.body = 'Creamy, delicious cheese!' if request.url.include?('blank')
      end
    end

Request interception

    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);
                    })) {
            var handler = new NetworkRequestHandler
            {
                RequestMatcher = request => request.Url.Contains("one.js"),
                RequestTransformer = request =>
                {
                    request.Url = request.Url.Replace("one", "two");

                    return request;
                }
            };
            INetwork networkInterceptor = driver.Manage().Network;
            networkInterceptor.AddRequestHandler(handler);
            await networkInterceptor.StartMonitoring();
    driver.intercept do |request, &continue|
      uri = URI(request.url)
      request.url = uri.to_s.gsub('one', 'two') if uri.path&.end_with?('one.js')
      continue.call(request)
    end

Performance Metrics

    devTools.send(Performance.enable(Optional.empty()));
    List<Metric> metricList = devTools.send(Performance.getMetrics());
    async with driver.bidi_connection() as connection:
        await connection.session.execute(connection.devtools.performance.enable())
        metric_list = await connection.session.execute(connection.devtools.performance.get_metrics())
            await domains.Performance.Enable(new OpenQA.Selenium.DevTools.V126.Performance.EnableCommandSettings());
            var metricsResponse =
                await session.SendCommand<GetMetricsCommandSettings, GetMetricsCommandResponse>(
                    new GetMetricsCommandSettings()
                );
    driver.devtools.performance.enable
    metric_list = driver.devtools.performance.get_metrics.dig('result', 'metrics')

Setting Cookies

    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()));
    async with driver.bidi_connection() as connection:
        execution = connection.devtools.network.set_cookie(
            name="cheese",
            value="gouda",
            domain="www.selenium.dev",
            secure=True
        )
        await connection.session.execute(execution)
            var cookieCommandSettings = new SetCookieCommandSettings
            {
                Name = "cheese",
                Value = "gouda",
                Domain = "www.selenium.dev",
                Secure = true
            };
            await domains.Network.SetCookie(cookieCommandSettings);
    driver.devtools.network.set_cookie(name: 'cheese',
                                       value: 'gouda',
                                       domain: 'www.selenium.dev',
                                       secure: true)

Waiting for Downloads

    devTools.send(
            Browser.setDownloadBehavior(
                    Browser.SetDownloadBehaviorBehavior.ALLOWANDNAME,
                    Optional.empty(),
                    Optional.of(""),
                    Optional.of(true)));
    driver.devtools.browser.set_download_behavior(behavior: 'allow',
                                                  download_path: '',
                                                  events_enabled: true)

    driver.devtools.browser.on(:download_progress) do |progress|
      @completed = progress['state'] == 'completed'
    end

4.3 - Chrome DevTools Script Features

Script features using CDP.

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

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);
            var key = await new JavaScriptEngine(driver).PinScript("return arguments;");
            var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element);
    key = driver.pin_script('return arguments;')
    arguments = driver.execute_script(key, 1, true, element)

DOM Mutation Handlers

            driver.get('https://www.selenium.dev/selenium/web/dynamic.html')
            driver.find_element(By.ID, "reveal").click()
            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.on_log_event(:mutation) { |mutation| mutations << mutation.element }

5 - 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.

5.1 - Browsing Context

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

Commands

This section contains the APIs related to browsing context commands.

Open a new window

Creates a new browsing context in a new window.

Selenium v4.8

        Assertions.assertNotNull(browsingContext.getId());
    }

    @Test

Selenium v4.8

            })
            assert.notEqual(browsingContext.id, null)
        })

Open a new tab

Creates a new browsing context in a new tab.

Selenium v4.8

        Assertions.assertNotNull(browsingContext.getId());
    }

    @Test

Selenium v4.8

            })
            assert.notEqual(browsingContext.id, null)
        })

Use existing window handle

Creates a browsing context for the existing tab/window to run commands.

Selenium v4.8

    }

    @Test
    void testCreateAWindowWithAReferenceContext() {
        BrowsingContext

Selenium v4.8

                browsingContextId: id,
            })
            assert.equal(browsingContext.id, 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.

Selenium v4.8

        Assertions.assertNotNull(browsingContext.getId());
    }

    @Test
    void testCreateATabWithAReferenceContext() {
        BrowsingContext

Selenium v4.8

                referenceContext: await driver.getWindowHandle(),
            })
            assert.notEqual(browsingContext.id, null)
        })

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.

Selenium v4.8


        NavigationResult info = browsingContext.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");

        Assertions.assertNotNull(browsingContext.getId());
        Assertions.assertNotNull(info.getNavigationId());
        Assertions.assertTrue(info.getUrl().contains("/bidi/logEntryAdded.html"));

Selenium v4.8

                referenceContext: await driver.getWindowHandle(),
            })
            assert.notEqual(browsingContext.id, null)
        })

Selenium v4.8

    @Test
    void testNavigateToAUrlWithReadinessState() {
        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.assertNotNull(info.getNavigationId());

Selenium v4.8

            assert.notEqual(browsingContext.id, null)

Selenium v4.8


    @Test
    void testGetTreeWithAChild() {
        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();

Selenium v4.8

                'complete'
            )

            assert.notEqual(browsingContext.id, null)

Get browsing context tree

Provides a tree of all browsing contexts descending from the parent browsing context, including the parent browsing context.

Selenium v4.8

        Assertions.assertEquals(1, info.getChildren().size());
        Assertions.assertEquals(referenceContextId, info.getId());
        Assertions.assertTrue(info.getChildren().get(0).getUrl().contains("formPage.html"));
    }

    @Test
    void testGetTreeWithDepth() {
        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);

Selenium v4.8

                browsingContextId: browsingContextId,
            })
            await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete')

            const contextInfo = await parentWindow.getTree()
            assert.equal(contextInfo.children.length, 1)
            assert.equal(contextInfo.id, browsingContextId)

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.

Selenium v4.8

        Assertions.assertNull(info.getChildren()); // since depth is 0
        Assertions.assertEquals(referenceContextId, info.getId());
    }

    @Test
    void testGetAllTopLevelContexts() {
        BrowsingContext window1 = new BrowsingContext(driver, driver.getWindowHandle());
        BrowsingContext window2 = new BrowsingContext(driver, WindowType.WINDOW);

        List<BrowsingContextInfo> contextInfoList = window1.getTopLevelContexts();

        Assertions.assertEquals(2, contextInfoList.size());
    }

Selenium v4.8

                browsingContextId: browsingContextId,
            })
            await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete')

            const contextInfo = await parentWindow.getTree(0)
            assert.equal(contextInfo.children, null)
            assert.equal(contextInfo.id, browsingContextId)

Get All Top level browsing contexts

Selenium v4.8

    void testCloseAWindow() {
        BrowsingContext window1 = new BrowsingContext(driver, WindowType.WINDOW);
        BrowsingContext window2 = new BrowsingContext(driver, WindowType.WINDOW);

        window2.close();

        Assertions.assertThrows(BiDiException.class, window2::getTree);
    }

Close a tab/window

Selenium v4.8

    void testCloseATab() {
        BrowsingContext tab1 = new BrowsingContext(driver, WindowType.TAB);
        BrowsingContext tab2 = new BrowsingContext(driver, WindowType.TAB);

        tab2.close();

        Assertions.assertThrows(BiDiException.class, tab2::getTree);
    }
}

Selenium v4.8


            await window2.close()

            assert.doesNotThrow(async function () {

Activate a browsing context

Reload a browsing context

Handle user prompt

Capture Screenshot

Capture Viewport Screenshot

Selenium v4.15

            })

            const response = await browsingContext.captureBoxScreenshot(5, 5, 10, 10)

            const base64code = response.slice(0, 5)

Capture Element Screenshot

Set Viewport

Selenium v4.15

            const result = await driver.executeScript('return [window.innerWidth, window.innerHeight];')

Selenium v4.10

                scale: 1,
                background: true,
                width: 30,
                height: 30,
                top: 1,
                bottom: 1,
                left: 1,
                right: 1,
                shrinkToFit: true,
                pageRanges: ['1-2'],
            })

            let base64Code = result.data.slice(0, 5)

Traverse history

Events

This section contains the APIs related to browsing context events.

Browsing Context Created Event

Selenium v4.10

    try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
        CompletableFuture<BrowsingContextInfo> future = new CompletableFuture<>();

        inspector.onBrowsingContextCreated(future::complete);

        String windowHandle = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle();

        BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS);

Selenium v4.9.2

            let contextInfo = null
            const browsingContextInspector = await BrowsingContextInspector(driver)
            await browsingContextInspector.onBrowsingContextCreated((entry) => {
                contextInfo = entry
            })

Dom Content loaded Event

Selenium v4.10

            String windowHandle = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle();

            BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS);

            Assertions.assertEquals(windowHandle, browsingContextInfo.getId());
        }
    }

    @Test
    void canListenToDomContentLoadedEvent()

Selenium v4.9.2

        it('can listen to dom content loaded event', async function () {
            const browsingContextInspector = await BrowsingContextInspector(driver)
            let navigationInfo = null
            await browsingContextInspector.onDomContentLoaded((entry) => {
                navigationInfo = entry
            })

            const browsingContext = await BrowsingContext(driver, {
                browsingContextId: await driver.getWindowHandle(),
            })

Browsing Context Loaded Event

Selenium v4.10

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<NavigationInfo> future = new CompletableFuture<>();
            inspector.onBrowsingContextLoaded(future::complete);

            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
            context.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE);

            NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS);

Selenium v4.9.2

            let navigationInfo = null
            const browsingContextInspector = await BrowsingContextInspector(driver)

            await browsingContextInspector.onBrowsingContextLoaded((entry) => {
                navigationInfo = entry
            })
            const browsingContext = await BrowsingContext(driver, {
                browsingContextId: await driver.getWindowHandle(),
            })

Selenium v4.15

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<NavigationInfo> future = new CompletableFuture<>();
            inspector.onNavigationStarted(future::complete);

            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
            context.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE);

            NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS);

Fragment Navigated Event

Selenium v4.15

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<NavigationInfo> future = new CompletableFuture<>();

            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
            context.navigate("https://www.selenium.dev/selenium/web/linked_image.html", ReadinessState.COMPLETE);

            inspector.onFragmentNavigated(future::complete);

            context.navigate("https://www.selenium.dev/selenium/web/linked_image.html#linkToAnchorOnThisPage", ReadinessState.COMPLETE);

            NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS);

Selenium v4.15.0

            let navigationInfo = null
            const browsingContextInspector = await BrowsingContextInspector(driver)

            const browsingContext = await BrowsingContext(driver, {
                browsingContextId: await driver.getWindowHandle(),
            })
            await browsingContext.navigate('https://www.selenium.dev/selenium/web/linked_image.html', 'complete')

            await browsingContextInspector.onFragmentNavigated((entry) => {
                navigationInfo = entry
            })

User Prompt Opened Event

Selenium v4.15

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<NavigationInfo> future = new CompletableFuture<>();

            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
            context.navigate("https://www.selenium.dev/selenium/web/linked_image.html", ReadinessState.COMPLETE);

            inspector.onFragmentNavigated(future::complete);

            context.navigate("https://www.selenium.dev/selenium/web/linked_image.html#linkToAnchorOnThisPage", ReadinessState.COMPLETE);

            NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS);

User Prompt Closed Event

Selenium v4.15

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<UserPromptClosed> future = new CompletableFuture<>();

            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());
            inspector.onUserPromptClosed(future::complete);

            driver.get("https://www.selenium.dev/selenium/web/alerts.html");

            driver.findElement(By.id("prompt")).click();

            context.handleUserPrompt(true, "selenium");

            UserPromptClosed userPromptClosed = future.get(5, TimeUnit.SECONDS);
            Assertions.assertEquals(context.getId(), userPromptClosed.getBrowsingContextId());

Browsing Context Destroyed Event

Selenium v4.18

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<BrowsingContextInfo> future = new CompletableFuture<>();

            inspector.onBrowsingContextDestroyed(future::complete);

            String windowHandle = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle();

            driver.close();

            BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS);

            Assertions.assertEquals(windowHandle, browsingContextInfo.getId());

Selenium v4.18.0

            let contextInfo = null
            const browsingContextInspector = await BrowsingContextInspector(driver)
            await browsingContextInspector.onBrowsingContextDestroyed((entry) => {
                contextInfo = entry
            })

            await driver.switchTo().newWindow('window')

            const windowHandle = await driver.getWindowHandle()

5.2 - 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 input commands.

Perform Actions

Selenium v4.17

        Actions selectThreeOptions =
                actions.click(options.get(1)).keyDown(Keys.SHIFT).click(options.get(3)).keyUp(Keys.SHIFT);

        input.perform(windowHandle, selectThreeOptions.getSequences());

Selenium v4.17

            let options = await driver.findElements(By.tagName('option'))

            const actions = driver.actions().click(options[1]).keyDown(Key.SHIFT).click(options[3]).keyUp(Key.SHIFT).getSequences()

Release Actions

Selenium v4.17

        Actions sendLowercase =
                new Actions(driver).keyDown(inputTextBox, "a").keyDown(inputTextBox, "b");

        input.perform(windowHandle, sendLowercase.getSequences());
        ((JavascriptExecutor) driver).executeScript("resetEvents()");

        input.release(windowHandle);

Selenium v4.17

            await driver.executeScript('resetEvents()')

5.3 - Network

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

Commands

This section contains the APIs related to network commands.

Add network intercept

Selenium v4.18

        try (Network network = new Network(driver)) {
            String intercept =
                    network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT));

Selenium v4.18

            const intercept = await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT))

Remove network intercept

Selenium v4.18

        try (Network network = new Network(driver)) {
            String intercept =
                    network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT));
            Assertions.assertNotNull(intercept);
            network.removeIntercept(intercept);

Selenium v4.18

            const network = await Network(driver)
            const intercept = await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT))

Continue request blocked at authRequired phase with credentials

Selenium v4.18

        try (Network network = new Network(driver)) {
            network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED));
            network.onAuthRequired(
                    responseDetails ->
                            network.continueWithAuth(
                                    responseDetails.getRequest().getRequestId(),
                                    new UsernameAndPassword("admin", "admin")));
            driver.get("https://the-internet.herokuapp.com/basic_auth");

Selenium v4.18

            await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED))

            await network.authRequired(async (event) => {
                await network.continueWithAuth(event.request.request, 'admin','admin')
            })

Continue request blocked at authRequired phase without credentials

Selenium v4.18

        try (Network network = new Network(driver)) {
            network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED));
            network.onAuthRequired(
                    responseDetails ->
                            // Does not handle the alert
                            network.continueWithAuthNoCredentials(responseDetails.getRequest().getRequestId()));
            driver.get("https://the-internet.herokuapp.com/basic_auth");

Selenium v4.18

            await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED))

            await network.authRequired(async (event) => {
                await network.continueWithAuthNoCredentials(event.request.request)
            })

Cancel request blocked at authRequired phase

Selenium v4.18

        try (Network network = new Network(driver)) {
            network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED));
            network.onAuthRequired(
                    responseDetails ->
                            // Does not handle the alert
                            network.cancelAuth(responseDetails.getRequest().getRequestId()));
            driver.get("https://the-internet.herokuapp.com/basic_auth");

Selenium v4.18

            await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED))

            await network.authRequired(async (event) => {
                await network.cancelAuth(event.request.request)
            })

Fail request

Selenium v4.18

        try (Network network = new Network(driver)) {
            network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT));
            network.onBeforeRequestSent(
                    responseDetails -> network.failRequest(responseDetails.getRequest().getRequestId()));
            driver.manage().timeouts().pageLoadTimeout(Duration.of(5, ChronoUnit.SECONDS));

Events

This section contains the APIs related to network events.

Before Request Sent

Selenium v4.15

        try (Network network = new Network(driver)) {
            CompletableFuture<BeforeRequestSent> future = new CompletableFuture<>();
            network.onBeforeRequestSent(future::complete);
            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");

            BeforeRequestSent requestSent = future.get(5, TimeUnit.SECONDS);

Selenium v4.18

            let beforeRequestEvent = null
            const network = await Network(driver)
            await network.beforeRequestSent(function (event) {
                beforeRequestEvent = event
            })

            await driver.get('https://www.selenium.dev/selenium/web/blank.html')

Response Started

Selenium v4.15

        try (Network network = new Network(driver)) {
            CompletableFuture<ResponseDetails> future = new CompletableFuture<>();
            network.onResponseStarted(future::complete);
            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");

            ResponseDetails response = future.get(5, TimeUnit.SECONDS);
            String windowHandle = driver.getWindowHandle();

Selenium v4.18

            let onResponseStarted = []
            const network = await Network(driver)
            await network.responseStarted(function (event) {
                onResponseStarted.push(event)
            })

            await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')

Response Completed

Selenium v4.15

        try (Network network = new Network(driver)) {
            CompletableFuture<ResponseDetails> future = new CompletableFuture<>();
            network.onResponseCompleted(future::complete);
            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");

            ResponseDetails response = future.get(5, TimeUnit.SECONDS);
            String windowHandle = driver.getWindowHandle();

Selenium v4.18

            let onResponseCompleted = []
            const network = await Network(driver)
            await network.responseCompleted(function (event) {
                onResponseCompleted.push(event)
            })

            await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')

Auth Required

Selenium v4.17

        try (Network network = new Network(driver)) {
            CompletableFuture<ResponseDetails> future = new CompletableFuture<>();
            network.onAuthRequired(future::complete);
            driver.get("https://the-internet.herokuapp.com/basic_auth");

            ResponseDetails response = future.get(5, TimeUnit.SECONDS);

5.4 - Script

Page being translated from English to Chinese. Do you speak Chinese? Help us to translate it by sending us pull requests!

Commands

This section contains the APIs related to script commands.

Call function in a browsing context

Selenium v4.15

        try (Script script = new Script(id, driver)) {
            List<LocalValue> arguments = new ArrayList<>();
            arguments.add(PrimitiveProtocolValue.numberValue(22));

            Map<Object, LocalValue> value = new HashMap<>();
            value.put("some_property", LocalValue.numberValue(42));
            LocalValue thisParameter = LocalValue.objectValue(value);

            arguments.add(thisParameter);

            EvaluateResult result =
                    script.callFunctionInBrowsingContext(
                            id,
                            "function processWithPromise(argument) {\n"
                                    + "  return new Promise((resolve, reject) => {\n"
                                    + "    setTimeout(() => {\n"
                                    + "      resolve(argument + this.some_property);\n"
                                    + "    }, 1000)\n"
                                    + "  })\n"
                                    + "}",
                            true,
                            Optional.of(arguments),
                            Optional.of(thisParameter),
                            Optional.of(ResultOwnership.ROOT));

Selenium v4.9

            const manager = await ScriptManager(id, driver)

            let argumentValues = []
            let value = new ArgumentValue(LocalValue.createNumberValue(22))
            argumentValues.push(value)

            let mapValue = {some_property: LocalValue.createNumberValue(42)}
            let thisParameter = new ArgumentValue(LocalValue.createObjectValue(mapValue)).asMap()

            const result = await manager.callFunctionInBrowsingContext(
                id,
                'function processWithPromise(argument) {' +
                'return new Promise((resolve, reject) => {' +
                'setTimeout(() => {' +
                'resolve(argument + this.some_property);' +
                '}, 1000)' +
                '})' +
                '}',
                true,
                argumentValues,
                thisParameter,
                ResultOwnership.ROOT)

Call function in a sandbox

Selenium v4.15

        try (Script script = new Script(id, driver)) {
            EvaluateResult result =
                    script.callFunctionInBrowsingContext(
                            id,
                            "sandbox",
                            "() => window.foo",
                            true,
                            Optional.empty(),
                            Optional.empty(),
                            Optional.empty());

Selenium v4.9

            const manager = await ScriptManager(id, driver)

            await manager.callFunctionInBrowsingContext(id, '() => { window.foo = 2; }', true, null, null, null, 'sandbox')

Call function in a realm

Selenium v4.15

        try (Script script = new Script(tab, driver)) {
            List<RealmInfo> realms = script.getAllRealms();
            String realmId = realms.get(0).getRealmId();

            EvaluateResult result = script.callFunctionInRealm(
                    realmId,
                    "() => { window.foo = 3; }",
                    true,
                    Optional.empty(),
                    Optional.empty(),
                    Optional.empty());

Selenium v4.9

            const manager = await ScriptManager(firstTab, driver)

            const realms = await manager.getAllRealms()
            const realmId = realms[0].realmId

            await manager.callFunctionInRealm(realmId, '() => { window.foo = 3; }', true)

Evaluate script in a browsing context

Selenium v4.15

        try (Script script = new Script(id, driver)) {
            EvaluateResult result =
                    script.evaluateFunctionInBrowsingContext(id, "1 + 2", true, Optional.empty());

Selenium v4.9

            const manager = await ScriptManager(id, driver)

            const result = await manager.evaluateFunctionInBrowsingContext(id, '1 + 2', true)

Evaluate script in a sandbox

Selenium v4.15

        try (Script script = new Script(id, driver)) {
            EvaluateResult result =
                    script.evaluateFunctionInBrowsingContext(
                            id, "sandbox", "window.foo", true, Optional.empty());

Selenium v4.9

            const manager = await ScriptManager(id, driver)

            await manager.evaluateFunctionInBrowsingContext(id, 'window.foo = 2', true, null, 'sandbox')

            const resultInSandbox = await manager.evaluateFunctionInBrowsingContext(id, 'window.foo', true, null, 'sandbox')

Evaluate script in a realm

Selenium v4.15

        try (Script script = new Script(tab, driver)) {
            List<RealmInfo> realms = script.getAllRealms();
            String realmId = realms.get(0).getRealmId();

            EvaluateResult result =
                    script.evaluateFunctionInRealm(
                            realmId, "window.foo", true, Optional.empty());

Selenium v4.9

            const manager = await ScriptManager(firstTab, driver)

            const realms = await manager.getAllRealms()
            const realmId = realms[0].realmId

            await manager.evaluateFunctionInRealm(realmId, 'window.foo = 3', true)

            const result = await manager.evaluateFunctionInRealm(realmId, 'window.foo', true)

Disown handles in a browsing context

Selenium v4.15

            script.disownBrowsingContextScript(

Selenium v4.9

            await manager.disownBrowsingContextScript(id, boxId)

Disown handles in a realm

Selenium v4.15

            script.disownRealmScript(realmId, List.of(boxId));

Selenium v4.9

            await manager.disownRealmScript(realmId, boxId)

Get all realms

Selenium v4.15

        try (Script script = new Script(firstWindow, driver)) {
            List<RealmInfo> realms = script.getAllRealms();

Selenium v4.9

            const manager = await ScriptManager(firstWindow, driver)

            const realms = await manager.getAllRealms()

Get realm by type

Selenium v4.15

        try (Script script = new Script(firstWindow, driver)) {
            List<RealmInfo> realms = script.getRealmsByType(RealmType.WINDOW);

Selenium v4.9

            const manager = await ScriptManager(firstWindow, driver)

            const realms = await manager.getRealmsByType(RealmType.WINDOW)

Get browsing context realms

Selenium v4.15

        try (Script script = new Script(windowId, driver)) {
            List<RealmInfo> realms = script.getRealmsInBrowsingContext(tabId);

Selenium v4.9

            const manager = await ScriptManager(windowId, driver)

            const realms = await manager.getRealmsInBrowsingContext(tabId)

Get browsing context realms by type

Selenium v4.15

            List<RealmInfo> windowRealms =
                    script.getRealmsInBrowsingContextByType(windowId, RealmType.WINDOW);

Selenium v4.9

            const realms = await manager.getRealmsInBrowsingContextByType(windowId, RealmType.WINDOW)

Preload a script

Selenium v4.15

            String id = script.addPreloadScript("() => { window.bar=2; }", "sandbox");

Selenium v4.10

            const manager = await ScriptManager(id, driver)

            const scriptId = await manager.addPreloadScript('() => {{ console.log(\'{preload_script_console_text}\') }}')

Remove a preloaded script

Selenium v4.15

                script.removePreloadScript(id);

Selenium v4.10

            await manager.removePreloadScript(scriptId)

Events

This section contains the APIs related to script events.

Message

Selenium v4.16

            script.onMessage(future::complete);

            script.callFunctionInBrowsingContext(
                    driver.getWindowHandle(),
                    "(channel) => channel('foo')",
                    false,
                    Optional.of(List.of(LocalValue.channelValue("channel_name"))),
                    Optional.empty(),
                    Optional.empty());

            Message message = future.get(5, TimeUnit.SECONDS);
            Assertions.assertEquals("channel_name", message.getChannel());
        }
    }

Selenium v4.18

            const manager = await ScriptManager(undefined, driver)

            let message = null

            await manager.onMessage((m) => {
                message = m
            })

            let argumentValues = []
            let value = new ArgumentValue(LocalValue.createChannelValue(new ChannelValue('channel_name')))
            argumentValues.push(value)

            const result = await manager.callFunctionInBrowsingContext(
                await driver.getWindowHandle(),
                '(channel) => channel("foo")',
                false,
                argumentValues,
            )

Realm Created

Selenium v4.16


            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());

            context.navigate("https://www.selenium.dev/selenium/blankPage");
            RealmInfo realmInfo = future.get(5, TimeUnit.SECONDS);
            Assertions.assertNotNull(realmInfo.getRealmId());
            Assertions.assertEquals(RealmType.WINDOW, realmInfo.getRealmType());
        }
    }

    @Test

Selenium v4.18

            const manager = await ScriptManager(undefined, driver)

            let realmInfo = null

            await manager.onRealmCreated((result) => {
                realmInfo = result
            })

            const id = await driver.getWindowHandle()
            const browsingContext = await BrowsingContext(driver, {
                browsingContextId: id,
            })

            await browsingContext.navigate('https://www.selenium.dev/selenium/web/blank', 'complete')

Realm Destroyed

Selenium v4.16


            BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());

            context.close();
            RealmInfo realmInfo = future.get(5, TimeUnit.SECONDS);
            Assertions.assertNotNull(realmInfo.getRealmId());
            Assertions.assertEquals(RealmType.WINDOW, realmInfo.getRealmType());
        }
    }
}

Selenium v4.19

            const manager = await ScriptManager(undefined, driver)

            let realmInfo = null

            await manager.onRealmDestroyed((result) => {
                realmInfo = result
            })

            const id = await driver.getWindowHandle()
            const browsingContext = await BrowsingContext(driver, {
                browsingContextId: id,
            })

            await browsingContext.close()

5.5 - 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.

Console logs

Listen to the console.log events and register callbacks to process the event.

    public void jsErrors() {
        CopyOnWriteArrayList<ConsoleLogEntry> logs = new CopyOnWriteArrayList<>();

        try (LogInspector logInspector = new LogInspector(driver)) {
            logInspector.onConsoleEntry(logs::add);
        }

        driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");

Selenium v4.8

            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)

JavaScript exceptions

Listen to the JS Exceptions and register callbacks to process the exception details.

            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());

Selenium v4.8

        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.

            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());