Path: blob/trunk/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py
4078 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"))349350try:351driver.browsing_context.set_viewport(context=context_id, viewport={"width": 251, "height": 301})352353viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")354355assert viewport_size[0] == 251356assert viewport_size[1] == 301357finally:358driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None)359360361def test_set_viewport_with_device_pixel_ratio(driver, pages):362"""Test setting the viewport with device pixel ratio."""363context_id = driver.current_window_handle364driver.get(pages.url("formPage.html"))365366try:367driver.browsing_context.set_viewport(368context=context_id, viewport={"width": 252, "height": 302}, device_pixel_ratio=5369)370371viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")372373assert viewport_size[0] == 252374assert viewport_size[1] == 302375376device_pixel_ratio = driver.execute_script("return window.devicePixelRatio")377378assert device_pixel_ratio == 5379finally:380driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None)381382383def test_set_viewport_with_no_args_doesnt_change_values(driver, pages):384"""Test setting the viewport with no args doesn't change viewport or device pixel ratio."""385context_id = driver.current_window_handle386driver.get(pages.url("formPage.html"))387388try:389driver.browsing_context.set_viewport(390context=context_id, viewport={"width": 253, "height": 303}, device_pixel_ratio=6391)392393driver.browsing_context.set_viewport(context=context_id)394395viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")396397assert viewport_size[0] == 253398assert viewport_size[1] == 303399400device_pixel_ratio = driver.execute_script("return window.devicePixelRatio")401402assert device_pixel_ratio == 6403finally:404driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None)405406407def test_set_viewport_back_to_default(driver, pages):408"""Test resetting the viewport and device pixel ratio to defaults."""409context_id = driver.current_window_handle410driver.get(pages.url("formPage.html"))411412default_viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")413default_device_pixel_ratio = driver.execute_script("return window.devicePixelRatio")414415try:416driver.browsing_context.set_viewport(417context=context_id, viewport={"width": 254, "height": 304}, device_pixel_ratio=10418)419420driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None)421422viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")423device_pixel_ratio = driver.execute_script("return window.devicePixelRatio")424425assert viewport_size[0] == default_viewport_size[0]426assert viewport_size[1] == default_viewport_size[1]427assert device_pixel_ratio == default_device_pixel_ratio428finally:429driver.browsing_context.set_viewport(context=context_id, viewport=None, device_pixel_ratio=None)430431432def test_print_page(driver, pages):433"""Test printing a page."""434context_id = driver.current_window_handle435436driver.get(pages.url("formPage.html"))437438print_result = driver.browsing_context.print(context=context_id)439440assert len(print_result) > 0441# Check if it's a valid PDF (starts with JVBERi which is the base64 encoding of %PDF)442assert "JVBERi" in print_result443444445def test_navigate_back_in_browser_history(driver, pages):446"""Test navigating back in the browser history."""447context_id = driver.current_window_handle448driver.browsing_context.navigate(context=context_id, url=pages.url("formPage.html"), wait=ReadinessState.COMPLETE)449450# Navigate to another page by submitting a form451driver.find_element(By.ID, "imageButton").submit()452WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))453454driver.browsing_context.traverse_history(context=context_id, delta=-1)455WebDriverWait(driver, 5).until(EC.title_is("We Leave From Here"))456457458def test_navigate_forward_in_browser_history(driver, pages):459"""Test navigating forward in the browser history."""460context_id = driver.current_window_handle461driver.browsing_context.navigate(context=context_id, url=pages.url("formPage.html"), wait=ReadinessState.COMPLETE)462463# Navigate to another page by submitting a form464driver.find_element(By.ID, "imageButton").submit()465WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))466467# Go back468driver.browsing_context.traverse_history(context=context_id, delta=-1)469WebDriverWait(driver, 5).until(EC.title_is("We Leave From Here"))470471# Go forward472driver.browsing_context.traverse_history(context=context_id, delta=1)473WebDriverWait(driver, 5).until(EC.title_is("We Arrive Here"))474475476# Tests for locate nodes477def test_locate_nodes(driver, pages):478"""Test locating nodes with CSS selector."""479context_id = driver.current_window_handle480481driver.get(pages.url("xhtmlTest.html"))482483elements = driver.browsing_context.locate_nodes(context=context_id, locator={"type": "css", "value": "div"})484485assert len(elements) > 0486487488def test_locate_nodes_with_css_locator(driver, pages):489"""Test locating nodes with specific CSS selector."""490context_id = driver.current_window_handle491492driver.get(pages.url("xhtmlTest.html"))493494elements = driver.browsing_context.locate_nodes(495context=context_id, locator={"type": "css", "value": "div.extraDiv, div.content"}, max_node_count=1496)497498assert len(elements) >= 1499500value = elements[0]501assert value["type"] == "node"502assert "value" in value503assert "localName" in value["value"]504assert value["value"]["localName"] == "div"505assert "attributes" in value["value"]506assert "class" in value["value"]["attributes"]507assert value["value"]["attributes"]["class"] == "content"508509510def test_locate_nodes_with_xpath_locator(driver, pages):511"""Test locating nodes with XPath selector."""512context_id = driver.current_window_handle513514driver.get(pages.url("xhtmlTest.html"))515516elements = driver.browsing_context.locate_nodes(517context=context_id, locator={"type": "xpath", "value": "/html/body/div[2]"}, max_node_count=1518)519520assert len(elements) >= 1521522value = elements[0]523assert value["type"] == "node"524assert "value" in value525assert "localName" in value["value"]526assert value["value"]["localName"] == "div"527assert "attributes" in value["value"]528assert "class" in value["value"]["attributes"]529assert value["value"]["attributes"]["class"] == "content"530531532@pytest.mark.xfail_firefox533def test_locate_nodes_with_inner_text(driver, pages):534"""Test locating nodes with innerText selector."""535context_id = driver.current_window_handle536537driver.get(pages.url("xhtmlTest.html"))538539elements = driver.browsing_context.locate_nodes(540context=context_id, locator={"type": "innerText", "value": "Spaced out"}, max_node_count=1541)542543assert len(elements) >= 1544545value = elements[0]546assert value["type"] == "node"547assert "value" in value548549550def test_locate_nodes_with_max_node_count(driver, pages):551"""Test locating nodes with maximum node count."""552context_id = driver.current_window_handle553554driver.get(pages.url("xhtmlTest.html"))555556elements = driver.browsing_context.locate_nodes(557context=context_id, locator={"type": "css", "value": "div"}, max_node_count=4558)559560assert len(elements) == 4561562563def test_locate_nodes_given_start_nodes(driver, pages):564"""Test locating nodes with start nodes."""565context_id = driver.current_window_handle566567driver.get(pages.url("formPage.html"))568569form_elements = driver.browsing_context.locate_nodes(570context=context_id, locator={"type": "css", "value": "form[name='login']"}571)572573assert len(form_elements) == 1574575form_shared_id = form_elements[0]["sharedId"]576577elements = driver.browsing_context.locate_nodes(578context=context_id,579locator={"type": "css", "value": "input"},580start_nodes=[{"sharedId": form_shared_id}],581max_node_count=50,582)583# The login form should have 3 input elements (email, age, and submit button)584assert len(elements) == 3585586587# Tests for event handlers588589590def test_add_event_handler_context_created(driver):591"""Test adding event handler for context_created event."""592events_received = []593594def on_context_created(info):595events_received.append(info)596597callback_id = driver.browsing_context.add_event_handler("context_created", on_context_created)598assert callback_id is not None599600# Create a new context to trigger the event601context_id = driver.browsing_context.create(type=WindowTypes.TAB)602603# Verify the event was received (might be > 1 since default context is also included)604assert len(events_received) >= 1605assert events_received[0].context == context_id or events_received[1].context == context_id606607driver.browsing_context.close(context_id)608driver.browsing_context.remove_event_handler("context_created", callback_id)609610611def test_add_event_handler_context_destroyed(driver):612"""Test adding event handler for context_destroyed event."""613events_received = []614615def on_context_destroyed(info):616events_received.append(info)617618callback_id = driver.browsing_context.add_event_handler("context_destroyed", on_context_destroyed)619assert callback_id is not None620621# Create and then close a context to trigger the event622context_id = driver.browsing_context.create(type=WindowTypes.TAB)623driver.browsing_context.close(context_id)624625assert len(events_received) == 1626assert events_received[0].context == context_id627628driver.browsing_context.remove_event_handler("context_destroyed", callback_id)629630631def test_add_event_handler_navigation_committed(driver, pages):632"""Test adding event handler for navigation_committed event."""633events_received = []634635def on_navigation_committed(info):636events_received.append(info)637638callback_id = driver.browsing_context.add_event_handler("navigation_committed", on_navigation_committed)639assert callback_id is not None640641# Navigate to trigger the event642context_id = driver.current_window_handle643url = pages.url("simpleTest.html")644driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)645646assert len(events_received) >= 1647assert any(url in event.url for event in events_received)648649driver.browsing_context.remove_event_handler("navigation_committed", callback_id)650651652def test_add_event_handler_dom_content_loaded(driver, pages):653"""Test adding event handler for dom_content_loaded event."""654events_received = []655656def on_dom_content_loaded(info):657events_received.append(info)658659callback_id = driver.browsing_context.add_event_handler("dom_content_loaded", on_dom_content_loaded)660assert callback_id is not None661662# Navigate to trigger the event663context_id = driver.current_window_handle664url = pages.url("simpleTest.html")665driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)666667assert len(events_received) == 1668assert any("simpleTest" in event.url for event in events_received)669670driver.browsing_context.remove_event_handler("dom_content_loaded", callback_id)671672673def test_add_event_handler_load(driver, pages):674"""Test adding event handler for load event."""675events_received = []676677def on_load(info):678events_received.append(info)679680callback_id = driver.browsing_context.add_event_handler("load", on_load)681assert callback_id is not None682683context_id = driver.current_window_handle684url = pages.url("simpleTest.html")685driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)686687assert len(events_received) == 1688assert any("simpleTest" in event.url for event in events_received)689690driver.browsing_context.remove_event_handler("load", callback_id)691692693def test_add_event_handler_navigation_started(driver, pages):694"""Test adding event handler for navigation_started event."""695events_received = []696697def on_navigation_started(info):698events_received.append(info)699700callback_id = driver.browsing_context.add_event_handler("navigation_started", on_navigation_started)701assert callback_id is not None702703context_id = driver.current_window_handle704url = pages.url("simpleTest.html")705driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)706707assert len(events_received) == 1708assert any("simpleTest" in event.url for event in events_received)709710driver.browsing_context.remove_event_handler("navigation_started", callback_id)711712713def test_add_event_handler_fragment_navigated(driver, pages):714"""Test adding event handler for fragment_navigated event."""715events_received = []716717def on_fragment_navigated(info):718events_received.append(info)719720callback_id = driver.browsing_context.add_event_handler("fragment_navigated", on_fragment_navigated)721assert callback_id is not None722723# First navigate to a page724context_id = driver.current_window_handle725url = pages.url("linked_image.html")726driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)727728# Then navigate to the same page with a fragment to trigger the event729fragment_url = url + "#link"730driver.browsing_context.navigate(context=context_id, url=fragment_url, wait=ReadinessState.COMPLETE)731732assert len(events_received) == 1733assert any("link" in event.url for event in events_received)734735driver.browsing_context.remove_event_handler("fragment_navigated", callback_id)736737738@pytest.mark.xfail_firefox739def test_add_event_handler_navigation_failed(driver):740"""Test adding event handler for navigation_failed event."""741events_received = []742743def on_navigation_failed(info):744events_received.append(info)745746callback_id = driver.browsing_context.add_event_handler("navigation_failed", on_navigation_failed)747assert callback_id is not None748749# Navigate to an invalid URL to trigger the event750context_id = driver.current_window_handle751try:752driver.browsing_context.navigate(context=context_id, url="http://invalid-domain-that-does-not-exist.test/")753except Exception:754# Expect an exception due to navigation failure755pass756757assert len(events_received) == 1758assert events_received[0].url == "http://invalid-domain-that-does-not-exist.test/"759assert events_received[0].context == context_id760761driver.browsing_context.remove_event_handler("navigation_failed", callback_id)762763764def test_add_event_handler_user_prompt_opened(driver, pages):765"""Test adding event handler for user_prompt_opened event."""766events_received = []767768def on_user_prompt_opened(info):769events_received.append(info)770771callback_id = driver.browsing_context.add_event_handler("user_prompt_opened", on_user_prompt_opened)772assert callback_id is not None773774# Create an alert to trigger the event775create_alert_page(driver, pages)776driver.find_element(By.ID, "alert").click()777WebDriverWait(driver, 5).until(EC.alert_is_present())778WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0)779780assert len(events_received) == 1781assert events_received[0].type == "alert"782assert events_received[0].message == "cheese"783784# Clean up the alert785driver.browsing_context.handle_user_prompt(context=driver.current_window_handle)786driver.browsing_context.remove_event_handler("user_prompt_opened", callback_id)787788789def test_add_event_handler_user_prompt_closed(driver, pages):790"""Test adding event handler for user_prompt_closed event."""791events_received = []792793def on_user_prompt_closed(info):794events_received.append(info)795796callback_id = driver.browsing_context.add_event_handler("user_prompt_closed", on_user_prompt_closed)797assert callback_id is not None798799create_prompt_page(driver, pages)800driver.execute_script("prompt('Enter something')")801WebDriverWait(driver, 5).until(EC.alert_is_present())802803driver.browsing_context.handle_user_prompt(804context=driver.current_window_handle, accept=True, user_text="test input"805)806807assert len(events_received) == 1808assert events_received[0].accepted is True809assert events_received[0].user_text == "test input"810811driver.browsing_context.remove_event_handler("user_prompt_closed", callback_id)812813814def test_add_event_handler_history_updated(driver, pages):815"""Test adding event handler for history_updated event."""816events_received = []817818def on_history_updated(info):819events_received.append(info)820821callback_id = driver.browsing_context.add_event_handler("history_updated", on_history_updated)822assert callback_id is not None823824context_id = driver.current_window_handle825url = pages.url("simpleTest.html")826driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)827828# Use history.pushState to trigger history updated event829driver.script.execute("() => { history.pushState({}, '', '/new-path'); }")830WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0)831832assert len(events_received) >= 1833assert "/new-path" in events_received[0].url834assert events_received[0].context == context_id835836driver.browsing_context.remove_event_handler("history_updated", callback_id)837838839def test_add_event_handler_download_will_begin(driver, pages):840"""Test adding event handler for download_will_begin event."""841events_received = []842843def on_download_will_begin(info):844events_received.append(info)845846callback_id = driver.browsing_context.add_event_handler("download_will_begin", on_download_will_begin)847assert callback_id is not None848849# click on a download link to trigger the event850context_id = driver.current_window_handle851url = pages.url("downloads/download.html")852driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)853854download_xpath_file_1_txt = '//*[@id="file-1"]'855driver.find_element(By.XPATH, download_xpath_file_1_txt).click()856WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0)857858assert len(events_received) == 1859# filename maybe file_1.txt or file_1(1).txt depending on existing files in download dir860assert "file_1" in events_received[0].suggested_filename861862driver.browsing_context.remove_event_handler("download_will_begin", callback_id)863864865def test_add_event_handler_download_end(driver, pages):866"""Test adding event handler for download_end event."""867events_received = []868869def on_download_end(info):870events_received.append(info)871872callback_id = driver.browsing_context.add_event_handler("download_end", on_download_end)873assert callback_id is not None874875context_id = driver.current_window_handle876url = pages.url("downloads/download.html")877driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE)878879driver.find_element(By.ID, "file-1").click()880881driver.find_element(By.ID, "file-2").click()882WebDriverWait(driver, 5).until(lambda d: len(events_received) == 2)883884assert len(events_received) == 2885886for ev in events_received:887assert ev.download_params is not None888assert ev.download_params.status == "complete"889assert ev.download_params.context == context_id890assert ev.download_params.timestamp is not None891892# we assert that atleast "file_1" is present in the downloaded file since multiple downloads893# will have numbered suffix like file_1 (1)894assert any(895"downloads/file_1.txt" in ev.download_params.url and "file_1" in ev.download_params.filepath896for ev in events_received897)898899assert any(900"downloads/file_2.jpg" in ev.download_params.url and "file_2" in ev.download_params.filepath901for ev in events_received902)903904driver.browsing_context.remove_event_handler("download_end", callback_id)905906907def test_add_event_handler_with_specific_contexts(driver):908"""Test adding event handler with specific browsing contexts."""909events_received = []910911def on_context_created(info):912events_received.append(info)913914context_id = driver.browsing_context.create(type=WindowTypes.TAB)915916# Add event handler for specific context917callback_id = driver.browsing_context.add_event_handler(918"context_created", on_context_created, contexts=[context_id]919)920assert callback_id is not None921922# Create another context (should trigger event)923new_context_id = driver.browsing_context.create(type=WindowTypes.TAB)924925assert len(events_received) >= 1926927driver.browsing_context.close(context_id)928driver.browsing_context.close(new_context_id)929driver.browsing_context.remove_event_handler("context_created", callback_id)930931932def test_remove_event_handler(driver):933"""Test removing event handler."""934events_received = []935936def on_context_created(info):937events_received.append(info)938939callback_id = driver.browsing_context.add_event_handler("context_created", on_context_created)940941# Create a context to trigger the event942context_id_1 = driver.browsing_context.create(type=WindowTypes.TAB)943944initial_events = len(events_received)945946# Remove the event handler947driver.browsing_context.remove_event_handler("context_created", callback_id)948949# Create another context (should not trigger event after removal)950context_id_2 = driver.browsing_context.create(type=WindowTypes.TAB)951952# Verify no new events were received after removal953assert len(events_received) == initial_events954955driver.browsing_context.close(context_id_1)956driver.browsing_context.close(context_id_2)957958959def test_multiple_event_handlers_same_event(driver):960"""Test adding multiple event handlers for the same event."""961events_received_1 = []962events_received_2 = []963964def on_context_created_1(info):965events_received_1.append(info)966967def on_context_created_2(info):968events_received_2.append(info)969970# Add multiple event handlers for the same event971callback_id_1 = driver.browsing_context.add_event_handler("context_created", on_context_created_1)972callback_id_2 = driver.browsing_context.add_event_handler("context_created", on_context_created_2)973974# Create a context to trigger both handlers975context_id = driver.browsing_context.create(type=WindowTypes.TAB)976977# Verify both handlers received the event978assert len(events_received_1) >= 1979assert len(events_received_2) >= 1980# Check any of the events has the required context ID981assert any(event.context == context_id for event in events_received_1)982assert any(event.context == context_id for event in events_received_2)983984driver.browsing_context.close(context_id)985driver.browsing_context.remove_event_handler("context_created", callback_id_1)986driver.browsing_context.remove_event_handler("context_created", callback_id_2)987988989def test_remove_specific_event_handler_multiple_handlers(driver):990"""Test removing a specific event handler when multiple handlers exist."""991events_received_1 = []992events_received_2 = []993994def on_context_created_1(info):995events_received_1.append(info)996997def on_context_created_2(info):998events_received_2.append(info)9991000# Add multiple event handlers1001callback_id_1 = driver.browsing_context.add_event_handler("context_created", on_context_created_1)1002callback_id_2 = driver.browsing_context.add_event_handler("context_created", on_context_created_2)10031004# Create a context to trigger both handlers1005context_id_1 = driver.browsing_context.create(type=WindowTypes.TAB)10061007# Verify both handlers received the event1008assert len(events_received_1) >= 11009assert len(events_received_2) >= 110101011# store the initial event counts1012initial_count_1 = len(events_received_1)1013initial_count_2 = len(events_received_2)10141015# Remove only the first handler1016driver.browsing_context.remove_event_handler("context_created", callback_id_1)10171018# Create another context1019context_id_2 = driver.browsing_context.create(type=WindowTypes.TAB)10201021# Verify only the second handler received the new event1022assert len(events_received_1) == initial_count_1 # No new events1023assert len(events_received_2) == initial_count_2 + 1 # 1 new event10241025driver.browsing_context.close(context_id_1)1026driver.browsing_context.close(context_id_2)1027driver.browsing_context.remove_event_handler("context_created", callback_id_2)102810291030class _EventHandlerTestHelper:1031def __init__(self, driver):1032self.driver = driver1033self.events_received = []1034self.context_counts = {}1035self.event_type_counts = {}1036self.processing_times = []1037self.consistency_errors = []1038self.thread_errors = []1039self.callback_ids = []1040self.data_lock = threading.Lock()1041self.registration_complete = threading.Event()10421043def make_callback(self):1044def callback(info):1045start_time = time.time()1046time.sleep(0.02) # Simulate race window10471048with self.data_lock:1049initial_event_count = len(self.events_received)10501051self.events_received.append(info)10521053context_id = info.context1054self.context_counts.setdefault(context_id, 0)1055self.context_counts[context_id] += 110561057event_type = info.__class__.__name__1058self.event_type_counts.setdefault(event_type, 0)1059self.event_type_counts[event_type] += 110601061processing_time = time.time() - start_time1062self.processing_times.append(processing_time)10631064final_event_count = len(self.events_received)1065final_context_total = sum(self.context_counts.values())1066final_type_total = sum(self.event_type_counts.values())1067final_processing_count = len(self.processing_times)10681069expected_count = initial_event_count + 11070if not (1071final_event_count1072== final_context_total1073== final_type_total1074== final_processing_count1075== expected_count1076):1077self.consistency_errors.append("Data consistency error")10781079return callback10801081def register_handler(self, thread_id):1082try:1083callback = self.make_callback()1084callback_id = self.driver.browsing_context.add_event_handler("context_created", callback)1085with self.data_lock:1086self.callback_ids.append(callback_id)1087if len(self.callback_ids) == 5:1088self.registration_complete.set()1089return callback_id1090except Exception as e:1091with self.data_lock:1092self.thread_errors.append(f"Thread {thread_id}: Registration failed: {e}")1093return None10941095def remove_handler(self, callback_id, thread_id):1096try:1097self.driver.browsing_context.remove_event_handler("context_created", callback_id)1098except Exception as e:1099with self.data_lock:1100self.thread_errors.append(f"Thread {thread_id}: Removal failed: {e}")110111021103def test_concurrent_event_handler_registration(driver):1104helper = _EventHandlerTestHelper(driver)11051106with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:1107futures = [executor.submit(helper.register_handler, f"reg-{i}") for i in range(5)]1108for future in futures:1109future.result(timeout=15)11101111helper.registration_complete.wait(timeout=5)1112assert len(helper.callback_ids) == 5, f"Expected 5 handlers, got {len(helper.callback_ids)}"1113assert not helper.thread_errors, "Errors during registration: \n" + "\n".join(helper.thread_errors)111411151116def test_event_callback_data_consistency(driver):1117helper = _EventHandlerTestHelper(driver)11181119for i in range(5):1120helper.register_handler(f"reg-{i}")11211122test_contexts = []1123for _ in range(3):1124context = driver.browsing_context.create(type=WindowTypes.TAB)1125test_contexts.append(context)11261127for ctx in test_contexts:1128driver.browsing_context.close(ctx)11291130with helper.data_lock:1131assert not helper.consistency_errors, "Consistency errors: " + str(helper.consistency_errors)1132assert len(helper.events_received) > 0, "No events received"1133assert len(helper.events_received) == sum(helper.context_counts.values())1134assert len(helper.events_received) == sum(helper.event_type_counts.values())1135assert len(helper.events_received) == len(helper.processing_times)113611371138def test_concurrent_event_handler_removal(driver):1139helper = _EventHandlerTestHelper(driver)11401141for i in range(5):1142helper.register_handler(f"reg-{i}")11431144with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:1145futures = [1146executor.submit(helper.remove_handler, callback_id, f"rem-{i}")1147for i, callback_id in enumerate(helper.callback_ids)1148]1149for future in futures:1150future.result(timeout=15)11511152assert not helper.thread_errors, "Errors during removal: \n" + "\n".join(helper.thread_errors)115311541155def test_no_event_after_handler_removal(driver):1156helper = _EventHandlerTestHelper(driver)11571158for i in range(5):1159helper.register_handler(f"reg-{i}")11601161context = driver.browsing_context.create(type=WindowTypes.TAB)1162driver.browsing_context.close(context)11631164events_before = len(helper.events_received)11651166for i, callback_id in enumerate(helper.callback_ids):1167helper.remove_handler(callback_id, f"rem-{i}")11681169post_context = driver.browsing_context.create(type=WindowTypes.TAB)1170driver.browsing_context.close(post_context)11711172with helper.data_lock:1173new_events = len(helper.events_received) - events_before11741175assert new_events == 0, f"Expected 0 new events after removal, got {new_events}"117611771178