From 3cbb5772fa133619fa874e27f58f20ad6ad95dc1 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:18:00 -0400 Subject: [PATCH 01/14] Fix `RuntimeWarning: coroutine 'Event.wait' was never awaited` --- seleniumbase/undetected/cdp_driver/connection.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/seleniumbase/undetected/cdp_driver/connection.py b/seleniumbase/undetected/cdp_driver/connection.py index 920fab3982e..ca4a725be4b 100644 --- a/seleniumbase/undetected/cdp_driver/connection.py +++ b/seleniumbase/undetected/cdp_driver/connection.py @@ -314,11 +314,15 @@ async def wait(self, t: Union[int, float] = None): try: if isinstance(t, (int, float)): try: + # Pass the coroutine directly to wait_for() await asyncio.wait_for( self.listener.idle.wait(), timeout=t ) - except RuntimeError: - await self.listener.idle.wait() + except (RuntimeError, asyncio.TimeoutError): + # If the wait_for() fails or errors out, just pass. + # The wait_for() cancels the underlying coroutine. + pass + # Keep waiting until the absolute time 't' has elapsed while (loop.time() - start_time) < t: await asyncio.sleep(0.1) else: From e7fc1a854c6dc42b61c88b0afa883f1550983c38 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:31:28 -0400 Subject: [PATCH 02/14] Remove the selenium-wire integration --- README.md | 1 - examples/raw_parameter_script.py | 1 - help_docs/customizing_test_runs.md | 5 - help_docs/method_summary.md | 5 - help_docs/syntax_formats.md | 123 ------------------------ seleniumbase/behave/behave_sb.py | 6 -- seleniumbase/core/browser_launcher.py | 75 +-------------- seleniumbase/core/sb_driver.py | 23 ----- seleniumbase/fixtures/base_case.py | 40 +------- seleniumbase/fixtures/constants.py | 6 -- seleniumbase/plugins/driver_manager.py | 14 --- seleniumbase/plugins/pytest_plugin.py | 15 --- seleniumbase/plugins/sb_manager.py | 15 --- seleniumbase/plugins/selenium_plugin.py | 19 ---- seleniumbase/undetected/__init__.py | 1 - 15 files changed, 3 insertions(+), 346 deletions(-) diff --git a/README.md b/README.md index acd767d42d2..8fd4233b601 100755 --- a/README.md +++ b/README.md @@ -853,7 +853,6 @@ pytest test_coffee_cart.py --trace --screenshot # (Save a screenshot at the end of each test.) --no-screenshot # (No screenshots saved unless tests directly ask it.) --visual-baseline # (Set the visual baseline for Visual/Layout tests.) ---wire # (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf # (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER # (Multiplies the default timeout values.) --list-fail-page # (After each failing test, list the URL of the failure.) diff --git a/examples/raw_parameter_script.py b/examples/raw_parameter_script.py index 7a61c73cf1b..ee12ea0e3e3 100644 --- a/examples/raw_parameter_script.py +++ b/examples/raw_parameter_script.py @@ -82,7 +82,6 @@ sb._crumbs = False sb._final_debug = False sb.esc_end = False - sb.use_wire = False sb.enable_3d_apis = False sb.window_position = None sb.window_size = None diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md index 3e9a6345144..a28bbc19050 100644 --- a/help_docs/customizing_test_runs.md +++ b/help_docs/customizing_test_runs.md @@ -224,7 +224,6 @@ pytest my_first_test.py --settings-file=custom_settings.py --screenshot # (Save a screenshot at the end of each test.) --no-screenshot # (No screenshots saved unless tests directly ask it.) --visual-baseline # (Set the visual baseline for Visual/Layout tests.) ---wire # (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf # (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER # (Multiplies the default timeout values.) --list-fail-page # (After each failing test, list the URL of the failure.) @@ -691,7 +690,6 @@ binary_location=None # Set path of the Chromium browser binary to use. driver_version=None # Set the chromedriver or uc_driver version to use. skip_js_waits=None # Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs=None # Wait for AngularJS to load after some actions. -use_wire=None # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None # Set the browser's starting window position: "X,Y" window_size=None # Set the browser's starting window size: "Width,Height" @@ -727,7 +725,6 @@ log_cdp=None # Shortcut / Duplicate of "log_cdp_events". ad_block=None # Shortcut / Duplicate of "ad_block_on". server=None # Shortcut / Duplicate of "servername". guest=None # Shortcut / Duplicate of "guest_mode". -wire=None # Shortcut / Duplicate of "use_wire". pls=None # Shortcut / Duplicate of "page_load_strategy". sjw=None # Shortcut / Duplicate of "skip_js_waits". wfa=None # Shortcut / Duplicate of "wait_for_angularjs". @@ -807,7 +804,6 @@ disable_features=None # "F1,F2" (Disable Chrome features, ","-separated.) binary_location=None # Set path of the Chromium browser binary to use. driver_version=None # Set the chromedriver or uc_driver version to use. page_load_strategy=None # Set Chrome PLS to "normal", "eager", or "none". -use_wire=None # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None # Set the browser's starting window position: "X,Y" window_size=None # Set the browser's starting window size: "Width,Height" @@ -827,7 +823,6 @@ log_cdp=None # Shortcut / Duplicate of "log_cdp_events". ad_block=None # Shortcut / Duplicate of "ad_block_on". server=None # Shortcut / Duplicate of "servername". guest=None # Shortcut / Duplicate of "guest_mode". -wire=None # Shortcut / Duplicate of "use_wire". pls=None # Shortcut / Duplicate of "page_load_strategy". use_chromium=None # Use base "Chromium" cft=None # Use "Chrome for Testing" diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 85b0995f298..ae72d8d3c67 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -235,7 +235,6 @@ self.get_new_driver( binary_location=None, driver_version=None, page_load_strategy=None, - use_wire=None, external_pdf=None, is_mobile=None, d_width=None, @@ -434,10 +433,6 @@ self.get_session_storage_items() ############ -self.set_wire_proxy(string) # Requires "--wire"! - -############ - self.add_css_link(css_link) self.add_js_link(js_link) self.add_css_style(css_style) diff --git a/help_docs/syntax_formats.md b/help_docs/syntax_formats.md index 26013ccb530..c1181df1abc 100644 --- a/help_docs/syntax_formats.md +++ b/help_docs/syntax_formats.md @@ -282,30 +282,6 @@ class OverrideDriverTest(BaseCase): (From examples/test_override_driver.py) -The above format lets you customize [selenium-wire](https://github.com/wkeeling/selenium-wire) for intercepting and inspecting requests and responses during SeleniumBase tests. Here's how a ``selenium-wire`` integration may look: - -```python -from seleniumbase import BaseCase -from seleniumwire import webdriver # Requires "pip install selenium-wire" -BaseCase.main(__name__, __file__) - -class WireTestCase(BaseCase): - def get_new_driver(self, *args, **kwargs): - options = webdriver.ChromeOptions() - options.add_experimental_option( - "excludeSwitches", ["enable-automation"] - ) - options.add_experimental_option("useAutomationExtension", False) - return webdriver.Chrome(options=options) - - def test_simple(self): - self.open("https://seleniumbase.io/demo_page") - for request in self.driver.requests: - print(request.url) -``` - -(NOTE: The ``selenium-wire`` integration is now included with ``seleniumbase``: Add ``--wire`` as a ``pytest`` command-line option to activate.) -

10. Overriding the driver via "sb" fixture

@@ -386,75 +362,6 @@ class TestOverride: (From examples/test_override_sb_fixture.py) -Here's how the [selenium-wire](https://github.com/wkeeling/selenium-wire) integration may look when overriding the ``sb`` pytest fixture to override the driver: - -```python -import pytest - -@pytest.fixture() -def sb(request): - import sys - from seleniumbase import BaseCase - from seleniumbase import config as sb_config - from seleniumwire import webdriver # Requires "pip install selenium-wire" - - class BaseClass(BaseCase): - def get_new_driver(self, *args, **kwargs): - options = webdriver.ChromeOptions() - if "linux" in sys.platform: - options.add_argument("--headless=new") - options.add_experimental_option( - "excludeSwitches", ["enable-automation"], - ) - return webdriver.Chrome(options=options) - - def setUp(self): - super().setUp() - - def tearDown(self): - self.save_teardown_screenshot() # On failure or "--screenshot" - super().tearDown() - - def base_method(self): - pass - - if request.cls: - request.cls.sb = BaseClass("base_method") - request.cls.sb.setUp() - request.cls.sb._needs_tearDown = True - request.cls.sb._using_sb_fixture = True - request.cls.sb._using_sb_fixture_class = True - sb_config._sb_node[request.node.nodeid] = request.cls.sb - yield request.cls.sb - if request.cls.sb._needs_tearDown: - request.cls.sb.tearDown() - request.cls.sb._needs_tearDown = False - else: - sb = BaseClass("base_method") - sb.setUp() - sb._needs_tearDown = True - sb._using_sb_fixture = True - sb._using_sb_fixture_no_class = True - sb_config._sb_node[request.node.nodeid] = sb - yield sb - if sb._needs_tearDown: - sb.tearDown() - sb._needs_tearDown = False - -def test_wire_with_no_class(sb): - sb.open("https://seleniumbase.io/demo_page") - for request in sb.driver.requests: - print(request.url) - -class TestWire: - def test_wire_inside_class(self, sb): - sb.open("https://seleniumbase.io/demo_page") - for request in sb.driver.requests: - print(request.url) -``` - -(NOTE: The ``selenium-wire`` integration is now included with ``seleniumbase``: Add ``--wire`` as a ``pytest`` command-line option to activate. If you need both ``--wire`` with ``--undetected`` modes together, you'll still need to override ``get_new_driver()``.) -

