Path: blob/trunk/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py
1865 views
# Licensed to the Software Freedom Conservancy (SFC) under one1# or more contributor license agreements. See the NOTICE file2# distributed with this work for additional information3# regarding copyright ownership. The SFC licenses this file4# to you under the Apache License, Version 2.0 (the5# "License"); you may not use this file except in compliance6# with the License. You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing,11# software distributed under the License is distributed on an12# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13# KIND, either express or implied. See the License for the14# specific language governing permissions and limitations15# under the License.1617import base6418import concurrent.futures19import threading20import time2122import pytest2324from selenium.webdriver.common.bidi.browsing_context import ReadinessState25from selenium.webdriver.common.by import By26from selenium.webdriver.common.window import WindowTypes27from selenium.webdriver.support import expected_conditions as EC28from selenium.webdriver.support.ui import WebDriverWait293031def create_alert_page(driver, pages):32"""Create a page with an alert."""33url = pages.url("alerts.html")34driver.get(url)35return url363738def create_prompt_page(driver, pages):39"""Create a page with a prompt."""40url = pages.url("javascriptPage.html")41driver.get(url)42return url434445def test_browsing_context_initialized(driver):46"""Test that the browsing context module is initialized properly."""47assert driver.browsing_context is not None484950def test_create_window(driver):51"""Test creating a window."""52context_id = driver.browsing_context.create(type=WindowTypes.WINDOW)53assert context_id is not None5455# Clean up56driver.browsing_context.close(context_id)575859def test_create_window_with_reference_context(driver):60"""Test creating a window with a reference context."""61reference_context = driver.current_window_handle62context_id = driver.browsing_context.create(type=WindowTypes.WINDOW, reference_context=reference_context)63assert context_id is not None6465# Clean up66driver.browsing_context.close(context_id)676869def test_create_tab(driver):70"""Test creating a tab."""71context_id = driver.browsing_context.create(type=WindowTypes.TAB)72assert context_id is not None7374# Clean up75driver.browsing_context.close(context_id)767778def test_create_tab_with_reference_context(driver):79"""Test creating a tab with a reference context."""80reference_context = driver.current_window_handle81context_id = driver.browsing_context.create(type=WindowTypes.TAB, reference_context=reference_context)82assert context_id is not None8384# Clean up85driver.browsing_context.close(context_id)868788def test_create_context_with_all_parameters(driver):89"""Test creating a context with all parameters."""90reference_context = driver.current_window_handle91user_context = driver.browser.create_user_context()9293context_id = driver.browsing_context.create(94type=WindowTypes.WINDOW, reference_context=reference_context, user_context=user_context, background=True95)96assert context_id is not None97assert context_id != reference_context9899# Clean up100driver.browsing_context.close(context_id)101driver.browser.remove_user_context(user_context)102103104def test_navigate_to_url(driver, pages):105"""Test navigating to a URL."""106context_id = driver.browsing_context.create(type=WindowTypes.TAB)107108url = pages.url("bidi/logEntryAdded.html")109result = driver.browsing_context.navigate(context=context_id, url=url)110111assert context_id is not None112assert "/bidi/logEntryAdded.html" in result["url"]113114# Clean up115driver.browsing_context.close(context_id)116117118def test_navigate_to_url_with_readiness_state(driver, pages):119"""Test navigating to a URL with readiness state."""120context_id = driver.browsing_context.create(type=WindowTypes.TAB)121122url = pages.url("bidi/logEntryAdded.html")123result = driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)124125assert context_id is not None126assert "/bidi/logEntryAdded.html" in result["url"]127128# Clean up129driver.browsing_context.close(context_id)130131132def test_get_tree_with_child(driver, pages):133"""Test getting the tree with a child."""134reference_context = driver.current_window_handle135136url = pages.url("iframes.html")137driver.browsing_context.navigate(context=reference_context, url=url, wait=ReadinessState.COMPLETE)138139context_info_list = driver.browsing_context.get_tree(root=reference_context)140141assert len(context_info_list) == 1142info = context_info_list[0]143assert len(info.children) == 1144assert info.context == reference_context145assert "formPage.html" in info.children[0].url146147148def test_get_tree_with_depth(driver, pages):149"""Test getting the tree with depth."""150reference_context = driver.current_window_handle151152url = pages.url("iframes.html")153driver.browsing_context.navigate(context=reference_context, url=url, wait=ReadinessState.COMPLETE)154155context_info_list = driver.browsing_context.get_tree(root=reference_context, max_depth=0)156157assert len(context_info_list) == 1158info = context_info_list[0]159assert info.children is None # since depth is 0160assert info.context == reference_context161162163def test_get_all_top_level_contexts(driver):164"""Test getting all top-level contexts."""165_ = driver.current_window_handle166window_handle = driver.browsing_context.create(type=WindowTypes.WINDOW)167168context_info_list = driver.browsing_context.get_tree()169170assert len(context_info_list) == 2171172# Clean up173driver.browsing_context.close(window_handle)174175176def test_close_window(driver):177"""Test closing a window."""178window1 = driver.browsing_context.create(type=WindowTypes.WINDOW)179window2 = driver.browsing_context.create(type=WindowTypes.WINDOW)180181driver.browsing_context.close(window2)182183with pytest.raises(Exception):184driver.browsing_context.get_tree(root=window2)185186# Clean up187driver.browsing_context.close(window1)188189190def test_close_tab(driver):191"""Test closing a tab."""192tab1 = driver.browsing_context.create(type=WindowTypes.TAB)193tab2 = driver.browsing_context.create(type=WindowTypes.TAB)194195driver.browsing_context.close(tab2)196197with pytest.raises(Exception):198driver.browsing_context.get_tree(root=tab2)199200# Clean up201driver.browsing_context.close(tab1)202203204def test_activate_browsing_context(driver):205"""Test activating a browsing context."""206window1 = driver.current_window_handle207# 2nd window is focused208window2 = driver.browsing_context.create(type=WindowTypes.WINDOW)209210# We did not switch the driver, so we are running the script to check focus on 1st window211assert not driver.execute_script("return document.hasFocus();")212213driver.browsing_context.activate(window1)214215assert driver.execute_script("return document.hasFocus();")216217# Clean up218driver.browsing_context.close(window2)219220221def test_reload_browsing_context(driver, pages):222"""Test reloading a browsing context."""223context_id = driver.browsing_context.create(type=WindowTypes.TAB)224225url = pages.url("bidi/logEntryAdded.html")226driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)227228reload_info = driver.browsing_context.reload(context=context_id)229230assert "/bidi/logEntryAdded.html" in reload_info["url"]231232# Clean up233driver.browsing_context.close(context_id)234235236def test_reload_with_readiness_state(driver, pages):237"""Test reloading with readiness state."""238context_id = driver.browsing_context.create(type=WindowTypes.TAB)239240url = pages.url("bidi/logEntryAdded.html")241driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)242243reload_info = driver.browsing_context.reload(context=context_id, wait=ReadinessState.COMPLETE)244245assert reload_info["navigation"] is not None246assert "/bidi/logEntryAdded.html" in reload_info["url"]247248# Clean up249driver.browsing_context.close(context_id)250251252def test_handle_user_prompt(driver, pages):253"""Test handling a user prompt."""254context_id = driver.current_window_handle255256create_alert_page(driver, pages)257258driver.find_element(By.ID, "alert").click()259WebDriverWait(driver, 5).until(EC.alert_is_present())260261driver.browsing_context.handle_user_prompt(context=context_id)262263assert "Alerts" in driver.title264265266def test_accept_user_prompt(driver, pages):267"""Test accepting a user prompt."""268context_id = driver.current_window_handle269270create_alert_page(driver, pages)271272driver.find_element(By.ID, "alert").click()273WebDriverWait(driver, 5).until(EC.alert_is_present())274275driver.browsing_context.handle_user_prompt(context=context_id, accept=True)276277assert "Alerts" in driver.title278279280def test_dismiss_user_prompt(driver, pages):281"""Test dismissing a user prompt."""282context_id = driver.current_window_handle283284create_alert_page(driver, pages)285286driver.find_element(By.ID, "alert").click()287WebDriverWait(driver, 5).until(EC.alert_is_present())288289driver.browsing_context.handle_user_prompt(context=context_id, accept=False)290291assert "Alerts" in driver.title292293294def test_pass_user_text_to_prompt(driver, pages):295"""Test passing user text to a prompt."""296context_id = driver.current_window_handle297298create_prompt_page(driver, pages)299300driver.execute_script("prompt('Enter something')")301WebDriverWait(driver, 5).until(EC.alert_is_present())302303user_text = "Selenium automates browsers"304305driver.browsing_context.handle_user_prompt(context=context_id, user_text=user_text)306307# Check if the text was entered (this is browser-dependent)308309310def test_capture_screenshot(driver, pages):311"""Test capturing a screenshot."""312context_id = driver.current_window_handle313314driver.get(pages.url("simpleTest.html"))315316screenshot = driver.browsing_context.capture_screenshot(context=context_id)317318# Verify it's a valid base64 string319try:320base64.b64decode(screenshot)321is_valid = True322except Exception:323is_valid = False324325assert is_valid326assert len(screenshot) > 0327328329def test_capture_screenshot_with_parameters(driver, pages):330"""Test capturing a screenshot with parameters."""331context_id = driver.current_window_handle332333driver.get(pages.url("coordinates_tests/simple_page.html"))334element = driver.find_element(By.ID, "box")335336rect = element.rect337338clip = {"type": "box", "x": rect["x"], "y": rect["y"], "width": 5, "height": 5}339340screenshot = driver.browsing_context.capture_screenshot(context=context_id, origin="document", clip=clip)341342assert len(screenshot) > 0343344345def test_set_viewport(driver, pages):346"""Test setting the viewport."""347context_id = driver.current_window_handle348driver.get(pages.url("formPage.html"))349350driver.browsing_context.set_viewport(context=context_id, viewport={"width": 250, "height": 300})351352viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")353354assert viewport_size[0] == 250355assert viewport_size[1] == 300356357358def test_set_viewport_with_device_pixel_ratio(driver, pages):359"""Test setting the viewport with device pixel ratio."""360context_id = driver.current_window_handle361driver.get(pages.url("formPage.html"))362363driver.browsing_context.set_viewport(364context=context_id, viewport={"width": 250, "height": 300}, device_pixel_ratio=5365)366367viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")368369assert viewport_size[0] == 250370assert viewport_size[1] == 300371372device_pixel_ratio = driver.execute_script("return window.devicePixelRatio")373374assert device_pixel_ratio == 5375376377def test_print_page(driver, pages):378"""Test printing a page."""379context_id = driver.current_window_handle380381driver.get(pages.url("formPage.html"))382383print_result = driver.browsing_context.print(context=context_id)384385assert len(print_result) > 0386# Check if it's a valid PDF (starts with JVBERi which is the base64 encoding of %PDF)387assert "JVBERi" in print_result388389390def test_navigate_back_in_browser_history(driver, pages):391"""Test navigating back in the browser history."""392context_id = driver.current_window_handle393driver.browsing_context.navigate(context=context_id, url=pages.url("formPage.html"), wait=ReadinessState.COMPLETE)394395# Navigate to another page by submitting a form396driver.find_element(By.ID, "imageButton").submit()397WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))398399driver.browsing_context.traverse_history(context=context_id, delta=-1)400WebDriverWait(driver, 5).until(EC.title_is("We Leave From Here"))401402403def test_navigate_forward_in_browser_history(driver, pages):404"""Test navigating forward in the browser history."""405context_id = driver.current_window_handle406driver.browsing_context.navigate(context=context_id, url=pages.url("formPage.html"), wait=ReadinessState.COMPLETE)407408# Navigate to another page by submitting a form409driver.find_element(By.ID, "imageButton").submit()410WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))411412# Go back413driver.browsing_context.traverse_history(context=context_id, delta=-1)414WebDriverWait(driver, 5).until(EC.title_is("We Leave From Here"))415416# Go forward417driver.browsing_context.traverse_history(context=context_id, delta=1)418WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))419420421# Tests for locate nodes422def test_locate_nodes(driver, pages):423"""Test locating nodes with CSS selector."""424context_id = driver.current_window_handle425426driver.get(pages.url("xhtmlTest.html"))427428elements = driver.browsing_context.locate_nodes(context=context_id, locator={"type": "css", "value": "div"})429430assert len(elements) > 0431432433def test_locate_nodes_with_css_locator(driver, pages):434"""Test locating nodes with specific CSS selector."""435context_id = driver.current_window_handle436437driver.get(pages.url("xhtmlTest.html"))438439elements = driver.browsing_context.locate_nodes(440context=context_id, locator={"type": "css", "value": "div.extraDiv, div.content"}, max_node_count=1441)442443assert len(elements) >= 1444445value = elements[0]446assert value["type"] == "node"447assert "value" in value448assert "localName" in value["value"]449assert value["value"]["localName"] == "div"450assert "attributes" in value["value"]451assert "class" in value["value"]["attributes"]452assert value["value"]["attributes"]["class"] == "content"453454455def test_locate_nodes_with_xpath_locator(driver, pages):456"""Test locating nodes with XPath selector."""457context_id = driver.current_window_handle458459driver.get(pages.url("xhtmlTest.html"))460461elements = driver.browsing_context.locate_nodes(462context=context_id, locator={"type": "xpath", "value": "/html/body/div[2]"}, max_node_count=1463)464465assert len(elements) >= 1466467value = elements[0]468assert value["type"] == "node"469assert "value" in value470assert "localName" in value["value"]471assert value["value"]["localName"] == "div"472assert "attributes" in value["value"]473assert "class" in value["value"]["attributes"]474assert value["value"]["attributes"]["class"] == "content"475476477@pytest.mark.xfail_firefox478def test_locate_nodes_with_inner_text(driver, pages):479"""Test locating nodes with innerText selector."""480context_id = driver.current_window_handle481482driver.get(pages.url("xhtmlTest.html"))483484elements = driver.browsing_context.locate_nodes(485context=context_id, locator={"type": "innerText", "value": "Spaced out"}, max_node_count=1486)487488assert len(elements) >= 1489490value = elements[0]491assert value["type"] == "node"492assert "value" in value493494495def test_locate_nodes_with_max_node_count(driver, pages):496"""Test locating nodes with maximum node count."""497context_id = driver.current_window_handle498499driver.get(pages.url("xhtmlTest.html"))500501elements = driver.browsing_context.locate_nodes(502context=context_id, locator={"type": "css", "value": "div"}, max_node_count=4503)504505assert len(elements) == 4506507508def test_locate_nodes_given_start_nodes(driver, pages):509"""Test locating nodes with start nodes."""510context_id = driver.current_window_handle511512driver.get(pages.url("formPage.html"))513514form_elements = driver.browsing_context.locate_nodes(515context=context_id, locator={"type": "css", "value": "form[name='login']"}516)517518assert len(form_elements) == 1519520form_shared_id = form_elements[0]["sharedId"]521522elements = driver.browsing_context.locate_nodes(523context=context_id,524locator={"type": "css", "value": "input"},525start_nodes=[{"sharedId": form_shared_id}],526max_node_count=50,527)528# The login form should have 3 input elements (email, age, and submit button)529assert len(elements) == 3530531532# Tests for event handlers533534535def test_add_event_handler_context_created(driver):536"""Test adding event handler for context_created event."""537events_received = []538539def on_context_created(info):540events_received.append(info)541542callback_id = driver.browsing_context.add_event_handler("context_created", on_context_created)543assert callback_id is not None544545# Create a new context to trigger the event546context_id = driver.browsing_context.create(type=WindowTypes.TAB)547548# Verify the event was received (might be > 1 since default context is also included)549assert len(events_received) >= 1550assert events_received[0].context == context_id or events_received[1].context == context_id551552driver.browsing_context.close(context_id)553driver.browsing_context.remove_event_handler("context_created", callback_id)554555556def test_add_event_handler_context_destroyed(driver):557"""Test adding event handler for context_destroyed event."""558events_received = []559560def on_context_destroyed(info):561events_received.append(info)562563callback_id = driver.browsing_context.add_event_handler("context_destroyed", on_context_destroyed)564assert callback_id is not None565566# Create and then close a context to trigger the event567context_id = driver.browsing_context.create(type=WindowTypes.TAB)568driver.browsing_context.close(context_id)569570assert len(events_received) == 1571assert events_received[0].context == context_id572573driver.browsing_context.remove_event_handler("context_destroyed", callback_id)574575576def test_add_event_handler_navigation_committed(driver, pages):577"""Test adding event handler for navigation_committed event."""578events_received = []579580def on_navigation_committed(info):581events_received.append(info)582583callback_id = driver.browsing_context.add_event_handler("navigation_committed", on_navigation_committed)584assert callback_id is not None585586# Navigate to trigger the event587context_id = driver.current_window_handle588url = pages.url("simpleTest.html")589driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)590591assert len(events_received) >= 1592assert any(url in event.url for event in events_received)593594driver.browsing_context.remove_event_handler("navigation_committed", callback_id)595596597def test_add_event_handler_dom_content_loaded(driver, pages):598"""Test adding event handler for dom_content_loaded event."""599events_received = []600601def on_dom_content_loaded(info):602events_received.append(info)603604callback_id = driver.browsing_context.add_event_handler("dom_content_loaded", on_dom_content_loaded)605assert callback_id is not None606607# Navigate to trigger the event608context_id = driver.current_window_handle609url = pages.url("simpleTest.html")610driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)611612assert len(events_received) == 1613assert any("simpleTest" in event.url for event in events_received)614615driver.browsing_context.remove_event_handler("dom_content_loaded", callback_id)616617618def test_add_event_handler_load(driver, pages):619"""Test adding event handler for load event."""620events_received = []621622def on_load(info):623events_received.append(info)624625callback_id = driver.browsing_context.add_event_handler("load", on_load)626assert callback_id is not None627628context_id = driver.current_window_handle629url = pages.url("simpleTest.html")630driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)631632assert len(events_received) == 1633assert any("simpleTest" in event.url for event in events_received)634635driver.browsing_context.remove_event_handler("load", callback_id)636637638def test_add_event_handler_navigation_started(driver, pages):639"""Test adding event handler for navigation_started event."""640events_received = []641642def on_navigation_started(info):643events_received.append(info)644645callback_id = driver.browsing_context.add_event_handler("navigation_started", on_navigation_started)646assert callback_id is not None647648context_id = driver.current_window_handle649url = pages.url("simpleTest.html")650driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)651652assert len(events_received) == 1653assert any("simpleTest" in event.url for event in events_received)654655driver.browsing_context.remove_event_handler("navigation_started", callback_id)656657658def test_add_event_handler_fragment_navigated(driver, pages):659"""Test adding event handler for fragment_navigated event."""660events_received = []661662def on_fragment_navigated(info):663events_received.append(info)664665callback_id = driver.browsing_context.add_event_handler("fragment_navigated", on_fragment_navigated)666assert callback_id is not None667668# First navigate to a page669context_id = driver.current_window_handle670url = pages.url("linked_image.html")671driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)672673# Then navigate to the same page with a fragment to trigger the event674fragment_url = url + "#link"675driver.browsing_context.navigate(context=context_id, url=fragment_url, wait=ReadinessState.COMPLETE)676677assert len(events_received) == 1678assert any("link" in event.url for event in events_received)679680driver.browsing_context.remove_event_handler("fragment_navigated", callback_id)681682683@pytest.mark.xfail_firefox684def test_add_event_handler_navigation_failed(driver):685"""Test adding event handler for navigation_failed event."""686events_received = []687688def on_navigation_failed(info):689events_received.append(info)690691callback_id = driver.browsing_context.add_event_handler("navigation_failed", on_navigation_failed)692assert callback_id is not None693694# Navigate to an invalid URL to trigger the event695context_id = driver.current_window_handle696try:697driver.browsing_context.navigate(context=context_id, url="http://invalid-domain-that-does-not-exist.test/")698except Exception:699# Expect an exception due to navigation failure700pass701702assert len(events_received) == 1703assert events_received[0].url == "http://invalid-domain-that-does-not-exist.test/"704assert events_received[0].context == context_id705706driver.browsing_context.remove_event_handler("navigation_failed", callback_id)707708709def test_add_event_handler_user_prompt_opened(driver, pages):710"""Test adding event handler for user_prompt_opened event."""711events_received = []712713def on_user_prompt_opened(info):714events_received.append(info)715716callback_id = driver.browsing_context.add_event_handler("user_prompt_opened", on_user_prompt_opened)717assert callback_id is not None718719# Create an alert to trigger the event720create_alert_page(driver, pages)721driver.find_element(By.ID, "alert").click()722WebDriverWait(driver, 5).until(EC.alert_is_present())723724assert len(events_received) == 1725assert events_received[0].type == "alert"726assert events_received[0].message == "cheese"727728# Clean up the alert729driver.browsing_context.handle_user_prompt(context=driver.current_window_handle)730driver.browsing_context.remove_event_handler("user_prompt_opened", callback_id)731732733def test_add_event_handler_user_prompt_closed(driver, pages):734"""Test adding event handler for user_prompt_closed event."""735events_received = []736737def on_user_prompt_closed(info):738events_received.append(info)739740callback_id = driver.browsing_context.add_event_handler("user_prompt_closed", on_user_prompt_closed)741assert callback_id is not None742743create_prompt_page(driver, pages)744driver.execute_script("prompt('Enter something')")745WebDriverWait(driver, 5).until(EC.alert_is_present())746747driver.browsing_context.handle_user_prompt(748context=driver.current_window_handle, accept=True, user_text="test input"749)750751assert len(events_received) == 1752assert events_received[0].accepted is True753assert events_received[0].user_text == "test input"754755driver.browsing_context.remove_event_handler("user_prompt_closed", callback_id)756757758def test_add_event_handler_history_updated(driver, pages):759"""Test adding event handler for history_updated event."""760events_received = []761762def on_history_updated(info):763events_received.append(info)764765callback_id = driver.browsing_context.add_event_handler("history_updated", on_history_updated)766assert callback_id is not None767768context_id = driver.current_window_handle769url = pages.url("simpleTest.html")770driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)771772# Use history.pushState to trigger history updated event773driver.script.execute("() => { history.pushState({}, '', '/new-path'); }")774WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0)775776assert len(events_received) >= 1777assert "/new-path" in events_received[0].url778assert events_received[0].context == context_id779780driver.browsing_context.remove_event_handler("history_updated", callback_id)781782783@pytest.mark.xfail_firefox784def test_add_event_handler_download_will_begin(driver, pages):785"""Test adding event handler for download_will_begin event."""786events_received = []787788def on_download_will_begin(info):789events_received.append(info)790791callback_id = driver.browsing_context.add_event_handler("download_will_begin", on_download_will_begin)792assert callback_id is not None793794# click on a download link to trigger the event795context_id = driver.current_window_handle796url = pages.url("downloads/download.html")797driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)798799download_xpath_file_1_txt = '//*[@id="file-1"]'800driver.find_element(By.XPATH, download_xpath_file_1_txt).click()801WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0)802803assert len(events_received) == 1804assert events_received[0].suggested_filename == "file_1.txt"805806driver.browsing_context.remove_event_handler("download_will_begin", callback_id)807808809def test_add_event_handler_with_specific_contexts(driver):810"""Test adding event handler with specific browsing contexts."""811events_received = []812813def on_context_created(info):814events_received.append(info)815816context_id = driver.browsing_context.create(type=WindowTypes.TAB)817818# Add event handler for specific context819callback_id = driver.browsing_context.add_event_handler(820"context_created", on_context_created, contexts=[context_id]821)822assert callback_id is not None823824# Create another context (should trigger event)825new_context_id = driver.browsing_context.create(type=WindowTypes.TAB)826827assert len(events_received) >= 1828829driver.browsing_context.close(context_id)830driver.browsing_context.close(new_context_id)831driver.browsing_context.remove_event_handler("context_created", callback_id)832833834def test_remove_event_handler(driver):835"""Test removing event handler."""836events_received = []837838def on_context_created(info):839events_received.append(info)840841callback_id = driver.browsing_context.add_event_handler("context_created", on_context_created)842843# Create a context to trigger the event844context_id_1 = driver.browsing_context.create(type=WindowTypes.TAB)845846initial_events = len(events_received)847848# Remove the event handler849driver.browsing_context.remove_event_handler("context_created", callback_id)850851# Create another context (should not trigger event after removal)852context_id_2 = driver.browsing_context.create(type=WindowTypes.TAB)853854# Verify no new events were received after removal855assert len(events_received) == initial_events856857driver.browsing_context.close(context_id_1)858driver.browsing_context.close(context_id_2)859860861def test_multiple_event_handlers_same_event(driver):862"""Test adding multiple event handlers for the same event."""863events_received_1 = []864events_received_2 = []865866def on_context_created_1(info):867events_received_1.append(info)868869def on_context_created_2(info):870events_received_2.append(info)871872# Add multiple event handlers for the same event873callback_id_1 = driver.browsing_context.add_event_handler("context_created", on_context_created_1)874callback_id_2 = driver.browsing_context.add_event_handler("context_created", on_context_created_2)875876# Create a context to trigger both handlers877context_id = driver.browsing_context.create(type=WindowTypes.TAB)878879# Verify both handlers received the event880assert len(events_received_1) >= 1881assert len(events_received_2) >= 1882# Check any of the events has the required context ID883assert any(event.context == context_id for event in events_received_1)884assert any(event.context == context_id for event in events_received_2)885886driver.browsing_context.close(context_id)887driver.browsing_context.remove_event_handler("context_created", callback_id_1)888driver.browsing_context.remove_event_handler("context_created", callback_id_2)889890891def test_remove_specific_event_handler_multiple_handlers(driver):892"""Test removing a specific event handler when multiple handlers exist."""893events_received_1 = []894events_received_2 = []895896def on_context_created_1(info):897events_received_1.append(info)898899def on_context_created_2(info):900events_received_2.append(info)901902# Add multiple event handlers903callback_id_1 = driver.browsing_context.add_event_handler("context_created", on_context_created_1)904callback_id_2 = driver.browsing_context.add_event_handler("context_created", on_context_created_2)905906# Create a context to trigger both handlers907context_id_1 = driver.browsing_context.create(type=WindowTypes.TAB)908909# Verify both handlers received the event910assert len(events_received_1) >= 1911assert len(events_received_2) >= 1912913# store the initial event counts914initial_count_1 = len(events_received_1)915initial_count_2 = len(events_received_2)916917# Remove only the first handler918driver.browsing_context.remove_event_handler("context_created", callback_id_1)919920# Create another context921context_id_2 = driver.browsing_context.create(type=WindowTypes.TAB)922923# Verify only the second handler received the new event924assert len(events_received_1) == initial_count_1 # No new events925assert len(events_received_2) == initial_count_2 + 1 # 1 new event926927driver.browsing_context.close(context_id_1)928driver.browsing_context.close(context_id_2)929driver.browsing_context.remove_event_handler("context_created", callback_id_2)930931932class _EventHandlerTestHelper:933def __init__(self, driver):934self.driver = driver935self.events_received = []936self.context_counts = {}937self.event_type_counts = {}938self.processing_times = []939self.consistency_errors = []940self.thread_errors = []941self.callback_ids = []942self.data_lock = threading.Lock()943self.registration_complete = threading.Event()944945def make_callback(self):946def callback(info):947start_time = time.time()948time.sleep(0.02) # Simulate race window949950with self.data_lock:951initial_event_count = len(self.events_received)952953self.events_received.append(info)954955context_id = info.context956self.context_counts.setdefault(context_id, 0)957self.context_counts[context_id] += 1958959event_type = info.__class__.__name__960self.event_type_counts.setdefault(event_type, 0)961self.event_type_counts[event_type] += 1962963processing_time = time.time() - start_time964self.processing_times.append(processing_time)965966final_event_count = len(self.events_received)967final_context_total = sum(self.context_counts.values())968final_type_total = sum(self.event_type_counts.values())969final_processing_count = len(self.processing_times)970971expected_count = initial_event_count + 1972if not (973final_event_count974== final_context_total975== final_type_total976== final_processing_count977== expected_count978):979self.consistency_errors.append("Data consistency error")980981return callback982983def register_handler(self, thread_id):984try:985callback = self.make_callback()986callback_id = self.driver.browsing_context.add_event_handler("context_created", callback)987with self.data_lock:988self.callback_ids.append(callback_id)989if len(self.callback_ids) == 5:990self.registration_complete.set()991return callback_id992except Exception as e:993with self.data_lock:994self.thread_errors.append(f"Thread {thread_id}: Registration failed: {e}")995return None996997def remove_handler(self, callback_id, thread_id):998try:999self.driver.browsing_context.remove_event_handler("context_created", callback_id)1000except Exception as e:1001with self.data_lock:1002self.thread_errors.append(f"Thread {thread_id}: Removal failed: {e}")100310041005def test_concurrent_event_handler_registration(driver):1006helper = _EventHandlerTestHelper(driver)10071008with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:1009futures = [executor.submit(helper.register_handler, f"reg-{i}") for i in range(5)]1010for future in futures:1011future.result(timeout=15)10121013helper.registration_complete.wait(timeout=5)1014assert len(helper.callback_ids) == 5, f"Expected 5 handlers, got {len(helper.callback_ids)}"1015assert not helper.thread_errors, "Errors during registration: \n" + "\n".join(helper.thread_errors)101610171018def test_event_callback_data_consistency(driver):1019helper = _EventHandlerTestHelper(driver)10201021for i in range(5):1022helper.register_handler(f"reg-{i}")10231024test_contexts = []1025for _ in range(3):1026context = driver.browsing_context.create(type=WindowTypes.TAB)1027test_contexts.append(context)10281029for ctx in test_contexts:1030driver.browsing_context.close(ctx)10311032with helper.data_lock:1033assert not helper.consistency_errors, "Consistency errors: " + str(helper.consistency_errors)1034assert len(helper.events_received) > 0, "No events received"1035assert len(helper.events_received) == sum(helper.context_counts.values())1036assert len(helper.events_received) == sum(helper.event_type_counts.values())1037assert len(helper.events_received) == len(helper.processing_times)103810391040def test_concurrent_event_handler_removal(driver):1041helper = _EventHandlerTestHelper(driver)10421043for i in range(5):1044helper.register_handler(f"reg-{i}")10451046with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:1047futures = [1048executor.submit(helper.remove_handler, callback_id, f"rem-{i}")1049for i, callback_id in enumerate(helper.callback_ids)1050]1051for future in futures:1052future.result(timeout=15)10531054assert not helper.thread_errors, "Errors during removal: \n" + "\n".join(helper.thread_errors)105510561057def test_no_event_after_handler_removal(driver):1058helper = _EventHandlerTestHelper(driver)10591060for i in range(5):1061helper.register_handler(f"reg-{i}")10621063context = driver.browsing_context.create(type=WindowTypes.TAB)1064driver.browsing_context.close(context)10651066events_before = len(helper.events_received)10671068for i, callback_id in enumerate(helper.callback_ids):1069helper.remove_handler(callback_id, f"rem-{i}")10701071post_context = driver.browsing_context.create(type=WindowTypes.TAB)1072driver.browsing_context.close(post_context)10731074with helper.data_lock:1075new_events = len(helper.events_received) - events_before10761077assert new_events == 0, f"Expected 0 new events after removal, got {new_events}"107810791080