This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

BiDirectional API (W3C compliant)

Examples of working with Chrome DevTools Protocol in Selenium. CDP support is temporary until WebDriver BiDi has been implemented.

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.

1 - Browsing Context

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

    void testCreateAWindow() {
        BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.WINDOW);
        Assertions.assertNotNull(browsingContext.getId());
    }

Selenium v4.8

  it 'creates browsing context for given id' do
    id = driver.window_handle
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, context_id: id

Selenium v4.8

  it('test create a window', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'window',
    })
    assert.notEqual(browsingContext.id, null)
  })

Selenium v4.8

@pytest.mark.driver_type("bidi")
def test_create_browsing_context_for_given_id(driver):
    id = driver.current_window_handle
    browsing_context = (

Open a new tab

Creates a new browsing context in a new tab.

Selenium v4.8

    void testCreateATab() {
        BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);
        Assertions.assertNotNull(browsingContext.getId());
    }

Selenium v4.8

    expect(browsing_context.id).to eq(id)
  end

  it 'creates a window' do

Selenium v4.8

  it('test create a tab', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'tab',
    })
    assert.notEqual(browsingContext.id, null)
  })

Selenium v4.8

        )
    )
    assert browsing_context == id

Use existing window handle

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

Selenium v4.8

    void testCreateABrowsingContextForGivenId() {
        String id = driver.getWindowHandle();
        BrowsingContext browsingContext = new BrowsingContext(driver, id);
        Assertions.assertEquals(id, browsingContext.getId());
    }

Selenium v4.8

RSpec.describe 'Browsing Context' do
  let(:driver) { start_bidi_session }
  let(:wait) { Selenium::WebDriver::Wait.new(timeout: 5) }

  it 'creates browsing context for given id' do
    id = driver.window_handle
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, context_id: id

Selenium v4.8

  it('test create a browsing context for given id', async function () {
    const id = await driver.getWindowHandle()
    const browsingContext = await BrowsingContext(driver, {
      browsingContextId: id,
    })
    assert.equal(browsingContext.id, id)
  })

Selenium v4.8

    )
    assert browsing_context is not None


@pytest.mark.driver_type("bidi")
def test_navigate_to_url(driver):
    browsing_context = (
        driver.bidi_connection.bidi_session.browsing_context.create(
            type_hint=WindowTypes.TAB
        )

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 testCreateATab() {
        BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);

Selenium v4.8

  it('test create a window with a reference context', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'window',
      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


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

Selenium v4.8

  it('test create a tab with a reference context', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'tab',
      referenceContext: await driver.getWindowHandle(),
    })
    assert.notEqual(browsingContext.id, null)
  })

Selenium v4.8

    void testNavigateToAUrl() {
        BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);

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

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

    @Test

Selenium v4.8

      driver, type_hint: :window
    )
    expect(browsing_context.id).not_to be_nil
  end

  it 'creates a tab' do
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, type_hint: :tab
    )
    expect(browsing_context.id).not_to be_nil
  end

Selenium v4.8

  it('test navigate to a url', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'tab',
    })

    let info = await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')

    assert.notEqual(browsingContext.id, null)
    assert.notEqual(info.navigationId, null)
    assert(info.url.includes('/bidi/logEntryAdded.html'))
  })

Selenium v4.8

def test_create_window(driver):
    browsing_context = (
        driver.bidi_connection.bidi_session.browsing_context.create(
            type_hint=WindowTypes.WINDOW
        )
    )
    assert browsing_context is not None


@pytest.mark.driver_type("bidi")
def test_create_tab(driver):
    browsing_context = (
        driver.bidi_connection.bidi_session.browsing_context.create(

Selenium v4.8

    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());
        Assertions.assertTrue(info.getUrl().contains("/bidi/logEntryAdded.html"));
    }

    @Test

Selenium v4.8

  it('test navigate to a url with readiness state', async function () {
    const browsingContext = await BrowsingContext(driver, {
      type: 'tab',
    })

    const info = await browsingContext.navigate(
      'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html',
      'complete'
    )

    assert.notEqual(browsingContext.id, null)
    assert.notEqual(info.navigationId, null)
    assert(info.url.includes('/bidi/logEntryAdded.html'))
  })

Get browsing context tree

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

Selenium v4.8

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

        Assertions.assertEquals(1, contextInfoList.size());
        BrowsingContextInfo info = contextInfoList.get(0);

Selenium v4.8

  it 'gets tree with children' do
    reference_context_id = driver.window_handle
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, context_id: reference_context_id
    )

    browsing_context.navigate('https://www.selenium.dev/selenium/web/iframes.html')
    tree = browsing_context.get_tree

    expect(tree).not_to be_empty
    expect(tree.first['context']).to eq(reference_context_id)
    expect(tree.first['children']).not_to be_empty

Selenium v4.8

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

    const contextInfo = await parentWindow.getTree()

Selenium v4.8

    )
    assert browsing_context is not None


@pytest.mark.driver_type("bidi")
def test_navigate_to_url(driver):
    browsing_context = (
        driver.bidi_connection.bidi_session.browsing_context.create(
            type_hint=WindowTypes.TAB
        )

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

    }

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

    @Test

Selenium v4.8

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

    const contextInfo = await parentWindow.getTree(0)

Get All Top level browsing contexts