11. BaseCase with Chinese translations

@@ -963,36 +870,6 @@ finally: (From examples/raw_driver_manager.py) -Here's how the [selenium-wire](https://github.com/wkeeling/selenium-wire) integration may look when using the ``Driver()`` format: - -```python -from seleniumbase import Driver - -driver = Driver(wire=True, headless=True) -try: - driver.get("https://wikipedia.org") - for request in driver.requests: - print(request.url) -finally: - driver.quit() -``` - -Here's another `selenium-wire` example with the `Driver()` format: - -```python -from seleniumbase import Driver - -def intercept_response(request, response): - print(request.headers) - -driver = Driver(wire=True) -try: - driver.response_interceptor = intercept_response - driver.get("https://wikipedia.org") -finally: - driver.quit() -``` - Here's an example of basic login with the ``Driver()`` format: ```python diff --git a/seleniumbase/behave/behave_sb.py b/seleniumbase/behave/behave_sb.py index 240d1452ac3..6cdfedcb819 100644 --- a/seleniumbase/behave/behave_sb.py +++ b/seleniumbase/behave/behave_sb.py @@ -101,7 +101,6 @@ -D screenshot (Save a screenshot at the end of each test.) -D no-screenshot (No screenshots saved unless tests directly ask it.) -D visual-baseline (Set the visual baseline for Visual/Layout tests.) --D wire (Use selenium-wire's webdriver for replacing selenium webdriver.) -D external-pdf (Set Chromium "plugins.always_open_pdf_externally":True.) -D timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) """ @@ -204,7 +203,6 @@ def get_configured_sb(context): sb._crumbs = False sb._disable_beforeunload = False sb.visual_baseline = False - sb.use_wire = False sb.window_position = None sb.window_size = None sb.maximize_option = False @@ -645,10 +643,6 @@ def get_configured_sb(context): if low_key in ["visual-baseline", "visual_baseline"]: sb.visual_baseline = True continue - # Handle: -D wire - if low_key == "wire": - sb.use_wire = True - continue # Handle: -D window-position=X,Y / window_position=X,Y if low_key in ["window-position", "window_position"]: window_position = userdata[key] diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index fb7bb4c9c0c..988fcb98b0d 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -312,8 +312,6 @@ def extend_driver( driver.execute_cdp_cmd( "Page.addScriptToEvaluateOnNewDocument", {"source": recorder_code} ) - if hasattr(driver, "proxy"): - driver.set_wire_proxy = DM.set_wire_proxy completed_loads = [] for ext_dir in sb_config._ext_dirs: if ext_dir not in completed_loads: @@ -2334,7 +2332,6 @@ def _set_chrome_options( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -3071,7 +3068,6 @@ def get_driver( binary_location=None, driver_version=None, page_load_strategy=None, - use_wire=False, external_pdf=False, test_id=None, mobile_emulator=False, @@ -3551,7 +3547,6 @@ def get_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, test_id, mobile_emulator, @@ -3610,7 +3605,6 @@ def get_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, mobile_emulator, device_width, @@ -3673,7 +3667,6 @@ def get_remote_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, test_id, mobile_emulator, @@ -3681,35 +3674,6 @@ def get_remote_driver( device_height, device_pixel_ratio, ): - if use_wire: - pip_find_lock = fasteners.InterProcessLock( - constants.PipInstall.FINDLOCK - ) - with pip_find_lock: # Prevent issues with multiple processes - with suppress(Exception): - shared_utils.make_writable(constants.PipInstall.FINDLOCK) - try: - from seleniumwire import webdriver - import blinker - with suppress(Exception): - use_blinker_ver = constants.SeleniumWire.BLINKER_VER - if blinker.__version__ != use_blinker_ver: - shared_utils.pip_install( - "blinker", version=use_blinker_ver - ) - del blinker - except Exception: - shared_utils.pip_install( - "blinker", version=constants.SeleniumWire.BLINKER_VER - ) - shared_utils.pip_install( - "selenium-wire", version=constants.SeleniumWire.VER - ) - from seleniumwire import webdriver - warnings.simplefilter("ignore", category=DeprecationWarning) - else: - from selenium import webdriver - # Construct the address for connecting to a Selenium Grid if servername.startswith("https://"): protocol = "https" @@ -3810,7 +3774,6 @@ def get_remote_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -3987,7 +3950,6 @@ def get_remote_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -4111,7 +4073,6 @@ def get_local_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, mobile_emulator, device_width, @@ -4170,35 +4131,6 @@ def get_local_driver( local_uc_driver = driver_dir + "/uc_driver.exe" b_path = binary_location use_uc = is_using_uc(undetectable, browser_name) - if use_wire: - pip_find_lock = fasteners.InterProcessLock( - constants.PipInstall.FINDLOCK - ) - with pip_find_lock: # Prevent issues with multiple processes - with suppress(Exception): - shared_utils.make_writable(constants.PipInstall.FINDLOCK) - try: - from seleniumwire import webdriver - import blinker - with suppress(Exception): - use_blinker_ver = constants.SeleniumWire.BLINKER_VER - if blinker.__version__ != use_blinker_ver: - shared_utils.pip_install( - "blinker", version=use_blinker_ver - ) - del blinker - except Exception: - shared_utils.pip_install( - "blinker", version=constants.SeleniumWire.BLINKER_VER - ) - shared_utils.pip_install( - "selenium-wire", version=constants.SeleniumWire.VER - ) - from seleniumwire import webdriver - warnings.simplefilter("ignore", category=DeprecationWarning) - else: - from selenium import webdriver - if browser_name == constants.Browser.FIREFOX: firefox_options = _set_firefox_options( downloads_path, @@ -5064,7 +4996,6 @@ def get_local_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -5124,7 +5055,7 @@ def get_local_driver( and major_chrome_version and int(major_chrome_version) >= 117 and not use_uc - and not (remote_debug or devtools or use_wire) + and not (remote_debug or devtools) and not (proxy_string or multi_proxy or proxy_pac_url) and (not chromium_arg or "debug" not in chromium_arg) and (not servername or servername == "localhost") @@ -5632,7 +5563,6 @@ def get_local_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -5888,7 +5818,6 @@ def get_local_driver( binary_location, driver_version, page_load_strategy, - use_wire, external_pdf, servername, mobile_emulator, @@ -6213,7 +6142,7 @@ def get_local_driver( elif extension_zip or extension_dir: print("\nUnable to load extension while starting Chrome!\n") raise - elif headless or headless2 or IS_LINUX or proxy_string or use_wire: + elif headless or headless2 or IS_LINUX or proxy_string: raise # Try running without any options (bare bones Chrome launch) if local_chromedriver and os.path.exists(local_chromedriver): diff --git a/seleniumbase/core/sb_driver.py b/seleniumbase/core/sb_driver.py index 383b9b5aef2..3108b03ed99 100644 --- a/seleniumbase/core/sb_driver.py +++ b/seleniumbase/core/sb_driver.py @@ -409,26 +409,3 @@ def reset_window_size(self): width = settings.CHROME_START_WIDTH height = settings.CHROME_START_HEIGHT self.driver.set_window_rect(x, y, width, height) - - def set_wire_proxy(self, string): - """Set a proxy server for selenium-wire mode ("--wire") - Examples: (ONLY avilable if using selenium-wire mode!) - driver.set_wire_proxy("SERVER:PORT") - driver.set_wire_proxy("socks5://SERVER:PORT") - driver.set_wire_proxy("USERNAME:PASSWORD@SERVER:PORT") - """ - the_http = "http" - the_https = "https" - if string.startswith("socks4://"): - the_http = "socks4" - the_https = "socks4" - elif string.startswith("socks5://"): - the_http = "socks5" - the_https = "socks5" - string = string.split("//")[-1] - if hasattr(self.driver, "proxy"): - self.driver.proxy = { - "http": "%s://%s" % (the_http, string), - "https": "%s://%s" % (the_https, string), - "no_proxy": "localhost,127.0.0.1", - } diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index a105ad8f602..52a06f46f6f 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -4115,7 +4115,6 @@ def get_new_driver( binary_location=None, driver_version=None, page_load_strategy=None, - use_wire=None, external_pdf=None, is_mobile=None, d_width=None, @@ -4177,7 +4176,6 @@ def get_new_driver( binary_location - the path of the browser binary to use (Chromium) driver_version - the chromedriver or uc_driver version to force page_load_strategy - the option to change pageLoadStrategy (Chrome) - use_wire - Use selenium-wire webdriver instead of the selenium one external_pdf - "plugins.always_open_pdf_externally": True. (Chrome) is_mobile - the option to use the mobile emulator (Chrome-only) d_width - the device width of the mobile emulator (Chrome-only) @@ -4210,7 +4208,7 @@ def get_new_driver( " for examples!)" % (browserstack_ref, sauce_labs_ref) ) - shortcuts = ["dark", "guest", "locale", "mobile", "pls", "uc", "wire"] + shortcuts = ["dark", "guest", "locale", "mobile", "pls", "uc"] if kwargs: for key in kwargs.keys(): if key not in shortcuts: @@ -4326,10 +4324,6 @@ def get_new_driver( page_load_strategy = self.page_load_strategy if "pls" in kwargs and not page_load_strategy: page_load_strategy = kwargs["pls"] - if use_wire is None: - use_wire = self.use_wire - if "wire" in kwargs and not use_wire: - use_wire = kwargs["wire"] if external_pdf is None: external_pdf = self.external_pdf test_id = self.__get_test_id() @@ -4408,7 +4402,6 @@ def get_new_driver( binary_location=binary_location, driver_version=driver_version, page_load_strategy=page_load_strategy, - use_wire=use_wire, external_pdf=external_pdf, test_id=test_id, mobile_emulator=is_mobile, @@ -9380,35 +9373,6 @@ def get_session_storage_items(self): ############ - # Methods ONLY for the selenium-wire integration ("--wire") - - def set_wire_proxy(self, string): - """Set a proxy server for selenium-wire mode ("--wire") - NOTE: This method ONLY works while using "--wire" mode! - Examples: - self.set_wire_proxy("SERVER:PORT") - self.set_wire_proxy("socks5://SERVER:PORT") - self.set_wire_proxy("USERNAME:PASSWORD@SERVER:PORT") """ - if not string: - self.driver.proxy = {} - return - the_http = "http" - the_https = "https" - if string.startswith("socks4://"): - the_http = "socks4" - the_https = "socks4" - elif string.startswith("socks5://"): - the_http = "socks5" - the_https = "socks5" - string = string.split("//")[-1] - self.driver.proxy = { - "http": "%s://%s" % (the_http, string), - "https": "%s://%s" % (the_https, string), - "no_proxy": "localhost,127.0.0.1", - } - - ############ - # Duplicates (Avoids name confusion when migrating from other frameworks.) def open_url(self, url): @@ -15553,7 +15517,6 @@ def setUp(self, masterqa_mode=False): self.binary_location = sb_config.binary_location self.driver_version = sb_config.driver_version self.page_load_strategy = sb_config.page_load_strategy - self.use_wire = sb_config.use_wire self.external_pdf = sb_config.external_pdf self._final_debug = sb_config.final_debug self.window_position = sb_config.window_position @@ -15937,7 +15900,6 @@ def setUp(self, masterqa_mode=False): binary_location=self.binary_location, driver_version=self.driver_version, page_load_strategy=self.page_load_strategy, - use_wire=self.use_wire, external_pdf=self.external_pdf, is_mobile=self.mobile_emulator, d_width=self.__device_width, diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index b30c0c96457..0a413c1037f 100644 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -346,12 +346,6 @@ class ProxyPy: VER = "2.4.3" -class SeleniumWire: - # The version installed if selenium-wire is not installed - VER = "5.1.0" - BLINKER_VER = "1.7.0" # The "blinker" dependency version - - class PyAutoGUI: # The version installed if PyAutoGUI is not installed VER = "0.9.54" diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py index d24137f0573..7c5c0e910f7 100644 --- a/seleniumbase/plugins/driver_manager.py +++ b/seleniumbase/plugins/driver_manager.py @@ -116,7 +116,6 @@ def Driver( binary_location=None, # Set path of the Chromium browser binary to use. driver_version=None, # Set the chromedriver or uc_driver version to use. page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none". - use_wire=None, # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None, # Set the browser's starting window position: "X,Y" window_size=None, # Set the browser's starting window size: "Width,Height" @@ -136,7 +135,6 @@ def Driver( ad_block=None, # Shortcut / Duplicate of "ad_block_on". server=None, # Shortcut / Duplicate of "servername". guest=None, # Shortcut / Duplicate of "guest_mode". - wire=None, # Shortcut / Duplicate of "use_wire". pls=None, # Shortcut / Duplicate of "page_load_strategy". cft=None, # Use "Chrome for Testing" chs=None, # Use "Chrome-Headless-Shell" @@ -214,7 +212,6 @@ def Driver( binary_location (str): Set path of the Chromium browser binary to use. driver_version (str): Set the chromedriver or uc_driver version to use. page_load_strategy (str): Set Chrome PLS to "normal", "eager", or "none". - use_wire (bool): Use selenium-wire's webdriver over selenium webdriver. external_pdf (bool): Set Chrome "plugins.always_open_pdf_externally":True window_position (x,y): Set the browser's starting window position: "X,Y" window_size (w,h): Set the browser's starting window size: "Width,Height" @@ -234,7 +231,6 @@ def Driver( ad_block (bool): Shortcut / Duplicate of "ad_block_on". server (str): Shortcut / Duplicate of "servername". guest (bool): Shortcut / Duplicate of "guest_mode". - wire (bool): Shortcut / Duplicate of "use_wire". pls (str): Shortcut / Duplicate of "page_load_strategy". """ from seleniumbase import config as sb_config @@ -873,15 +869,6 @@ def Driver( do_not_track = True else: do_not_track = False - if use_wire is None and wire is None: - if "--wire" in sys_argv: - use_wire = True - else: - use_wire = False - elif use_wire or wire: - use_wire = True - else: - use_wire = False if external_pdf is None: if "--external-pdf" in sys_argv or "--external_pdf" in sys_argv: external_pdf = True @@ -1019,7 +1006,6 @@ def Driver( binary_location=binary_location, driver_version=driver_version, page_load_strategy=page_load_strategy, - use_wire=use_wire, external_pdf=external_pdf, test_id=test_id, mobile_emulator=is_mobile, diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index cc90f86d913..436f678fc44 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -128,7 +128,6 @@ def pytest_addoption(parser): --screenshot (Save a screenshot at the end of each test.) --no-screenshot (No screenshots saved unless tests directly ask it.) --visual-baseline (Set the visual baseline for Visual/Layout tests.) - --wire (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) --list-fail-page (After each failing test, list the URL of the failure.) @@ -1386,13 +1385,6 @@ def pytest_addoption(parser): When a test calls self.check_window(), it will rebuild its files in the visual_baseline folder.""", ) - parser.addoption( - "--wire", - action="store_true", - dest="use_wire", - default=False, - help="""Use selenium-wire's webdriver for selenium webdriver.""", - ) parser.addoption( "--external_pdf", "--external-pdf", @@ -1665,12 +1657,6 @@ def pytest_addoption(parser): '\n (Your browser choice was: "%s")\n' % browser_list[0] ) raise Exception(message) - if undetectable and "--wire" in sys_argv: - raise Exception( - "\n SeleniumBase doesn't support mixing --uc with --wire mode!" - "\n If you need both, override get_new_driver() from BaseCase:" - "\n https://seleniumbase.io/help_docs/syntax_formats/#sb_sf_09\n" - ) def pytest_configure(config): @@ -1897,7 +1883,6 @@ def pytest_configure(config): sb_config.save_screenshot = config.getoption("save_screenshot") sb_config.no_screenshot = config.getoption("no_screenshot") sb_config.visual_baseline = config.getoption("visual_baseline") - sb_config.use_wire = config.getoption("use_wire") sb_config.external_pdf = config.getoption("external_pdf") sb_config.timeout_multiplier = config.getoption("timeout_multiplier") sb_config.list_fp = config.getoption("fail_page") diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index ed8d6798bb7..d95bf6e2417 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -82,7 +82,6 @@ def SB( driver_version=None, # Set the chromedriver or uc_driver version to use. skip_js_waits=None, # Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs=None, # Wait for AngularJS to load after some actions. - use_wire=None, # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None, # Set the browser's starting window position: "X,Y" window_size=None, # Set the browser's starting window size: "Width,Height" @@ -118,7 +117,6 @@ def SB( ad_block=None, # Shortcut / Duplicate of "ad_block_on". server=None, # Shortcut / Duplicate of "servername". guest=None, # Shortcut / Duplicate of "guest_mode". - wire=None, # Shortcut / Duplicate of "use_wire". pls=None, # Shortcut / Duplicate of "page_load_strategy". sjw=None, # Shortcut / Duplicate of "skip_js_waits". wfa=None, # Shortcut / Duplicate of "wait_for_angularjs". @@ -208,7 +206,6 @@ def SB( driver_version (str): Set the chromedriver or uc_driver version to use. skip_js_waits (bool): Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs (bool): Wait for AngularJS to load after some actions. - use_wire (bool): Use selenium-wire's webdriver over selenium webdriver. external_pdf (bool): Set Chrome "plugins.always_open_pdf_externally":True. window_position (x,y): Set the browser's starting window position: "X,Y" window_size (w,h): Set the browser's starting window size: "Width,Height" @@ -244,7 +241,6 @@ def SB( ad_block (bool): Shortcut / Duplicate of "ad_block_on". server (str): Shortcut / Duplicate of "servername". guest (bool): Shortcut / Duplicate of "guest_mode". - wire (bool): Shortcut / Duplicate of "use_wire". pls (str): Shortcut / Duplicate of "page_load_strategy". sjw (bool): Shortcut / Duplicate of "skip_js_waits". wfa (bool): Shortcut / Duplicate of "wait_for_angularjs". @@ -1030,15 +1026,6 @@ def SB( do_not_track = True else: do_not_track = False - if use_wire is None and wire is None: - if "--wire" in sys_argv: - use_wire = True - else: - use_wire = False - elif use_wire or wire: - use_wire = True - else: - use_wire = False if external_pdf is None: if "--external-pdf" in sys_argv or "--external_pdf" in sys_argv: external_pdf = True @@ -1226,7 +1213,6 @@ def SB( sb_config.host_resolver_rules = host_resolver_rules sb_config.block_images = block_images sb_config.do_not_track = do_not_track - sb_config.use_wire = use_wire sb_config.external_pdf = external_pdf sb_config.remote_debug = remote_debug sb_config.settings_file = settings_file @@ -1337,7 +1323,6 @@ def SB( sb.host_resolver_rules = sb_config.host_resolver_rules sb.block_images = sb_config.block_images sb.do_not_track = sb_config.do_not_track - sb.use_wire = sb_config.use_wire sb.external_pdf = sb_config.external_pdf sb.remote_debug = sb_config.remote_debug sb.settings_file = sb_config.settings_file diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index 3ecf7c0af2d..fd1077851af 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -100,7 +100,6 @@ class SeleniumBrowser(Plugin): --maximize (Start tests with the browser window maximized.) --screenshot (Save a screenshot at the end of each test.) --visual-baseline (Set the visual baseline for Visual/Layout tests.) - --wire (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf (Set Chromium "plugins.always_open_pdf_externally": True.) --timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) """ @@ -1047,13 +1046,6 @@ def options(self, parser, env): When a test calls self.check_window(), it will rebuild its files in the visual_baseline folder.""", ) - parser.addoption( - "--wire", - action="store_true", - dest="use_wire", - default=False, - help="""Use selenium-wire's webdriver for selenium webdriver.""", - ) parser.addoption( "--external_pdf", "--external-pdf", @@ -1443,7 +1435,6 @@ def beforeTest(self, test): test.test.save_screenshot_after_test = self.options.save_screenshot test.test.no_screenshot_after_test = self.options.no_screenshot test.test.visual_baseline = self.options.visual_baseline - test.test.use_wire = self.options.use_wire test.test.external_pdf = self.options.external_pdf test.test.timeout_multiplier = self.options.timeout_multiplier test.test.dashboard = False @@ -1476,16 +1467,6 @@ def beforeTest(self, test): else: self.options.xvfb = True test.test.xvfb = True - if self.options.use_wire and self.options.undetectable: - print( - "\n" - "SeleniumBase doesn't support mixing --uc with --wire mode.\n" - "If you need both, override get_new_driver() from BaseCase:\n" - "https://seleniumbase.io/help_docs/syntax_formats/#sb_sf_09\n" - "(Only UC Mode without Wire Mode will be used for this run)\n" - ) - self.options.use_wire = False - test.test.use_wire = False # Recorder Mode can still optimize scripts in --headless2 mode. if self.options.recorder_mode and self.options.headless: self.options.headless = False diff --git a/seleniumbase/undetected/__init__.py b/seleniumbase/undetected/__init__.py index 70d2f8fcb6d..e37947d8c3b 100644 --- a/seleniumbase/undetected/__init__.py +++ b/seleniumbase/undetected/__init__.py @@ -84,7 +84,6 @@ def __init__( If left as 0, a free port will be found. enable_cdp_events: (default: False) - This enables the handling of wire messages. When enabled, you can subscribe to CDP events by using: driver.add_cdp_listener("Network.dataReceived", yourcallback) From ed817adefb810db91b47f62d1b17faa2a3420cce Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:38:00 -0400 Subject: [PATCH 03/14] activate_cdp_mode(url) should still open the url if already active --- seleniumbase/fixtures/base_case.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 52a06f46f6f..d274dc72037 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -5067,6 +5067,8 @@ def activate_cdp_mode(self, url=None, **kwargs): """Activate CDP Mode with the URL and kwargs.""" if getattr(self.driver, "_is_using_uc", None): if self.__is_cdp_swap_needed(): + if url: + self.cdp.open(url, **kwargs) return # CDP Mode is already active if not self.is_connected(): self.driver.connect() From cf996bf0399b5c41ac70e1e3e84992cdf4d154be Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:40:14 -0400 Subject: [PATCH 04/14] Save time & memory by only importing sb_install if needed --- seleniumbase/undetected/patcher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seleniumbase/undetected/patcher.py b/seleniumbase/undetected/patcher.py index abba1348b7b..7df17e62c41 100644 --- a/seleniumbase/undetected/patcher.py +++ b/seleniumbase/undetected/patcher.py @@ -8,7 +8,6 @@ import time import zipfile from contextlib import suppress -from seleniumbase.console_scripts import sb_install from seleniumbase.fixtures import shared_utils logger = logging.getLogger(__name__) @@ -111,6 +110,7 @@ def auto(self, executable_path=None, force=False, version_main=None): if int(self.version_main) < 115: self.unzip_package(self.fetch_package()) else: + from seleniumbase.console_scripts import sb_install sb_install.main( override="chromedriver %s" % self.version_main, intel_for_uc=shared_utils.is_arm_mac(), @@ -131,6 +131,7 @@ def fetch_release_number(self): path = path.upper() logger.debug("Getting release number from %s" % path) if self.version_main and int(self.version_main) > 114: + from seleniumbase.console_scripts import sb_install return ( sb_install.get_cft_latest_version_from_milestone( str(self.version_main) From c1ab11fc1ddf2ab9077041481d0085310482ed23 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:45:24 -0400 Subject: [PATCH 05/14] Update the CDP Mode activation sequence --- seleniumbase/core/browser_launcher.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 988fcb98b0d..7e7d893da42 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -643,14 +643,12 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs): import asyncio from seleniumbase.undetected.cdp_driver import cdp_util - current_url = None + time.sleep(0.012) try: - current_url = driver.current_url + driver.disconnect() except Exception: - driver.connect() - current_url = driver.current_url - url_protocol = current_url.split(":")[0] - driver.disconnect() + time.sleep(0.012) + driver.disconnect() cdp_details = driver._get_cdp_details() cdp_host = cdp_details[1].split("://")[1].split(":")[0] From d86c86fd9acafb7e03c02ee8dda8977e63a883e3 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:47:10 -0400 Subject: [PATCH 06/14] Update __init__.py --- seleniumbase/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/seleniumbase/__init__.py b/seleniumbase/__init__.py index de258d40f2f..f4bab6f2bbd 100644 --- a/seleniumbase/__init__.py +++ b/seleniumbase/__init__.py @@ -3,7 +3,6 @@ import pdb import sys from contextlib import suppress -from selenium import webdriver from seleniumbase.__version__ import __version__ from seleniumbase.common import decorators # noqa from seleniumbase.common import encryption # noqa @@ -45,13 +44,11 @@ pdb.DefaultConfig.sticky_by_default = True colored_traceback.add_hook() os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats -webdriver.TouchActions = None # Lifeline for past selenium-wire versions if sys.version_info >= (3, 10): collections.Callable = collections.abc.Callable # Lifeline for nosetests del collections # Undo "import collections" / Simplify "dir(seleniumbase)" del os # Undo "import os" / Simplify "dir(seleniumbase)" del sys # Undo "import sys" / Simplify "dir(seleniumbase)" -del webdriver # Undo "import webdriver" / Simplify "dir(seleniumbase)" version_list = [int(i) for i in __version__.split(".") if i.isdigit()] version_tuple = tuple(version_list) From 8bcde79feef0c57b0456be3f69971d55e2e8c3b2 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 3 Jun 2026 18:47:52 -0400 Subject: [PATCH 07/14] Add pytest.ini to unit_tests --- examples/unit_tests/pytest.ini | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 examples/unit_tests/pytest.ini diff --git a/examples/unit_tests/pytest.ini b/examples/unit_tests/pytest.ini new file mode 100644 index 00000000000..5fc7a520f3d --- /dev/null +++ b/examples/unit_tests/pytest.ini @@ -0,0 +1,48 @@ +[pytest] + +# Display console output. Disable cacheprovider: +addopts = --capture=tee-sys -p no:cacheprovider + +# Skip these directories during test collection: +norecursedirs = .* build dist recordings temp assets + +# Ignore DeprecationWarning, PytestUnknownMarkWarning +filterwarnings = + ignore::pytest.PytestWarning + ignore:.*U.*mode is deprecated:DeprecationWarning + ignore::pytest.PytestAssertRewriteWarning + +# Configure the junit_family option explicitly: +junit_family = legacy + +# Set pytest discovery rules: +# (Most of the rules here are similar to the default rules.) +# (Inheriting unittest.TestCase could override these rules.) +python_files = test_*.py *_test.py *_tests.py *_suite.py +python_classes = Test* *Test* *Test *Tests *Suite +python_functions = test_* + +# Common pytest markers used in examples: +# (pytest may require marker registration to prevent warnings.) +# (Future versions may turn those marker warnings into errors.) +markers = + marker1: custom marker + marker2: custom marker + marker3: custom marker + marker_test_suite: custom marker + expected_failure: custom marker + local: custom marker + remote: custom marker + offline: custom marker + develop: custom marker + qa: custom marker + ci: custom marker + e2e: custom marker + ready: custom marker + smoke: custom marker + deploy: custom marker + active: custom marker + master: custom marker + release: custom marker + staging: custom marker + production: custom marker From 8274d737affebf0096cb287e09d3a3a3d6b8c915 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:07:51 -0400 Subject: [PATCH 08/14] Optimize the pytest-html report plugin --- seleniumbase/fixtures/constants.py | 1 + seleniumbase/plugins/pytest_plugin.py | 73 ++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index 0a413c1037f..6bfe749ec95 100644 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -145,6 +145,7 @@ def get_favicon(): if not hasattr(encoded_images, "REPORT_FAVICON"): encoded_images.REPORT_FAVICON = encoded_images.get_report_favicon() return encoded_images.REPORT_FAVICON + LOCKFILE = Files.DOWNLOADS_FOLDER + "/htmlreport.lock" class SideBySide: diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index 436f678fc44..1b420780d93 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -1661,6 +1661,8 @@ def pytest_addoption(parser): def pytest_configure(config): """This runs after command-line options have been parsed.""" + global _pytest_config + _pytest_config = config sb_config.item_count = 0 sb_config.item_count_passed = 0 sb_config.item_count_failed = 0 @@ -2161,6 +2163,16 @@ def pytest_collection_finish(session): ) else: print("Dashboard: %s%s%s\n%s" % (c1, dash_url, cr, stars)) + htmlpath = session.config.getoption("--html", default=None) + if htmlpath: + with suppress(Exception): + if getattr(sb_config, "_multithreaded", None): + from filelock import FileLock + dash_lock = FileLock(constants.Report.LOCKFILE) + with dash_lock: + _fix_html_report(htmlpath) + else: + _fix_html_report(htmlpath) def pytest_runtest_setup(item): @@ -2176,10 +2188,49 @@ def pytest_runtest_setup(item): sb_config._latest_display_id = display_id +def pytest_runtest_logreport(report): + with suppress(Exception): + if _pytest_config: # Global variable + htmlpath = _pytest_config.getoption("--html", default=None) + if htmlpath: + # The HTML report option is enabled + if report.when == "teardown": + if getattr(sb_config, "_multithreaded", None): + # Multi-threaded tests + from filelock import FileLock + dash_lock = FileLock(constants.Report.LOCKFILE) + with dash_lock: + _fix_html_report(htmlpath) + else: + # Single-threaded tests + _fix_html_report(htmlpath) + + +def _fix_html_report(report_path): + # (Only if there's a pytest-html report present) + with suppress(Exception): + abs_path = os.path.abspath(".") + html_report_path = os.path.join(abs_path, report_path) + if not os.path.exists(html_report_path): + return + the_html_r = None + with open(html_report_path, mode="r", encoding="utf-8") as f: + the_html_r = f.read() + if "page to ge the" not in the_html_r: + return # The typo was already fixed + # Collapsible rows are trickier to select for copy/paste + the_html_r = the_html_r.replace( + "findAll('.collapsible", "//findAll('.collapsible" + ) + # Fix typo in pytest-html 4.0.2 + # (Later versions have a worse bug that prevents live updates) + the_html_r = the_html_r.replace("page to ge the", "page to get the") + with open(html_report_path, mode="w", encoding="utf-8") as f: + f.write(the_html_r) # Update the HTML report + + def pytest_runtest_teardown(item): - """This runs after every test with pytest. - Make sure that webdriver and headless displays have exited. - (Has zero effect on tests using --reuse-session / --rs)""" + """This runs after every test with pytest.""" if "--co" in sys_argv or "--collect-only" in sys_argv: return with suppress(Exception): @@ -2308,10 +2359,7 @@ def _perform_pytest_unconfigure_(config): reporter = config.pluginmanager.get_plugin("terminalreporter") start_time = None - if hasattr(reporter, "_sessionstarttime"): - start_time = reporter._sessionstarttime # (pytest < 8.4.0) - else: - start_time = reporter._session_start.time # (pytest >= 8.4.0) + start_time = reporter._session_start.time # (pytest >= 8.4.0) duration = time.time() - start_time if not getattr(sb_config, "multi_proxy", None): proxy_helper.remove_proxy_zip_if_present() @@ -2648,13 +2696,10 @@ def pytest_unconfigure(config): return reporter = config.pluginmanager.get_plugin("terminalreporter") if ( - not hasattr(reporter, "_sessionstarttime") - and ( - not hasattr(reporter, "_session_start") - or ( - hasattr(reporter, "_session_start") - and not hasattr(reporter._session_start, "time") - ) + not hasattr(reporter, "_session_start") + or ( + hasattr(reporter, "_session_start") + and not hasattr(reporter._session_start, "time") ) ): return From b8686dc65d43a2baf9a5280c2ee1b8ad6ff0a7aa Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:12:07 -0400 Subject: [PATCH 09/14] Update the documentation --- README.md | 8 ++++---- examples/ReadMe.md | 11 +++++------ examples/cdp_mode/ReadMe.md | 7 +++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8fd4233b601..bb777b2fb38 100755 --- a/README.md +++ b/README.md @@ -507,10 +507,10 @@ pip install -e . ▶️ Here's sample output from a chromedriver download. (click to expand) ```zsh -*** chromedriver to download = 148.0.7778.178 (Latest Stable) +*** chromedriver to download = 149.0.7827.54 (Latest Stable) Downloading chromedriver-mac-arm64.zip from: -https://storage.googleapis.com/chrome-for-testing-public/148.0.7778.178/mac-arm64/chromedriver-mac-arm64.zip ... +https://storage.googleapis.com/chrome-for-testing-public/149.0.7827.54/mac-arm64/chromedriver-mac-arm64.zip ... Download Complete! Extracting ['chromedriver'] from chromedriver-mac-arm64.zip ... @@ -520,8 +520,8 @@ The file [chromedriver] was saved to: ~/github/SeleniumBase/seleniumbase/drivers/ chromedriver -Making [chromedriver 148.0.7778.178] executable ... -[chromedriver 148.0.7778.178] is now ready for use! +Making [chromedriver 149.0.7827.54] executable ... +[chromedriver 149.0.7827.54] is now ready for use! ``` diff --git a/examples/ReadMe.md b/examples/ReadMe.md index 3162b7f0169..fc1f6e06a96 100644 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -4,21 +4,20 @@

SeleniumBase Demo Page

-* SeleniumBase "tests" are run with pytest. +* Many SeleniumBase "tests" run with pytest. * Chrome is the default browser if not specified. -* Tests are structured using [25 unique syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md). +* Tests are structured using [25 unique design patterns](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md). * Logs from test failures are saved to `./latest_logs/`. * Tests can be run with [multiple command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md). * Examples can be found in [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples). -* For stealthy examples, see [SeleniumBase/examples/cdp_mode/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode). +* Stealthy CDP Mode examples: [SeleniumBase/examples/cdp_mode/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode). +* Stealthy Playwright examples: [./examples/cdp_mode/playwright/](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md). (NOTE: Some example tests fail on purpose to demonstrate [logging features](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md).) -------- -

Example tests with run commands to help you get started:

- --------- +

Example tests with run commands:

Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py): (Default option: `--chrome`) diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 711ee34d27e..aa168cbca3e 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -323,6 +323,7 @@ with SB(uc=True, test=True, ad_block=True) as sb: price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") + item.scroll_into_view() ``` @@ -662,8 +663,7 @@ await tab.set_window_size(left=0, top=0, width=1280, height=1024) await tab.set_window_rect(left=0, top=0, width=1280, height=1024) await tab.activate() await tab.bring_to_front() -await tab.set_window_state( - left=0, top=0, width=1280, height=720, state="normal") +await tab.set_window_state(left=0, top=0, width=1280, height=720, state="normal") await tab.get_navigation_history() await tab.get_user_agent() await tab.get_cookie_string() @@ -677,8 +677,7 @@ await tab.wait_for(selector="", text="", timeout=10) await tab.set_attributes(selector, attribute, value) await tab.internalize_links() await tab.download_file(url, filename=None) -await tab.save_screenshot( - filename="auto", format="png", full_page=False) +await tab.save_screenshot(filename="auto", format="png", full_page=False) await tab.print_to_pdf(filename="auto") await tab.set_download_path(path) await tab.get_all_linked_sources() From 41666ae760049966f0be96acd6aa5b04c77b8a15 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:15:03 -0400 Subject: [PATCH 10/14] Update examples --- examples/cdp_mode/raw_ad_blocking.py | 4 +++- examples/cdp_mode/raw_antibot.py | 7 ++++--- examples/cdp_mode/raw_basic_cdp.py | 6 +++--- examples/cdp_mode/raw_cdp_pixelscan.py | 3 ++- examples/cdp_mode/raw_cdp_tavus.py | 9 +++++++++ examples/cdp_mode/raw_cdp_walmart.py | 1 + examples/cdp_mode/raw_cf.py | 3 ++- examples/cdp_mode/raw_mfa_login.py | 6 +++--- examples/cdp_mode/raw_mobile_roblox.py | 2 ++ examples/cdp_mode/raw_multi_cdp.py | 2 +- examples/cdp_mode/raw_pixelscan.py | 3 ++- examples/cdp_mode/raw_tavus.py | 10 ++++++++++ examples/cdp_mode/raw_walmart.py | 1 + examples/cdp_mode/raw_wsform.py | 5 ++++- examples/presenter/multi_uc.py | 25 +++++++++---------------- examples/raw_antibot_login.py | 6 ++++-- examples/raw_brotector_captcha.py | 11 +++++------ examples/raw_cdp_logging.py | 2 ++ examples/raw_form_turnstile.py | 9 +++++---- examples/raw_gui_click.py | 9 ++++++--- examples/raw_hobbit.py | 8 +++----- examples/raw_uc_events.py | 2 ++ examples/raw_uc_mode.py | 5 ++++- examples/uc_cdp_events.py | 2 ++ examples/verify_undetected.py | 17 ++++++++--------- 25 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 examples/cdp_mode/raw_cdp_tavus.py create mode 100644 examples/cdp_mode/raw_tavus.py diff --git a/examples/cdp_mode/raw_ad_blocking.py b/examples/cdp_mode/raw_ad_blocking.py index 783e8334bc2..5567d3a6367 100644 --- a/examples/cdp_mode/raw_ad_blocking.py +++ b/examples/cdp_mode/raw_ad_blocking.py @@ -1,3 +1,5 @@ +"""Test Ad-Blocking using mycdp.network.set_blocked_urls(). +tab.send() is the stealthy version of execute_cdp_cmd().""" import mycdp from seleniumbase import decorators from seleniumbase import sb_cdp @@ -28,4 +30,4 @@ async def block_urls(tab): sb.assert_false("doubleclick.net" in source) sb.assert_false("google-analytics.com" in source) sb.post_message("Blocking was successful!") - sb.driver.quit() + sb.quit() diff --git a/examples/cdp_mode/raw_antibot.py b/examples/cdp_mode/raw_antibot.py index 58036f418e6..0c999265d03 100644 --- a/examples/cdp_mode/raw_antibot.py +++ b/examples/cdp_mode/raw_antibot.py @@ -1,14 +1,15 @@ +"""CDP Mode bypasses bot-detection and performs stealthy actions. +sb.press_keys() is a slower sb.type() for human-like speed.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/antibot/login" sb.activate_cdp_mode(url) sb.press_keys("input#username", "demo_user") - sb.press_keys("input#password", "secret_pass") + sb.type("input#password", "secret_pass") sb.click("button#myButton") - sb.sleep(1.5) + sb.sleep(1.4) sb.click("a#log-in") sb.assert_text("Welcome!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") - sb.sleep(1.5) diff --git a/examples/cdp_mode/raw_basic_cdp.py b/examples/cdp_mode/raw_basic_cdp.py index 126ddcb8360..dddcb0af6b9 100644 --- a/examples/cdp_mode/raw_basic_cdp.py +++ b/examples/cdp_mode/raw_basic_cdp.py @@ -1,7 +1,7 @@ from seleniumbase import sb_cdp -url = "https://seleniumbase.io/simple/login" -sb = sb_cdp.Chrome(url) +sb = sb_cdp.Chrome() +sb.open("https://seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') @@ -14,4 +14,4 @@ print(nav_item.text) sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") -sb.driver.stop() +sb.quit() diff --git a/examples/cdp_mode/raw_cdp_pixelscan.py b/examples/cdp_mode/raw_cdp_pixelscan.py index 0a73349c161..945cc6ad75e 100644 --- a/examples/cdp_mode/raw_cdp_pixelscan.py +++ b/examples/cdp_mode/raw_cdp_pixelscan.py @@ -1,6 +1,6 @@ from seleniumbase import sb_cdp -sb = sb_cdp.Chrome(incognito=True) +sb = sb_cdp.Chrome(guest=True, ad_block=True) sb.open("https://pixelscan.net/fingerprint-check") sb.sleep(1) sb.wait_for_element("pxlscn-dynamic-ad") @@ -13,3 +13,4 @@ sb.highlight("span.status-success") sb.highlight("pxlscn-fingerprint-masking p") sb.highlight("pxlscn-bot-detection p") +print("Bot Not Detected") diff --git a/examples/cdp_mode/raw_cdp_tavus.py b/examples/cdp_mode/raw_cdp_tavus.py new file mode 100644 index 00000000000..c398391110f --- /dev/null +++ b/examples/cdp_mode/raw_cdp_tavus.py @@ -0,0 +1,9 @@ +from seleniumbase import sb_cdp + +sb = sb_cdp.Chrome() +sb.open("platform.tavus.io/auth/sign-in?is_developer=true") +sb.sleep(3) +sb.solve_captcha() +sb.sleep(1) +sb.assert_element('input[type="email"]') +sb.assert_element('button[type="submit"]') diff --git a/examples/cdp_mode/raw_cdp_walmart.py b/examples/cdp_mode/raw_cdp_walmart.py index ceafddb2c10..73e49fab54c 100644 --- a/examples/cdp_mode/raw_cdp_walmart.py +++ b/examples/cdp_mode/raw_cdp_walmart.py @@ -43,4 +43,5 @@ price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") + item.scroll_into_view() sb.driver.stop() diff --git a/examples/cdp_mode/raw_cf.py b/examples/cdp_mode/raw_cf.py index 452c6958a9b..d3383161a97 100644 --- a/examples/cdp_mode/raw_cf.py +++ b/examples/cdp_mode/raw_cf.py @@ -1,4 +1,5 @@ -"""Using CDP Mode to bypass CAPTCHAs in different ways.""" +"""Using CDP Mode to bypass CAPTCHAs in different ways. +PyAutoGUI is installed automatically if not already.""" from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: diff --git a/examples/cdp_mode/raw_mfa_login.py b/examples/cdp_mode/raw_mfa_login.py index def87484d01..66c9bce30e3 100644 --- a/examples/cdp_mode/raw_mfa_login.py +++ b/examples/cdp_mode/raw_mfa_login.py @@ -1,7 +1,7 @@ from seleniumbase import sb_cdp -url = "https://seleniumbase.io/realworld/login" -sb = sb_cdp.Chrome(url) +sb = sb_cdp.Chrome() +sb.open("https://seleniumbase.io/realworld/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") @@ -9,4 +9,4 @@ sb.click('a:contains("This Page")') sb.highlight("h1") sb.highlight("img#image1") -sb.driver.stop() +sb.quit() diff --git a/examples/cdp_mode/raw_mobile_roblox.py b/examples/cdp_mode/raw_mobile_roblox.py index eee8c146a2e..aaeb5e0b363 100644 --- a/examples/cdp_mode/raw_mobile_roblox.py +++ b/examples/cdp_mode/raw_mobile_roblox.py @@ -1,3 +1,5 @@ +"""Test mobile emulation via mycdp.emulation.set_device_metrics_override(). +tab.send() is a stealthy version of Selenium's execute_cdp_cmd() method.""" import mycdp from seleniumbase import SB diff --git a/examples/cdp_mode/raw_multi_cdp.py b/examples/cdp_mode/raw_multi_cdp.py index dbec0400be3..56ea75ea9c6 100644 --- a/examples/cdp_mode/raw_multi_cdp.py +++ b/examples/cdp_mode/raw_multi_cdp.py @@ -12,7 +12,7 @@ def main(url): sb.highlight("button") sb.click("button") sb.sleep(2) - sb.driver.quit() + sb.quit() if __name__ == "__main__": diff --git a/examples/cdp_mode/raw_pixelscan.py b/examples/cdp_mode/raw_pixelscan.py index 8976f222fc7..1ceedc79a79 100644 --- a/examples/cdp_mode/raw_pixelscan.py +++ b/examples/cdp_mode/raw_pixelscan.py @@ -1,6 +1,6 @@ from seleniumbase import SB -with SB(uc=True, test=True, incognito=True, ad_block=True) as sb: +with SB(uc=True, test=True, guest=True, ad_block=True) as sb: url = "https://pixelscan.net/fingerprint-check" sb.activate_cdp_mode(url) sb.sleep(1) @@ -14,3 +14,4 @@ sb.cdp.highlight("span.status-success") sb.cdp.highlight("pxlscn-fingerprint-masking p") sb.cdp.highlight("pxlscn-bot-detection p") + print("Bot Not Detected") diff --git a/examples/cdp_mode/raw_tavus.py b/examples/cdp_mode/raw_tavus.py new file mode 100644 index 00000000000..c80f848dd52 --- /dev/null +++ b/examples/cdp_mode/raw_tavus.py @@ -0,0 +1,10 @@ +from seleniumbase import SB + +with SB(uc=True, test=True) as sb: + sb.activate_cdp_mode() + sb.open("platform.tavus.io/auth/sign-in?is_developer=true") + sb.sleep(3) + sb.solve_captcha() + sb.sleep(1) + sb.assert_element('input[type="email"]') + sb.assert_element('button[type="submit"]') diff --git a/examples/cdp_mode/raw_walmart.py b/examples/cdp_mode/raw_walmart.py index 1fccb4071fb..b39a20cbe6e 100644 --- a/examples/cdp_mode/raw_walmart.py +++ b/examples/cdp_mode/raw_walmart.py @@ -44,3 +44,4 @@ price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") + item.scroll_into_view() diff --git a/examples/cdp_mode/raw_wsform.py b/examples/cdp_mode/raw_wsform.py index aee5bbb1567..3046e40016c 100644 --- a/examples/cdp_mode/raw_wsform.py +++ b/examples/cdp_mode/raw_wsform.py @@ -1,3 +1,6 @@ +"""CDP Mode for bypassing bot-detection & CAPTCHAs. +Note: sb.uc_gui_click_captcha() requires PyAutoGUI, +which is installed automatically if not already.""" from seleniumbase import SB with SB(uc=True, test=True, locale="en", incognito=True) as sb: @@ -5,5 +8,5 @@ sb.activate_cdp_mode(url) sb.sleep(2) sb.scroll_into_view("div.grid") - sb.uc_gui_click_captcha() + sb.uc_gui_click_captcha() # PyAutoGUI mouse click sb.sleep(2) diff --git a/examples/presenter/multi_uc.py b/examples/presenter/multi_uc.py index de7e1853f18..c56d3720b90 100644 --- a/examples/presenter/multi_uc.py +++ b/examples/presenter/multi_uc.py @@ -1,4 +1,4 @@ -"""Part of the UC presentation""" +"""Part of the UC presentation. (Upgraded for CDP Mode.)""" import pytest from random import randint from seleniumbase import BaseCase @@ -7,18 +7,11 @@ @pytest.mark.parametrize("", [[]] * 3) def test_multi_threaded(sb): - url = "https://gitlab.com/users/sign_in" - sb.driver.uc_open_with_reconnect(url, 3) - sb.set_window_rect(randint(0, 755), randint(38, 403), 700, 500) - try: - sb.assert_text("Username", '[for="user_login"]', timeout=3) - sb.post_message("SeleniumBase wasn't detected", duration=4) - sb._print("\n Success! Website did not detect Selenium! ") - except Exception: - sb.driver.uc_open_with_reconnect(url, 3) - try: - sb.assert_text("Username", '[for="user_login"]', timeout=3) - sb.post_message("SeleniumBase wasn't detected", duration=4) - sb._print("\n Success! Website did not detect Selenium! ") - except Exception: - sb.fail('Selenium was detected! Try using: "pytest --uc"') + sb.activate_cdp_mode() # If not UC Mode, then 2nd browser + sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520) + sb.open("https://gitlab.com/users/sign_in") + sb.sleep(2) + sb.solve_captcha() + sb.assert_text("Username", '[for="user_login"]', timeout=3) + sb.post_message("SeleniumBase wasn't detected", duration=4) + sb._print("\n Success: SeleniumBase wasn't detected! ") diff --git a/examples/raw_antibot_login.py b/examples/raw_antibot_login.py index 0ee882616b1..6950fdbf81d 100644 --- a/examples/raw_antibot_login.py +++ b/examples/raw_antibot_login.py @@ -1,4 +1,6 @@ -"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" +"""UC Mode has PyAutoGUI methods for stealthy actions. +PyAutoGUI gets installed automatically if not already. +This old UC Mode code is made obsolete by CDP Mode.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: @@ -7,7 +9,7 @@ sb.uc_gui_write("\t" + "demo_user") sb.uc_gui_write("\t" + "secret_pass") sb.uc_gui_press_keys("\t" + " ") # For Single-char keys - sb.sleep(1.5) + sb.sleep(1.4) sb.uc_gui_press_keys(["\t", "ENTER"]) # Multi-char keys sb.reconnect(1.8) sb.assert_text("Welcome!", "h1") diff --git a/examples/raw_brotector_captcha.py b/examples/raw_brotector_captcha.py index b123b29f274..5f8fcee6b8c 100644 --- a/examples/raw_brotector_captcha.py +++ b/examples/raw_brotector_captcha.py @@ -1,9 +1,8 @@ -"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: - url = "https://seleniumbase.io/apps/brotector" - sb.uc_open_with_disconnect(url, 2.2) - sb.uc_gui_press_key("\t") - sb.uc_gui_press_key(" ") - sb.reconnect(2.2) + sb.activate_cdp_mode() + sb.open("https://seleniumbase.io/apps/brotector") + sb.click("button span#mySpan") + sb.assert_text("SUCCESS", "label#pText") + sb.highlight("label#pText") diff --git a/examples/raw_cdp_logging.py b/examples/raw_cdp_logging.py index 51885103cc8..9d893a760e9 100644 --- a/examples/raw_cdp_logging.py +++ b/examples/raw_cdp_logging.py @@ -1,3 +1,5 @@ +"""NOTE: This uses plain UC Mode, which was replaced by UC + CDP Mode. +PyAutoGUI is installed automatically for uc_gui methods if not already.""" from rich.pretty import pprint from seleniumbase import Driver diff --git a/examples/raw_form_turnstile.py b/examples/raw_form_turnstile.py index 7af920545bd..bf3d8eba76a 100644 --- a/examples/raw_form_turnstile.py +++ b/examples/raw_form_turnstile.py @@ -1,8 +1,9 @@ +"""CDP Mode for bypassing bot-detection & CAPTCHAs.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: - url = "seleniumbase.io/apps/form_turnstile" - sb.uc_open_with_reconnect(url, 1.1) + sb.activate_cdp_mode() + sb.open("seleniumbase.io/apps/form_turnstile") sb.press_keys("#name", "SeleniumBase") sb.press_keys("#email", "test@test.test") sb.press_keys("#phone", "1-555-555-5555") @@ -12,8 +13,8 @@ sb.click('span:contains("9:00 PM")') sb.highlight_click('input[value="AR"] + span') sb.click('input[value="cc"] + span') - sb.scroll_to('div[class*="cf-turnstile"]') - sb.uc_gui_handle_captcha() + sb.scroll_down(40) + sb.solve_captcha() sb.highlight("img#captcha-success", timeout=3) sb.highlight_click('button:contains("Request & Pay")') sb.highlight("img#submit-success") diff --git a/examples/raw_gui_click.py b/examples/raw_gui_click.py index ec96cb51ae5..e4a22aa4bc0 100644 --- a/examples/raw_gui_click.py +++ b/examples/raw_gui_click.py @@ -1,8 +1,11 @@ +"""CDP Mode for bypassing bot-detection & CAPTCHAs. +Note: sb.uc_gui_click_captcha() requires PyAutoGUI, +which is installed automatically if not already.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: - url = "seleniumbase.io/apps/form_turnstile" - sb.activate_cdp_mode(url) + sb.activate_cdp_mode() + sb.open("seleniumbase.io/apps/form_turnstile") sb.press_keys("#name", "SeleniumBase") sb.press_keys("#email", "test@test.test") sb.press_keys("#phone", "1-555-555-5555") @@ -14,7 +17,7 @@ sb.click('input[value="cc"] + span') sb.scroll_to('div[class*="cf-turnstile"]') sb.scroll_down(40) - sb.uc_gui_click_captcha() + sb.uc_gui_click_captcha() # PyAutoGUI mouse click sb.highlight("img#captcha-success", timeout=3) sb.highlight_click('button:contains("Request & Pay")') sb.highlight("img#submit-success") diff --git a/examples/raw_hobbit.py b/examples/raw_hobbit.py index 51fbc98dd83..2fc91b0d510 100644 --- a/examples/raw_hobbit.py +++ b/examples/raw_hobbit.py @@ -1,11 +1,9 @@ -"""UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: - url = "https://seleniumbase.io/hobbit/login" - sb.uc_open_with_disconnect(url, 2.2) - sb.uc_gui_press_keys("\t ") - sb.reconnect(1.5) + sb.activate_cdp_mode() + sb.open("https://seleniumbase.io/hobbit/login") + sb.click("button span#mySpan") sb.assert_text("Welcome to Middle Earth!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") diff --git a/examples/raw_uc_events.py b/examples/raw_uc_events.py index 9e35f781db2..e55e63debda 100644 --- a/examples/raw_uc_events.py +++ b/examples/raw_uc_events.py @@ -1,3 +1,5 @@ +"""NOTE: This uses plain UC Mode, which was replaced by UC + CDP Mode. +PyAutoGUI is installed automatically for uc_gui methods if not already.""" from rich.pretty import pprint from seleniumbase import SB diff --git a/examples/raw_uc_mode.py b/examples/raw_uc_mode.py index 3634bd605d7..29c1e2ec27b 100644 --- a/examples/raw_uc_mode.py +++ b/examples/raw_uc_mode.py @@ -1,4 +1,7 @@ -"""SB Manager using UC Mode for evading bot-detection.""" +"""SB Manager using UC Mode for evading bot-detection. +Note that plain UC Mode was replaced by the newer CDP Mode: +Instead of uc_open_with_reconnect, use activate_cdp_mode(). +Instead of uc_gui_click_captcha(), use solve_captcha().""" from seleniumbase import SB with SB(uc=True, test=True) as sb: diff --git a/examples/uc_cdp_events.py b/examples/uc_cdp_events.py index 526dd70e4e4..947e1d21214 100644 --- a/examples/uc_cdp_events.py +++ b/examples/uc_cdp_events.py @@ -1,3 +1,5 @@ +"""NOTE: This uses plain UC Mode, which was replaced by UC + CDP Mode. +PyAutoGUI is installed automatically for uc_gui methods if not already.""" from rich.pretty import pprint from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc", "--uc-cdp") diff --git a/examples/verify_undetected.py b/examples/verify_undetected.py index 555c485df8e..d99e0f1257f 100644 --- a/examples/verify_undetected.py +++ b/examples/verify_undetected.py @@ -1,17 +1,16 @@ -"""Determine if your browser is detectable by anti-bot services. -Some sites use scripts to detect Selenium, and then block you. -To evade detection, add --uc as a pytest command-line option.""" +"""Verify if your browser is detectable by anti-bot services. +To evade detection, add --uc as a pytest command-line option. +UC + CDP Mode is used: CDP Mode is activated from UC Mode.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class UndetectedTest(BaseCase): def test_browser_is_undetected(self): - url = "https://gitlab.com/users/sign_in" - if not self.undetectable: - self.get_new_driver(undetectable=True) - self.uc_open_with_reconnect(url) - self.uc_gui_click_captcha() + self.activate_cdp_mode() # If not UC Mode, then 2nd browser + self.open("https://gitlab.com/users/sign_in") + self.sleep(2) + self.solve_captcha() # (Only runs when a CAPTCHA is visible) self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) - self._print("\n Success! Website did not detect Selenium! ") + self._print("\n Success: SeleniumBase wasn't detected! ") From 1c234e270d233eb8b0709276c470f36a99566d97 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:15:56 -0400 Subject: [PATCH 11/14] Refresh mkdocs dependencies --- mkdocs_build/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 5ed1539a88f..b601ffcf1f7 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -3,10 +3,10 @@ regex>=2026.5.9 pymdown-extensions>=10.21.3 -pipdeptree>=2.35.3 +pipdeptree>=3.1.0 python-dateutil>=2.8.2 +click>=8.4.1 Markdown==3.10.2 -click==8.4.1 ghp-import==2.1.0 watchdog==6.0.0 cairocffi==1.7.1 From f709444731c7c6285a31aaeaa13a2e439d51be43 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:17:44 -0400 Subject: [PATCH 12/14] Refresh Python dependencies --- requirements.txt | 4 ++-- setup.py | 36 ++++++------------------------------ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/requirements.txt b/requirements.txt index b0909de473a..cd44d8637c5 100755 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ exceptiongroup>=1.3.1 websockets~=15.0.1;python_version<"3.10" websockets>=16.0;python_version>="3.10" filelock~=3.19.1;python_version<"3.10" -filelock>=3.29.0;python_version>="3.10" +filelock>=3.29.1;python_version>="3.10" fasteners>=0.20 mycdp>=1.3.7 pynose>=1.5.5 @@ -30,7 +30,7 @@ pygments>=2.20.0 pyreadline3>=3.5.4;platform_system=="Windows" tabcompleter>=1.4.1 pdbp>=1.8.2 -idna>=3.17 +idna>=3.18 charset-normalizer>=3.4.7,<4 urllib3>=1.26.20,<2;python_version<"3.10" urllib3>=2.7.0,<3;python_version>="3.10" diff --git a/setup.py b/setup.py index 8fc93825a8b..cb92328e150 100755 --- a/setup.py +++ b/setup.py @@ -173,7 +173,7 @@ 'websockets~=15.0.1;python_version<"3.10"', 'websockets>=16.0;python_version>="3.10"', 'filelock~=3.19.1;python_version<"3.10"', - 'filelock>=3.29.0;python_version>="3.10"', + 'filelock>=3.29.1;python_version>="3.10"', 'fasteners>=0.20', 'mycdp>=1.3.7', 'pynose>=1.5.5', @@ -193,7 +193,7 @@ 'pyreadline3>=3.5.4;platform_system=="Windows"', 'tabcompleter>=1.4.1', 'pdbp>=1.8.2', - 'idna>=3.17', + 'idna>=3.18', 'charset-normalizer>=3.4.7,<4', 'urllib3>=1.26.20,<2;python_version<"3.10"', 'urllib3>=2.7.0,<3;python_version>="3.10"', @@ -243,9 +243,9 @@ # Usage: pytest --alluredir=allure_results # Serve: allure serve allure_results "allure": [ - 'allure-pytest>=2.13.5', - 'allure-python-commons>=2.13.5', - 'allure-behave>=2.13.5', + 'allure-pytest>=2.16.0', + 'allure-python-commons>=2.16.0', + 'allure-behave>=2.16.0', ], # pip install -e .[coverage] # Usage: coverage run -m pytest; coverage html; coverage report @@ -262,12 +262,6 @@ 'pyflakes==3.4.0', 'pycodestyle==2.14.0', ], - # pip install -e .[ipdb] - # (Not needed for debugging anymore. SeleniumBase now includes "pdbp".) - "ipdb": [ - "ipdb==0.13.13", - 'ipython==7.34.0', - ], # pip install -e .[mss] # (An optional library for tile_windows() in CDP Mode.) "mss": [ @@ -293,7 +287,7 @@ # (If you see [SSL: CERTIFICATE_VERIFY_FAILED], then get this.) # (May help those with corporate self-signed certs on Windows.) "pip-system-certs": [ - 'pip-system-certs==4.0;platform_system=="Windows"', + 'pip-system-certs>=4.0;platform_system=="Windows"', ], # pip install -e .[proxy] # Usage: proxy @@ -313,24 +307,6 @@ "pyautogui": [ 'PyAutoGUI>=0.9.54;platform_system!="Linux"', ], - # pip install -e .[selenium-stealth] - "selenium-stealth": [ - 'selenium-stealth==1.0.6', - ], - # pip install -e .[selenium-wire] - "selenium-wire": [ - 'selenium-wire==5.1.0', - 'pyOpenSSL>=24.2.1', - 'pyparsing>=3.1.4', - 'Brotli==1.1.0', - 'blinker==1.7.0', # Newer ones had issues - 'h2==4.1.0', - 'hpack==4.0.0', - 'hyperframe==6.0.1', - 'kaitaistruct==0.10', - 'pyasn1==0.6.1', - 'zstandard>=0.23.0', - ], }, packages=[ "seleniumbase", From 9e78a662ab7c5fef9fa80ab4357510848cf913a6 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:18:09 -0400 Subject: [PATCH 13/14] Officially support Python 3.15 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index cb92328e150..5aece7f1806 100755 --- a/setup.py +++ b/setup.py @@ -142,6 +142,7 @@ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP :: Browsers", "Topic :: Scientific/Engineering", From 583e6cd6b97c17dcc5f9fc52b440727b3216d6e8 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Fri, 5 Jun 2026 01:18:29 -0400 Subject: [PATCH 14/14] Version 4.49.6 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 85754b965af..2aab272ac42 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.49.5" +__version__ = "4.49.6"