Selenium v4.8

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

  it('Get All Top level browsing contexts', async () => {
    const id = await driver.getWindowHandle()
    const window1 = await BrowsingContext(driver, {
      browsingContextId: id,
    })
    await BrowsingContext(driver, { type: 'window' })
    const res = await window1.getTopLevelContexts()
    assert.equal(res.length, 2)

Close a tab/window

Selenium v4.8

        BrowsingContext tab2 = new BrowsingContext(driver, WindowType.TAB);

        tab2.close();

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

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

        window1.activate();

        boolean isFocused = (boolean) ((JavascriptExecutor) driver).executeScript("return document.hasFocus();");

        Assertions.assertTrue(isFocused);
    }

Selenium v4.8

  it 'navigates to url with readiness state' do
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, type_hint: :tab
    )

    navigation_info = browsing_context.navigate(
      'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html',
      wait: :complete
    )

Selenium v4.8

    const window1 = await BrowsingContext(driver, {type: 'window'})
    const window2 = await BrowsingContext(driver, {type: 'window'})

    await window2.close()

Selenium v4.8

    navigation_info = (
        driver.bidi_connection.bidi_session.browsing_context.navigate(
            context=browsing_context,
            url="https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"

Activate a browsing context

Selenium v4.14.1

        BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB);

        browsingContext.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE);

        NavigationResult reloadInfo = browsingContext.reload(ReadinessState.INTERACTIVE);

Selenium v4.14.1

    expect(browsing_context.id).not_to be_nil
    expect(navigation_info['navigation_id']).not_to be_nil
  end

Selenium v4.15

    const window1 = await BrowsingContext(driver, {
      browsingContextId: id,
    })
    await window1.activate()

Selenium v4.14.1


    assert browsing_context is not None
    assert navigation_info.get('navigation_id') is not None
    assert "/bidi/logEntryAdded.html" in navigation_info.get('url', '')

Reload a browsing context

Selenium v4.13.0

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

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

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

Selenium v4.13.0

    reference_context_id = driver.window_handle
    browsing_context = Selenium::WebDriver::BiDi::BrowsingContext.new(
      driver, context_id: reference_context_id
    )

    browsing_context.navigate('https://www.selenium.dev/selenium/web/iframes.html')
    tree = browsing_context.get_tree

Selenium v4.15

    await browsingContext.reload(undefined, 'complete')

Selenium v4.13.0

@pytest.mark.driver_type("bidi")
def test_get_tree(driver):
    reference_context_id = driver.current_window_handle

    driver.get("https://www.selenium.dev/selenium/web/iframes.html")
    tree = (
        driver.bidi_connection.bidi_session.browsing_context.get_tree(
            root=reference_context_id
        )
    )

Handle user prompt

Selenium v4.13.0

    @Test
    void testDismissUserPromptWithText() {
        BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());

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

        driver.findElement(By.id("prompt-with-default")).click();

        String userText = "Selenium automates browsers";

Selenium v4.15

    await browsingContext.handleUserPrompt(true, userText)

Capture Screenshot

Selenium v4.13.0


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

        WebElement element = driver.findElement(By.id("box"));
        Rectangle elementRectangle = element.getRect();

Selenium v4.15

    const response = await browsingContext.captureScreenshot()

Capture Viewport Screenshot

Selenium v4.14.0

    }

    @Test
    void textCaptureElementScreenshot() {
        BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());

        driver.get("https://www.selenium.dev/selenium/web/formPage.html");
        WebElement element = driver.findElement(By.id("checky"));

        String screenshot = browsingContext.captureElementScreenshot(((RemoteWebElement) element).getId());

Selenium v4.15

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

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

Capture Element Screenshot

Selenium v4.14.0

        BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
        driver.get("https://www.selenium.dev/selenium/web/formPage.html");

        browsingContext.setViewport(250, 300);

        List<Long> newViewportSize =

Selenium v4.15

    const response = await browsingContext.captureElementScreenshot(elementId)

Set Viewport

Selenium v4.14.1

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

        driver.get("https://www.selenium.dev/selenium/web/formPage.html");
        PrintOptions printOptions = new PrintOptions();

Selenium v4.15

    await browsingContext.setViewport(250, 300)

Selenium v4.14.1

        browsingContext.navigate("https://www.selenium.dev/selenium/web/formPage.html", ReadinessState.COMPLETE);

        wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit();
        wait.until(titleIs("We Arrive Here"));

        browsingContext.back();

Selenium v4.10

    const result = await browsingContext.printPage({
      orientation: 'landscape',
      scale: 1,
      background: true,
      width: 30,
      height: 30,
      top: 1,
      bottom: 1,
      left: 1,
      right: 1,
      shrinkToFit: true,
      pageRanges: ['1-2'],
    })

Selenium v4.16.0


        wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit();
        wait.until(titleIs("We Arrive Here"));

        browsingContext.back();
        Assertions.assertTrue(driver.getPageSource().contains("We Leave From Here"));

Selenium v4.17

    await browsingContext.back()

Selenium v4.16.0

    void canTraverseBrowserHistory() {
        BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
        browsingContext.navigate("https://www.selenium.dev/selenium/web/formPage.html", ReadinessState.COMPLETE);

        wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit();
        wait.until(titleIs("We Arrive Here"));

        browsingContext.traverseHistory(-1);
        Assertions.assertTrue(driver.getPageSource().contains("We Leave From Here"));
    }
}

Selenium v4.17

    await browsingContext.forward()

Traverse history

Selenium v4.16.0

Selenium v4.17

    await browsingContext.traverseHistory(-1)

Locate nodes

Selenium v4.17

    void canLocateNodes() {
        BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
        driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html");

        LocateNodeParameters parameters = new LocateNodeParameters(Locator.css("div"));

        List<RemoteValue> elements = browsingContext.locateNodes(parameters);
        Assertions.assertEquals(13, elements.size());
    }

Selenium v4.17

  it 'locates nodes by css selector' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    nodes = driver.script.locate_nodes(locator: {type: 'css', value: 'button'})

    expect(nodes).not_to be_empty
  end

Selenium v4.17

  it('can locate nodes', async function () {
    const id = await driver.getWindowHandle()
    const browsingContext = await BrowsingContext(driver, {
      browsingContextId: id,
    })

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

    const element = await browsingContext.locateNodes(Locator.css('div'))
    assert.strictEqual(element.length, 13)
  })

Selenium v4.17

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

    # Locate nodes by CSS selector
    nodes = driver.script.locate_nodes(
        locator={"type": "css", "value": "button"}
    )

    assert len(nodes) > 0


@pytest.mark.driver_type("bidi")
def test_locate_nodes_by_xpath(driver):

Locate nodes with start nodes

Selenium v4.17

    void canLocateNodesGivenStartNodes() {
        String handle = driver.getWindowHandle();
        BrowsingContext browsingContext = new BrowsingContext(driver, handle);

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

        Script script = new Script(driver);
        EvaluateResult result =
                script.evaluateFunctionInBrowsingContext(
                        handle,
                        "document.querySelectorAll(\"form\")",
                        false,
                        Optional.of(ResultOwnership.ROOT));

        EvaluateResultSuccess resultSuccess = (EvaluateResultSuccess) result;
        List<RemoteReference> startNodes = new ArrayList<>();

        RemoteValue remoteValue = resultSuccess.getResult();
        List<RemoteValue> remoteValues = (List<RemoteValue>) remoteValue.getValue().get();

        remoteValues.forEach(
                value ->
                        startNodes.add(
                                new RemoteReference(RemoteReference.Type.SHARED_ID, value.getSharedId().get())));

        LocateNodeParameters parameters =
                new LocateNodeParameters(Locator.css("input"))
                        .setStartNodes(startNodes)
                        .setMaxNodeCount(50);

        List<RemoteValue> elements = browsingContext.locateNodes(parameters);
        Assertions.assertEquals(35, elements.size());
    }

Selenium v4.17

  it 'locates nodes with start nodes' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    body = driver.find_element(tag_name: 'body')

    nodes = driver.script.locate_nodes(
      locator: {type: 'css', value: 'button'},
      start_nodes: [body]
    )

    expect(nodes).not_to be_empty
  end

Selenium v4.17

  it('can locate node with given start nodes', async function () {
    const id = await driver.getWindowHandle()
    const browsingContext = await BrowsingContext(driver, {
      browsingContextId: id,
    })

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

    const script = await ScriptManager(id, driver)

    const result = await script.evaluateFunctionInBrowsingContext(
      id,
      "document.querySelectorAll('form')",
      false,
      'root',
    )

    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.notEqual(result.realmId, null)
    assert.equal(result.result.type, 'nodelist')

    const value = result.result.value

    const startNodes = []

    value.forEach((node) => {
      startNodes.push(new ReferenceValue(node.handle, node.sharedId))
    })

    const elements = await browsingContext.locateNodes(
      Locator.css('input'),
      50,
      'none',
      undefined,
      undefined,
      startNodes,
    )

    assert.strictEqual(elements.length, 37)
  })

Selenium v4.17

    # Get start node
    body = driver.find_element(tag_name="body")

    # Locate nodes starting from body
    nodes = driver.script.locate_nodes(
        locator={"type": "css", "value": "button"},
        start_nodes=[body]
    )

    assert len(nodes) > 0


@pytest.mark.driver_type("bidi")
def test_locate_nodes_by_id(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

    # Locate specific element by ID

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

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

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

Dom Content loaded Event

Selenium v4.10

        try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) {
            CompletableFuture<NavigationInfo> future = new CompletableFuture<>();
            inspector.onDomContentLoaded(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);

            Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded"));
        }

Selenium v4.9.2

    const browsingContextInspector = await BrowsingContextInspector(driver)
    let navigationInfo = null
    await browsingContextInspector.onDomContentLoaded((entry) => {
      navigationInfo = entry
    })

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

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

            Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded"));
        }

Selenium v4.9.2

    const browsingContextInspector = await BrowsingContextInspector(driver)

    await browsingContextInspector.onBrowsingContextLoaded((entry) => {
      navigationInfo = entry
    })
    const browsingContext = await BrowsingContext(driver, {
      browsingContextId: await driver.getWindowHandle(),
    })
    await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', 'complete')

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

            Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded"));
        }

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

            Assertions.assertTrue(navigationInfo.getUrl().contains("linkToAnchorOnThisPage"));
        }

Selenium v4.15.0

    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
    })

    await browsingContext.navigate('https://www.selenium.dev/selenium/web/linked_image.html#linkToAnchorOnThisPage', 'complete')

User Prompt Opened Event

Selenium v4.15

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

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

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

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

            UserPromptOpened userPromptOpened = future.get(5, TimeUnit.SECONDS);
            Assertions.assertEquals(context.getId(), userPromptOpened.getBrowsingContextId());
        }

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());
            Assertions.assertTrue(browsingContextInfo.getUrl().contains("about:blank"));
        }

Selenium v4.18.0

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

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

    const windowHandle = await driver.getWindowHandle()
    await driver.close()

2 - Input

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

  it 'sends keyboard input' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    input_field = driver.find_element(id: 'textInput')
    input_field.send_keys('Hello World')

    expect(input_field.attribute('value')).to eq('Hello World')
  end

Selenium v4.17

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

    await input.perform(browsingContextId, actions)

Selenium v4.17

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

    input_field = driver.find_element(id="textInput")
    input_field.send_keys("Hello World")

    assert input_field.get_attribute("value") == "Hello World"


@pytest.mark.driver_type("bidi")
def test_input_mouse_click(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

    button = driver.find_element(id="consoleLog")
    button.click()

    # Verify click occurred
    assert button is not None

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


    input_field = driver.find_element(id: 'textInput')
    input_field.send_keys('a')

    expect(input_field.attribute('value')).to include('a')
  end

  it 'clicks element' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    button = driver.find_element(id: 'consoleLog')
    button.click

Selenium v4.17

    await input.release(browsingContextId)

Selenium v4.17


@pytest.mark.driver_type("bidi")
def test_dispatch_keyboard_events(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

    driver.execute_script("""
        document.addEventListener('keydown', function(e) {
            console.log('Key pressed: ' + e.key);
        });
    """)

    body = driver.find_element(tag_name="body")
    body.send_keys("a")


@pytest.mark.driver_type("bidi")
def test_dispatch_mouse_events(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

3 - Log

This section contains the APIs related to logging.

Console logs

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

Selenium v4.8

    void testListenToConsoleLog() throws ExecutionException, InterruptedException, TimeoutException {
        try (LogInspector logInspector = new LogInspector(driver)) {
            CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();
            logInspector.onConsoleEntry(future::complete);

            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
            driver.findElement(By.id("consoleLog")).click();

            ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

            Assertions.assertEquals("Hello, world!", logEntry.getText());

            Assertions.assertEquals(1, logEntry.getArgs().size());
            Assertions.assertEquals("console", logEntry.getType());
            Assertions.assertEquals("log", logEntry.getMethod());
            Assertions.assertNull(logEntry.getStackTrace());
        }
    }

Selenium v4.8

  it 'adds console message handler' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'
    log_entries = []

    driver.script.add_console_message_handler { |log| log_entries << log }

    driver.find_element(id: 'consoleLog').click
    wait.until { log_entries.any? }
    expect(log_entries.first&.text).to eq 'Hello, world!'
  end
  it('test listen to console log', async function () {
    let logEntry = null
    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)

    await inspector.close()
  })

Selenium v4.8

def test_add_console_log_handler(driver):
    driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')
    log_entries = []

    driver.script.add_console_message_handler(log_entries.append)

    driver.find_element(By.ID, "consoleLog").click()
    WebDriverWait(driver, 5).until(lambda _: log_entries)
    assert log_entries[0].text == "Hello, world!"

JavaScript exceptions

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

    void testListenToJavascriptErrorLog()
            throws ExecutionException, InterruptedException, TimeoutException {
        try (LogInspector logInspector = new LogInspector(driver)) {
            CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();
            logInspector.onJavaScriptException(future::complete);

            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
            driver.findElement(By.id("jsException")).click();

            JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

            Assertions.assertEquals("Error: Not working", logEntry.getText());
            Assertions.assertEquals("javascript", logEntry.getType());
        }
    }

Selenium v4.8

  it 'adds JavaScript error handler' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'
    log_entries = []

    driver.script.add_javascript_error_handler { |error| log_entries << error }

    driver.find_element(id: 'jsException').click
    wait.until { log_entries.any? }
    expect(log_entries.first&.text).to eq 'Error: Not working'
  end
  it('test listen to javascript error log', async function () {
    let logEntry = null
    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')

    await inspector.close()
  })

Selenium v4.8

def test_add_js_exception_handler(driver):
    driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')
    log_entries = []

    driver.script.add_javascript_error_handler(log_entries.append)

    driver.find_element(By.ID, "jsException").click()
    WebDriverWait(driver, 5).until(lambda _: log_entries)
    assert log_entries[0].text == "Error: Not working"

Listen to JS Logs

Listen to all JS logs at all levels and register callbacks to process the log.

Selenium v4.8

    void testListenToJavascriptLog()
            throws ExecutionException, InterruptedException, TimeoutException {
        try (LogInspector logInspector = new LogInspector(driver)) {
            CompletableFuture<JavascriptLogEntry> future = new CompletableFuture<>();
            logInspector.onJavaScriptLog(future::complete);

            driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html");
            driver.findElement(By.id("jsException")).click();

            JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

            Assertions.assertEquals("Error: Not working", logEntry.getText());
            Assertions.assertEquals("javascript", logEntry.getType());
            Assertions.assertEquals(LogLevel.ERROR, logEntry.getLevel());
        }
    }

4 - Network

Commands

This section contains the APIs related to network commands.

Add network intercept

Selenium v4.18

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

Selenium v4.18

    wait.until { response_events.any? }
    
    expect(response_events).not_to be_empty
  end

  it 'continues network request' do

Selenium v4.18

    const intercept = await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT))
    assert.notEqual(intercept, null)
  })

Selenium v4.18

def test_intercept_network_requests(driver):
    request_events = []

    def on_request(event):
        request_events.append(event)

    driver.bidi_connection.add_network_request_listener(on_request)

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

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(request_events) > 0)

    assert len(request_events) > 0

Remove network intercept

Selenium v4.18

    void canRemoveIntercept() {
        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))
    assert.notEqual(intercept, null)

    await network.removeIntercept(intercept)
  })

Selenium v4.18

def test_intercept_network_requests(driver):
    request_events = []

    def on_request(event):
        request_events.append(event)

    driver.bidi_connection.add_network_request_listener(on_request)

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

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(request_events) > 0)

    assert len(request_events) > 0

Continue request blocked at authRequired phase with credentials

Selenium v4.18

    @Test
    void canContinueWithAuthCredentials() {
        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");
            String successMessage = "Congratulations! You must have the proper credentials.";
            WebElement elementMessage = driver.findElement(By.tagName("p"));
            Assertions.assertEquals(successMessage, elementMessage.getText());
        }
    }

Selenium v4.18

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

    await network.authRequired(async (event) => {
      await network.continueWithAuth(event.request.request, 'admin','admin')
    })
    await driver.get('https://the-internet.herokuapp.com/basic_auth')

    const successMessage = 'Congratulations! You must have the proper credentials.'

    let elementMessage = await driver.findElement(By.tagName('p')).getText()
    assert.equal(elementMessage, successMessage)
  })

Selenium v4.18

    auth_events = []

    def on_auth_required(event):
        auth_events.append(event)

    driver.bidi_connection.add_auth_required_listener(on_auth_required)

Continue request blocked at authRequired phase without credentials

Selenium v4.18

    @Test
    void canContinueWithoutAuthCredentials() {
        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");
            Alert alert = wait.until(ExpectedConditions.alertIsPresent());
            alert.dismiss();
            Assertions.assertTrue(driver.getPageSource().contains("Not authorized"));
        }
    }

Selenium v4.18

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

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

    await driver.get('https://the-internet.herokuapp.com/basic_auth')
    const alert = await driver.wait(until.alertIsPresent())
    await alert.dismiss()

    let source = await driver.getPageSource()
    assert.equal(source.includes('Not authorized'), true)
  })

Cancel request blocked at authRequired phase

Selenium v4.18

    void canCancelAuth() {
        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");
            Assertions.assertTrue(driver.getPageSource().contains("Not authorized"));
        }
    }

Selenium v4.18

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

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

    await driver.get('https://the-internet.herokuapp.com/basic_auth')
    let source = await driver.getPageSource()
    assert.equal(source.includes('Not authorized'), true)
  })
})

Fail request

Selenium v4.18

    void canFailRequest() {
        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));
            Assertions.assertThrows(TimeoutException.class, () -> driver.get("https://the-internet.herokuapp.com/basic_auth"));
            }
    }
}

Events

This section contains the APIs related to network events.

Before Request Sent

Selenium v4.15

    void canListenToBeforeRequestSentEvent()
            throws ExecutionException, InterruptedException, TimeoutException {
        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);
            String windowHandle = driver.getWindowHandle();
            Assertions.assertEquals(windowHandle, requestSent.getBrowsingContextId());
            Assertions.assertEquals("get", requestSent.getRequest().getMethod().toLowerCase());
        }
    }

Selenium v4.15

    driver.network.add_authentication_handler('test', 'test')
    driver.navigate.to url_for('basicAuth')
    expect(driver.find_element(tag_name: 'h1').text).to eq('authorized')
  end

  it 'intercepts network requests' do
    request_events = []
    
    driver.bidi_connection.add_network_request_listener do |event|
      request_events << event
    end
    
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

Selenium v4.18

  it('can listen to event before request is sent', async function () {
    let beforeRequestEvent = []
    const network = await Network(driver)
    await network.beforeRequestSent(function (event) {
      beforeRequestEvent.push(event)
    })

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

    const currentUrl = await driver.getCurrentUrl()
    const currentUrlFound = beforeRequestEvent.some(event => event.request.url.includes(currentUrl))
    assert(currentUrlFound, `${currentUrl} was not requested`)
  })

Selenium v4.15

def test_intercept_network_requests(driver):
    request_events = []

    def on_request(event):
        request_events.append(event)

    driver.bidi_connection.add_network_request_listener(on_request)

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

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(request_events) > 0)

    assert len(request_events) > 0

Response Started

Selenium v4.15

    void canListenToResponseStartedEvent()
            throws ExecutionException, InterruptedException, TimeoutException {
        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();

            Assertions.assertEquals(windowHandle, response.getBrowsingContextId());
            Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase());
            Assertions.assertEquals(200L, response.getResponseData().getStatus());
        }
    }

Selenium v4.15

    wait.until { request_events.any? }
    
    expect(request_events).not_to be_empty
  end

  it 'intercepts network responses' do
    response_events = []
    
    driver.bidi_connection.add_network_response_listener do |event|
      response_events << event
    end
    
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

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')

    assert.equal(onResponseStarted[0].request.method, 'GET')
    assert.equal(onResponseStarted[0].request.url, await driver.getCurrentUrl())
    assert.equal(onResponseStarted[0].response.url, await driver.getCurrentUrl())
  })

Selenium v4.15

def test_intercept_network_responses(driver):
    response_events = []

    def on_response(event):
        response_events.append(event)

    driver.bidi_connection.add_network_response_listener(on_response)

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

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(response_events) > 0)

    assert len(response_events) > 0

Response Completed

Selenium v4.15

    void canListenToResponseCompletedEvent()
            throws ExecutionException, InterruptedException, TimeoutException {
        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();

            Assertions.assertEquals(windowHandle, response.getBrowsingContextId());
            Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase());
            Assertions.assertEquals(200L, response.getResponseData().getStatus());
        }
    }

Selenium v4.18

  it('can subscribe to response completed', async function () {
    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')

    assert.equal(onResponseCompleted[0].request.method, 'GET')
    assert.equal(onResponseCompleted[0].request.url, await driver.getCurrentUrl())
    assert.equal(onResponseCompleted[0].response.fromCache, false)

Selenium v4.15

def test_intercept_network_requests(driver):
    request_events = []

    def on_request(event):
        request_events.append(event)

    driver.bidi_connection.add_network_request_listener(on_request)

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

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(request_events) > 0)

    assert len(request_events) > 0

Auth Required

Selenium v4.17

    void canListenToOnAuthRequiredEvent()
            throws ExecutionException, InterruptedException, TimeoutException {
        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);
            String windowHandle = driver.getWindowHandle();
            Assertions.assertEquals(windowHandle, response.getBrowsingContextId());
            Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase());
            Assertions.assertEquals(401L, response.getResponseData().getStatus());
        }
    }
}

Selenium v4.17

    end
    
    driver.navigate.to 'https://www.selenium.dev/selenium/web/iframes.html'
  end
end

Selenium v4.17

    auth_events = []

    def on_auth_required(event):
        auth_events.append(event)

    driver.bidi_connection.add_auth_required_listener(on_auth_required)

5 - Script

Commands

This section contains the APIs related to script commands.

Call function in a browsing context

Selenium v4.15

    void canCallFunction() {
        String id = driver.getWindowHandle();
        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));

            EvaluateResultSuccess successResult = (EvaluateResultSuccess) result;
            Assertions.assertEquals(64L, (Long) successResult.getResult().getValue().get());
        }

Selenium v4.15

  it 'calls a function' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    result = driver.script.call_function(
      'function(a, b) { return a + b; }',
      args: [
        {type: 'number', value: 2},
        {type: 'number', value: 3}
      ]
    )

    expect(result['type']).to eq('number')
    expect(result['value']).to eq(5)
  end

Selenium v4.9

  it('can call function', async function () {
    const id = await driver.getWindowHandle()
    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)
    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.equal(result.result.value, 64)
  })

Selenium v4.15

@pytest.mark.driver_type("bidi")
def test_call_function(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

    result = driver.script.call_function(
        "function(a, b) { return a + b; }",
        args=[{"type": "number", "value": 2}, {"type": "number", "value": 3}]
    )

    assert result.get("type") == "number"
    assert result.get("value") == 5


@pytest.mark.driver_type("bidi")
def test_evaluate_script(driver):

Call function in a sandbox

Selenium v4.15

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

        Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
        }

Selenium v4.9

    const manager = await ScriptManager(id, driver)

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

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

Call function in a realm

Selenium v4.15

    void canCallFunctionInARealm() {
        String tab = driver.getWindowHandle();
        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());

            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
        }

Selenium v4.15


    realms = driver.script.get_realms
    realm_id = realms.first['realm']

    result = driver.script.evaluate('1 + 1', realm: realm_id)

    expect(result['type']).to eq('number')
    expect(result['value']).to eq(2)
  end

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)

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

    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.equal(result.result.value, 3)
  })

Selenium v4.15

    mutation_events = []

    def on_mutation(event):
        mutation_events.append(event)

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

    script = """
    const div = document.createElement('div');
    div.textContent = 'Hello';
    document.body.appendChild(div);
    """
    driver.execute_script(script)

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(mutation_events) > 0)

Evaluate script in a browsing context

Selenium v4.15

    @Test
    void canEvaluateScript() {
        String id = driver.getWindowHandle();

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

            EvaluateResultSuccess successResult = (EvaluateResultSuccess) result;
            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
            Assertions.assertEquals(3L, (Long) successResult.getResult().getValue().get());

Selenium v4.15

  it 'evaluates a script' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    result = driver.script.evaluate('2 + 2')

    expect(result['type']).to eq('number')
    expect(result['value']).to eq(4)
  end

Selenium v4.9

    const manager = await ScriptManager(id, driver)

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

    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.equal(result.result.value, 3)
  })

Selenium v4.15

    result = driver.script.evaluate("2 + 2")

    assert result.get("type") == "number"
    assert result.get("value") == 4


@pytest.mark.driver_type("bidi")
def test_disown_value(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

Evaluate script in a sandbox

Selenium v4.15


            EvaluateResultSuccess successResult = (EvaluateResultSuccess) result;
            Assertions.assertTrue(successResult.getResult().getHandle().isPresent());
        }
    }

    @Test
    void canEvaluateInASandBox() {
        String id = driver.getWindowHandle();
        try (Script script = new Script(id, driver)) {

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')

    assert.equal(resultInSandbox.resultType, EvaluateResultType.SUCCESS)
    assert.equal(resultInSandbox.result.value, 2)
  })

  it('can evaluate in a realm', async function () {
    const firstTab = await driver.getWindowHandle()

Evaluate script in a realm

Selenium v4.15

                            id, "sandbox", "window.foo", true, Optional.empty());


            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
        }
    }

    @Test
    void canEvaluateInARealm() {
        String tab = driver.getWindowHandle();
        try (Script script = new Script(tab, driver)) {
            List<RealmInfo> realms = script.getAllRealms();

Selenium v4.15

    mutation_events = []

    driver.script.add_dom_mutation_handler do |event|
      mutation_events << event
    end

    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    driver.execute_script(<<~SCRIPT)
      const div = document.createElement('div');

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)

    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.equal(result.result.value, 3)
  })

Selenium v4.15

    mutation_events = []

    def on_mutation(event):
        mutation_events.append(event)

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

    script = """
    const div = document.createElement('div');
    div.textContent = 'Hello';
    document.body.appendChild(div);
    """
    driver.execute_script(script)

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(mutation_events) > 0)

Disown handles in a browsing context

Selenium v4.15

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

            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
        }
    }

    @Test
    void canDisownHandle() {
        String window = driver.getWindowHandle();
        try (Script script = new Script(window, driver)) {
            BrowsingContext context = new BrowsingContext(driver, window);

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

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

            getLocatedElement(driver, By.id("box0"));

            EvaluateResult result =
                    script.evaluateFunctionInBrowsingContext(
                            window, "document.querySelector('.redbox');", false, Optional.of(ResultOwnership.ROOT));
            String boxId = ((EvaluateResultSuccess)result).getResult().getHandle().get();

            script.disownBrowsingContextScript(

Selenium v4.15

  it 'disowns a value' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    result = driver.script.evaluate('({x: 1})')
    handle = result['handle']

    expect { driver.script.disown(handles: [handle]) }.not_to raise_error
  end

Selenium v4.9

    await manager.disownBrowsingContextScript(id, boxId)

    await manager.callFunctionInBrowsingContext(id, 'arg => arg.a', false).catch((error) => {
      assert(error instanceof WebDriverError)
    })
  })

  it('can disown handles in realm', async function () {
    const id = await driver.getWindowHandle()
    const manager = await ScriptManager(id, driver)

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

Selenium v4.15

    handle = result.get("handle")

    # Disown the value
    driver.script.disown(handles=[handle])
    # If no exception is raised, disown was successful


@pytest.mark.driver_type("bidi")
def test_call_function_with_element_args(driver):
    driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html")

    element = driver.find_element(By.ID, "consoleLog")

    result = driver.script.call_function(

Disown handles in a realm

Selenium v4.15

    void canDisownHandleInARealm() {
        String window = driver.getWindowHandle();
        try (Script script = new Script(window, driver)) {
            BrowsingContext context = new BrowsingContext(driver, window);

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

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

            getLocatedElement(driver, By.id("box0"));

            List<RealmInfo> realms = script.getAllRealms();
            String realmId = realms.get(0).getRealmId();

            // Retrieve the handle for the element added to DOM
            EvaluateResult result =
                    script.evaluateFunctionInBrowsingContext(
                            window, "document.querySelector('.redbox');", false, Optional.of(ResultOwnership.ROOT));
            String boxId = ((EvaluateResultSuccess)result).getResult().getHandle().get();

            LocalValue value =
                    LocalValue.remoteReference(
                            RemoteReference.Type.HANDLE, boxId);

            EvaluateResult checkHandle = script.callFunctionInBrowsingContext(
                    window,
                    "arg => arg.a",
                    false, Optional.of(List.of(value)),
                    Optional.empty(),
                    Optional.empty());

            // The handle is present in memory, else it would result in exception
            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, checkHandle.getResultType());

            // Useful to memory management in a dynamic webpage where DOM mutations happen often
            script.disownRealmScript(realmId, List.of(boxId));

            // Since the handle is now eligible for garbage collections, it is no longer available to be used.
            Assertions.assertThrows(WebDriverException.class, () -> script.callFunctionInBrowsingContext(
                    window,
                    "arg => arg.a",
                    false, Optional.of(List.of(value)),
                    Optional.empty(),
                    Optional.empty()));
        }
    }

Selenium v4.9

    await manager.disownRealmScript(realmId, boxId)

    await manager.callFunctionInBrowsingContext(id, 'arg => arg.a', false).catch((error) => {
      assert(error instanceof WebDriverError)
    })
  })

  it('can get all realms', async function () {
    const firstWindow = await driver.getWindowHandle()
    await driver.switchTo().newWindow('window')
    const secondWindow = await driver.getWindowHandle()
    const manager = await ScriptManager(firstWindow, driver)

    const realms = await manager.getAllRealms()
    assert.equal(realms.length, 2)
  })

Get all realms

Selenium v4.15

    void canGetAllRealms() {
        String firstWindow = driver.getWindowHandle();
        String secondWindow = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle();
        try (Script script = new Script(firstWindow, driver)) {
            List<RealmInfo> realms = script.getAllRealms();
            Assertions.assertEquals(2, realms.size());
        }
    }

Selenium v4.15


    realms = driver.script.get_realms
    realm_id = realms.first['realm']

    result = driver.script.evaluate('1 + 1', realm: realm_id)

    expect(result['type']).to eq('number')
    expect(result['value']).to eq(2)
  end

Selenium v4.9

    await driver.switchTo().newWindow('window')
    const secondWindow = await driver.getWindowHandle()
    const manager = await ScriptManager(firstWindow, driver)

    const realms = await manager.getRealmsByType(RealmType.WINDOW)
    assert.equal(realms.length, 2)
  })

Selenium v4.15

    mutation_events = []

    def on_mutation(event):
        mutation_events.append(event)

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

    script = """
    const div = document.createElement('div');
    div.textContent = 'Hello';
    document.body.appendChild(div);
    """
    driver.execute_script(script)

    wait = WebDriverWait(driver, 5)
    wait.until(lambda _: len(mutation_events) > 0)

Get realm by type

Selenium v4.15

    void canGetRealmByType() {
        String firstWindow = driver.getWindowHandle();
        String secondWindow = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle();
        try (Script script = new Script(firstWindow, driver)) {
            List<RealmInfo> realms = script.getRealmsByType(RealmType.WINDOW);
            Assertions.assertEquals(2, realms.size());
        }
    }

Selenium v4.9

    await driver.switchTo().newWindow('tab')
    const tabId = await driver.getWindowHandle()
    const manager = await ScriptManager(windowId, driver)

    const realms = await manager.getRealmsInBrowsingContext(tabId)

    const tabRealm = realms[0]

Get browsing context realms

Selenium v4.15

    void canGetRealmInBrowsingContext() {
        String windowId = driver.getWindowHandle();
        String tabId = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle();
        try (Script script = new Script(windowId, driver)) {
            List<RealmInfo> realms = script.getRealmsInBrowsingContext(tabId);
            Assertions.assertEquals(1, realms.size());
        }
    }

Selenium v4.9

  it('can get realm in browsing context by type', async function () {
    const windowId = await driver.getWindowHandle()
    await driver.switchTo().newWindow('tab')
    const manager = await ScriptManager(windowId, driver)

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

Get browsing context realms by type

Selenium v4.15

    void canGetRealmInBrowsingContextByType() {
        String windowId = driver.getWindowHandle();
        String tabId = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle();
        try (Script script = new Script(windowId, driver)) {
            List<RealmInfo> windowRealms =
                    script.getRealmsInBrowsingContextByType(windowId, RealmType.WINDOW);
            Assertions.assertEquals(1, windowRealms.size());
        }

Selenium v4.9

    const manager = await ScriptManager(id, driver)

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

    let logEntry = null
    const inspector = await LogInspector(driver)
    await inspector.onConsoleEntry(function (log) {

Preload a script

Selenium v4.15


    @Test
    void canAddPreloadScript() throws ExecutionException, InterruptedException, TimeoutException {
        try (Script script = new Script(driver)) {
            String id =
                    script.addPreloadScript("() => {{ console.log('{preload_script_console_text}') }}");

            Assertions.assertNotNull(id);

            try (LogInspector logInspector = new LogInspector(driver)) {
                CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();
                logInspector.onConsoleEntry(future::complete);

                driver.get("https://www.selenium.dev/selenium/blankPage");

                ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS);

                Assertions.assertEquals("{preload_script_console_text}", logEntry.getText());
            }

Selenium v4.10


  it('can add preload script to sandbox', async function () {
    const id = await driver.getWindowHandle()
    const manager = await ScriptManager(id, driver)

    await manager.addPreloadScript('() => { window.bar = 2; }', undefined, 'sandbox')

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

    let result_in_sandbox = await manager.evaluateFunctionInBrowsingContext(
      id,
      'window.bar',
      true,
      null,
      'sandbox',
    )

Remove a preloaded script

Selenium v4.15

            driver.get("https://www.selenium.dev/selenium/blankPage");

            EvaluateResult result =
                    script.evaluateFunctionInBrowsingContext(
                            driver.getWindowHandle(), "sandbox", "window.bar", true, Optional.empty());
            Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType());
        }
    }

    @Test
    void canRemovePreloadScript() throws ExecutionException, InterruptedException, TimeoutException {
        try (Script script = new Script(driver)) {
            String id =
                    script.addPreloadScript("() => {{ console.log('{preload_script_console_text}') }}");

            Assertions.assertNotNull(id);

            try (LogInspector logInspector = new LogInspector(driver)) {
                CompletableFuture<ConsoleLogEntry> future = new CompletableFuture<>();

Selenium v4.10

  })
})

Events

This section contains the APIs related to script events.

Message

Selenium v4.16

    void canListenToChannelMessage()
            throws ExecutionException, InterruptedException, TimeoutException {
        try (Script script = new Script(driver)) {
            CompletableFuture<Message> future = new CompletableFuture<>();
            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());
        }
    }

    @Test

Selenium v4.16

  it 'calls function with element arguments' do
    driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html'

    element = driver.find_element(id: 'consoleLog')

    result = driver.script.call_function(
      'function(elem) { return elem.tagName; }',
      args: [
        {type: 'HTMLElement', handle: element}
      ]
    )

    expect(result['value']).to eq('BUTTON')

Selenium v4.18


  it('can listen to channel message', async function () {
    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,
    )
    assert.equal(result.resultType, EvaluateResultType.SUCCESS)
    assert.notEqual(message, null)
    assert.equal(message.channel, 'channel_name')

Selenium v4.16

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

    element = driver.find_element(By.ID, "consoleLog")

    result = driver.script.call_function(
        "function(elem) { return elem.tagName; }",
        args=[{"type": "HTMLElement", "handle": element}]
    )

    assert result.get("value") == "BUTTON"

Realm Created

Selenium v4.16

        try (Script script = new Script(driver)) {
            CompletableFuture<RealmInfo> future = new CompletableFuture<>();
            script.onRealmCreated(future::complete);

            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
    @Disabled

Selenium v4.18


  it('can listen to realm created message', async function () {
    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

        try (Script script = new Script(driver)) {
            CompletableFuture<RealmInfo> future = new CompletableFuture<>();
            script.onRealmDestroyed(future::complete);

            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


  xit('can listen to realm destroyed message', async function () {
    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()

DOM Mutation

Selenium v4.16

    SCRIPT

    wait.until { mutation_events.any? }

    expect(mutation_events).not_to be_empty
  end
end

Selenium v4.16

  it('can listen to dom mutations', async function () {
    let message = null
    await driver.script().addDomMutationHandler((m) => {
      message = m
    })

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

    let element = driver.findElement({ id: 'reveal' })
    await element.click()
    let revealed = driver.findElement({ id: 'revealed' })
    await driver.wait(until.elementIsVisible(revealed), 5000)

    assert.strictEqual(message['attribute_name'], 'style')
    assert.strictEqual(message['current_value'], '')
    assert.strictEqual(message['old_value'], 'display:none;')
  })

Selenium v4.16