From 884b428bd9b8fde4b72dcff50dcb88f415ee7b7b Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 29 Nov 2025 15:29:52 -0500 Subject: [PATCH 1/4] Update CDP Mode --- seleniumbase/core/browser_launcher.py | 27 ++++++++++++++----- seleniumbase/core/sb_cdp.py | 9 ++++--- seleniumbase/fixtures/base_case.py | 2 +- seleniumbase/plugins/pytest_plugin.py | 2 +- seleniumbase/plugins/selenium_plugin.py | 2 +- seleniumbase/undetected/cdp_driver/browser.py | 17 ++++++++++-- .../undetected/cdp_driver/cdp_util.py | 10 +++++++ seleniumbase/undetected/cdp_driver/config.py | 3 ++- 8 files changed, 57 insertions(+), 15 deletions(-) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index d362d786ec1..08779bfcce6 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -445,11 +445,11 @@ def has_captcha(text): or 'action="/?__cf_chl_f_tk' in text or 'id="challenge-widget-' in text or 'src="chromedriver.js"' in text + or "com/recaptcha/api.js" in text or 'class="g-recaptcha"' in text or 'content="Pixelscan"' in text or 'id="challenge-form"' in text or "window._cf_chl_opt" in text - or "/recaptcha/api.js" in text or "/turnstile/" in text ): return True @@ -694,6 +694,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs): xvfb=xvfb, xvfb_metrics=xvfb_metrics, browser_executable_path=binary_location, + mobile=getattr(sb_config, "_cdp_mobile_mode", None), ) ) loop.run_until_complete(driver.cdp_base.wait(0)) @@ -2761,6 +2762,8 @@ def _set_chrome_options( included_disabled_features.append("SidePanelPinning") included_disabled_features.append("UserAgentClientHint") included_disabled_features.append("DisableLoadExtensionCommandLineSwitch") + included_disabled_features.append("WebAuthentication") + included_disabled_features.append("PasskeyAuth") for item in extra_disabled_features: if item not in included_disabled_features: included_disabled_features.append(item) @@ -3056,11 +3059,6 @@ def get_driver( if _special_binary_exists(binary_location, "atlas"): driver_dir = DRIVER_DIR_ATLAS sb_config._cdp_browser = "atlas" - if undetectable and mobile_emulator: - # For stealthy mobile mode, see the CDP Mode examples - # to learn how to properly configure it. - user_agent = None # Undo the override - mobile_emulator = False # Instead, set from CDP Mode if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) @@ -3075,6 +3073,23 @@ def get_driver( if browser_name in constants.ChromiumSubs.chromium_subs: browser_name = "chrome" browser_name = browser_name.lower() + if is_using_uc(undetectable, browser_name): + if ad_block_on: + sb_config.ad_block_on = True + else: + sb_config.ad_block_on = False + if disable_csp: + sb_config.disable_csp = True + else: + sb_config.disable_csp = False + if mobile_emulator: + # For stealthy mobile mode, see the CDP Mode examples + # to learn how to properly configure it. + user_agent = None # Undo the override + mobile_emulator = False # Instead, set from CDP Mode + sb_config._cdp_mobile_mode = True + else: + sb_config._cdp_mobile_mode = False if headless2 and browser_name == constants.Browser.FIREFOX: headless2 = False # Only for Chromium headless = True diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index c0bb26a2137..d030d1b9fa4 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -1922,8 +1922,8 @@ def _on_a_cf_turnstile_page(self, source=None): return False def _on_a_g_recaptcha_page(self, source=None): - time.sleep(0.25) - self.loop.run_until_complete(self.page.wait(0.25)) + time.sleep(0.2) + self.loop.run_until_complete(self.page.wait(0.2)) source = self.get_page_source() if ( ( @@ -1934,7 +1934,7 @@ def _on_a_g_recaptcha_page(self, source=None): ): self.loop.run_until_complete(self.page.wait(0.1)) return True - elif "/recaptcha/api.js" in source: + elif "com/recaptcha/api.js" in source: time.sleep(1.6) # Still loading self.loop.run_until_complete(self.page.wait(0.1)) return True @@ -2330,6 +2330,9 @@ def gui_hover_element(self, selector, timeframe=0.25): self.loop.run_until_complete(self.page.wait()) def gui_hover_and_click(self, hover_selector, click_selector): + if getattr(sb_config, "_cdp_mobile_mode", None): + self.select(click_selector).click() + return gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index cc41ef6f4cd..4413fc06e50 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -15585,7 +15585,7 @@ def setUp(self, masterqa_mode=False): "Invalid input for Mobile Emulator device metrics!\n" "Expecting a comma-separated string with integer values\n" "for Width/Height, and an int or float for Pixel-Ratio.\n" - 'Example: --metrics="411,731,3" ' + 'Example: --metrics="412,732,3" ' ) if len(metrics_list) != 3: raise Exception(exception_string) diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index e3721b7f511..43ae9890424 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -625,7 +625,7 @@ def pytest_addoption(parser): help="""Designates the three device metrics of the mobile emulator: CSS Width, CSS Height, and Pixel-Ratio. Format: A comma-separated string with the 3 values. - Examples: "375,734,5" or "411,731,3" or "390,715,3" + Examples: "375,734,5" or "412,732,3" or "390,715,3" Default: None. (Will use default values if None)""", ) parser.addoption( diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index 42733e1aa56..b25f3080262 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -367,7 +367,7 @@ def options(self, parser, env): help="""Designates the three device metrics of the mobile emulator: CSS Width, CSS Height, and Pixel-Ratio. Format: A comma-separated string with the 3 values. - Examples: "375,734,5" or "411,731,3" or "390,715,3" + Examples: "375,734,5" or "412,732,3" or "390,715,3" Default: None. (Will use default values if None)""", ) parser.addoption( diff --git a/seleniumbase/undetected/cdp_driver/browser.py b/seleniumbase/undetected/cdp_driver/browser.py index 03768228bc6..c410b1ba7b8 100644 --- a/seleniumbase/undetected/cdp_driver/browser.py +++ b/seleniumbase/undetected/cdp_driver/browser.py @@ -327,6 +327,7 @@ async def get( _cdp_platform = None _cdp_disable_csp = None _cdp_geolocation = None + _cdp_mobile_mode = None _cdp_recorder = None _cdp_ad_block = None if getattr(sb_config, "_cdp_timezone", None): @@ -339,8 +340,12 @@ async def get( _cdp_platform = sb_config._cdp_platform if getattr(sb_config, "_cdp_geolocation", None): _cdp_geolocation = sb_config._cdp_geolocation + if getattr(sb_config, "_cdp_mobile_mode", None): + _cdp_mobile_mode = sb_config._cdp_mobile_mode if getattr(sb_config, "ad_block_on", None): _cdp_ad_block = sb_config.ad_block_on + if getattr(sb_config, "disable_csp", None): + _cdp_disable_csp = sb_config.disable_csp if "timezone" in kwargs: _cdp_timezone = kwargs["timezone"] elif "tzone" in kwargs: @@ -361,12 +366,14 @@ async def get( _cdp_platform = kwargs["plat"] if "disable_csp" in kwargs: _cdp_disable_csp = kwargs["disable_csp"] - elif hasattr(sb_config, "disable_csp"): - _cdp_disable_csp = sb_config.disable_csp if "geolocation" in kwargs: _cdp_geolocation = kwargs["geolocation"] elif "geoloc" in kwargs: _cdp_geolocation = kwargs["geoloc"] + if "ad_block" in kwargs: + _cdp_ad_block = kwargs["ad_block"] + if "mobile" in kwargs: + _cdp_mobile_mode = kwargs["mobile"] if "recorder" in kwargs: _cdp_recorder = kwargs["recorder"] await connection.sleep(0.01) @@ -422,6 +429,12 @@ async def get( await connection.set_geolocation(_cdp_geolocation) if _cdp_disable_csp: await connection.send(cdp.page.set_bypass_csp(enabled=True)) + if _cdp_mobile_mode: + await connection.send( + cdp.emulation.set_device_metrics_override( + width=412, height=732, device_scale_factor=3, mobile=True + ) + ) # (The code below is for the Chrome 142 extension fix) if ( getattr(sb_config, "_cdp_proxy", None) diff --git a/seleniumbase/undetected/cdp_driver/cdp_util.py b/seleniumbase/undetected/cdp_driver/cdp_util.py index 73a0393562f..30953bf6bb1 100644 --- a/seleniumbase/undetected/cdp_driver/cdp_util.py +++ b/seleniumbase/undetected/cdp_driver/cdp_util.py @@ -281,6 +281,7 @@ async def start( proxy: Optional[str] = None, # "host:port" or "user:pass@host:port" tzone: Optional[str] = None, # Eg "America/New_York", "Asia/Kolkata" geoloc: Optional[list | tuple] = None, # Eg (48.87645, 2.26340) + mobile: Optional[bool] = None, # Use Mobile Mode with default args disable_csp: Optional[str] = None, # Disable content security policy extension_dir: Optional[str] = None, # Chrome extension directory **kwargs: Optional[dict], @@ -353,6 +354,15 @@ async def start( guest = True else: guest = False + if mobile is None: + if "--mobile" in sys_argv: + mobile = True + else: + mobile = False + if mobile: + sb_config._cdp_mobile_mode = True + else: + sb_config._cdp_mobile_mode = False if ad_block is None: if "--ad-block" in sys_argv or "--ad_block" in sys_argv: ad_block = True diff --git a/seleniumbase/undetected/cdp_driver/config.py b/seleniumbase/undetected/cdp_driver/config.py index c9d40098409..1f78f1148ed 100644 --- a/seleniumbase/undetected/cdp_driver/config.py +++ b/seleniumbase/undetected/cdp_driver/config.py @@ -206,7 +206,8 @@ def __call__(self): "OptimizationTargetPrediction,OptimizationGuideModelDownloading," "SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4," "OptimizationHintsFetching,InterestFeedContentSuggestions," - "DisableLoadExtensionCommandLineSwitch" + "DisableLoadExtensionCommandLineSwitch," + "WebAuthentication,PasskeyAuth" ] if self.expert: args += [ From 8bb7dc6e90405e41bf80352729391430d21390e7 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 29 Nov 2025 15:31:41 -0500 Subject: [PATCH 2/4] Update ReadMe files --- examples/ReadMe.md | 2 +- examples/cdp_mode/ReadMe.md | 2 +- examples/chart_maker/ReadMe.md | 2 +- examples/presenter/ReadMe.md | 4 ++-- examples/tour_examples/ReadMe.md | 2 +- examples/translations/ReadMe.md | 2 +- examples/unit_tests/ReadMe.md | 2 +- examples/visual_testing/ReadMe.md | 2 +- help_docs/ReadMe.md | 2 +- help_docs/behave_gui.md | 2 +- help_docs/case_plans.md | 2 +- help_docs/commander.md | 2 +- help_docs/customizing_test_runs.md | 2 +- help_docs/demo_mode.md | 2 +- help_docs/desired_capabilities.md | 2 +- help_docs/features_list.md | 5 +++-- help_docs/handling_iframes.md | 2 +- help_docs/how_it_works.md | 2 +- help_docs/html_inspector.md | 2 +- help_docs/install.md | 2 +- help_docs/js_package_manager.md | 4 +++- help_docs/locale_codes.md | 4 ++-- help_docs/method_summary.md | 2 +- help_docs/mobile_testing.md | 4 ++-- help_docs/recorder_mode.md | 2 +- help_docs/translations.md | 2 +- help_docs/uc_mode.md | 2 +- help_docs/verify_webdriver.md | 2 +- help_docs/webdriver_installation.md | 4 ++-- integrations/github/workflows/extras.md | 2 +- seleniumbase/console_scripts/ReadMe.md | 4 ++-- seleniumbase/utilities/selenium_grid/ReadMe.md | 2 +- 32 files changed, 41 insertions(+), 38 deletions(-) diff --git a/examples/ReadMe.md b/examples/ReadMe.md index 616086c7851..c56f7465da3 100644 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Example Tests: +

Example Tests:

SeleniumBase Demo Page

diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 26aac55a0f4..dfd7ed0b5a3 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) CDP Mode πŸ™ +

CDP Mode πŸ™

πŸ™ SeleniumBase CDP Mode is a stealth mode of SeleniumBase that uses the Chrome Devtools Protocol (via MyCDP) to control the web browser. CDP Mode can be used either as a subset of SeleniumBase UC Mode, or via Pure CDP Mode (sb_cdp), which doesn't use WebDriver at all, and has a slightly different setup. diff --git a/examples/chart_maker/ReadMe.md b/examples/chart_maker/ReadMe.md index 03cdba41b10..63bea47e6f4 100644 --- a/examples/chart_maker/ReadMe.md +++ b/examples/chart_maker/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) πŸ“Š ChartMaker πŸ“Ά +

πŸ“Š ChartMaker πŸ“Ά

SeleniumBase ChartMaker lets you use Python to generate HTML charts.

diff --git a/examples/presenter/ReadMe.md b/examples/presenter/ReadMe.md index ffcd5a89c22..03763b6b303 100644 --- a/examples/presenter/ReadMe.md +++ b/examples/presenter/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) πŸ“‘ Presenter 🎞️ +

πŸ“‘ Presenter 🎞️

SeleniumBase Presenter (slide-maker) lets you use Python to generate HTML presentations.

@@ -21,7 +21,7 @@ cd examples/presenter pytest my_presentation.py ``` -**Here's a presentation with a chart:** +Here's a presentation with a chart:
diff --git a/examples/tour_examples/ReadMe.md b/examples/tour_examples/ReadMe.md index 8884a972a3f..d1231d6a38f 100644 --- a/examples/tour_examples/ReadMe.md +++ b/examples/tour_examples/ReadMe.md @@ -2,7 +2,7 @@

SeleniumBase Tour

-## [](https://github.com/seleniumbase/SeleniumBase/) Interactive Product Tours 🚎 +

🌏 Interactive Product Tours 🚎

Increase SaaS Product Adoption by 10x or more.

diff --git a/examples/translations/ReadMe.md b/examples/translations/ReadMe.md index 649367d4ab0..c01698eee1e 100644 --- a/examples/translations/ReadMe.md +++ b/examples/translations/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) 🌏 Translated Tests 🈺 +

🌏 Translated Tests 🈺

SeleniumBase supports the following 10 languages: English, Chinese, Dutch, French, Italian, Japanese, Korean, Portuguese, Russian, and Spanish. (Examples can be found in SeleniumBase/examples/translations) diff --git a/examples/unit_tests/ReadMe.md b/examples/unit_tests/ReadMe.md index 3cc6d2313a3..48152526a0d 100644 --- a/examples/unit_tests/ReadMe.md +++ b/examples/unit_tests/ReadMe.md @@ -1,5 +1,5 @@ -### pytest-specific unit tests +

pytest-specific unit tests

The tests in this folder are for basic verification of the SeleniumBase framework with pytest. diff --git a/examples/visual_testing/ReadMe.md b/examples/visual_testing/ReadMe.md index fa870a2eb70..0a3409d984f 100644 --- a/examples/visual_testing/ReadMe.md +++ b/examples/visual_testing/ReadMe.md @@ -2,7 +2,7 @@

SeleniumBase

-## [](https://github.com/seleniumbase/SeleniumBase/) Automated Visual Regression Testing +

Automated Visual Regression Testing

Automated Visual Regression Testing can help you detect when the layout of a web page has changed. Instead of comparing pixels from screenshots, layout differences can be detected by comparing HTML tags and attributes with a baseline. If a change is detected, it could mean that something broke, the web page was redesigned, or dynamic content changed. diff --git a/help_docs/ReadMe.md b/help_docs/ReadMe.md index 64d48dc4b77..be492e5b389 100644 --- a/help_docs/ReadMe.md +++ b/help_docs/ReadMe.md @@ -2,7 +2,7 @@

SeleniumBase

-## [](https://github.com/seleniumbase/SeleniumBase/) Help Docs +

Help Docs

πŸš€ Start | diff --git a/help_docs/behave_gui.md b/help_docs/behave_gui.md index e96afd76d62..bdfc63b59d7 100644 --- a/help_docs/behave_gui.md +++ b/help_docs/behave_gui.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Behave GUI / Commander πŸπŸŽ–οΈ +

SeleniumBase Behave GUI / Commander πŸπŸŽ–οΈ

πŸπŸŽ–οΈ The SeleniumBase Behave GUI lets you run behave scripts from a Desktop GUI.
diff --git a/help_docs/case_plans.md b/help_docs/case_plans.md index a7128a01c29..ad1772f62ba 100644 --- a/help_docs/case_plans.md +++ b/help_docs/case_plans.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Case Plans πŸ—‚οΈ +

SeleniumBase Case Plans πŸ—‚οΈ

diff --git a/help_docs/commander.md b/help_docs/commander.md index 7d5f125ee68..351a4da2d6d 100644 --- a/help_docs/commander.md +++ b/help_docs/commander.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Commander πŸŽ–οΈ +

SeleniumBase Commander πŸŽ–οΈ

πŸŽ–οΈ SeleniumBase Commander lets you run pytest scripts from a Desktop GUI.
diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md index b82bfc1092c..dc8d7f56a58 100644 --- a/help_docs/customizing_test_runs.md +++ b/help_docs/customizing_test_runs.md @@ -616,7 +616,7 @@ pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1 pytest test_swag_labs.py --mobile # Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio -pytest test_swag_labs.py --mobile --metrics="411,731,3" +pytest test_swag_labs.py --mobile --metrics="412,732,3" # Run mobile tests specifying the user agent pytest test_swag_labs.py --mobile --agent="Mozilla/5.0 (Linux; Android 9; Pixel 3 XL)" diff --git a/help_docs/demo_mode.md b/help_docs/demo_mode.md index 7e554cf9aeb..cdd9c913ede 100644 --- a/help_docs/demo_mode.md +++ b/help_docs/demo_mode.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Demo Mode 🎦 +

Demo Mode 🎦

SeleniumBase Example

diff --git a/help_docs/desired_capabilities.md b/help_docs/desired_capabilities.md index 6ac4881bb71..5fa7447a4bf 100644 --- a/help_docs/desired_capabilities.md +++ b/help_docs/desired_capabilities.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities +

Using Desired Capabilities

You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as BrowserStack or Sauce Labs). diff --git a/help_docs/features_list.md b/help_docs/features_list.md index dd34f9b7d4a..cdf2fdb9a86 100644 --- a/help_docs/features_list.md +++ b/help_docs/features_list.md @@ -5,7 +5,7 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Features: 🏰 +

SeleniumBase Features: 🏰

* A powerful Python framework for browser automation and E2E UI testing. * Includes [Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md) for instantly generating browser tests in Python. @@ -26,6 +26,7 @@ * Can run tests with a customized browser user agent. (``--agent=USER_AGENT_STRING``) * Can set a Chromium User Data Directory/Profile to load. (``--user-data-dir=DIR``) * Can [avoid detection](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md) by sites that try to block Selenium. (``--undetected``/``--uc``) +* Can [use CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) for advanced stealth abilities. (``activate_cdp_mode(url)``) * Can integrate with [selenium-wire](https://github.com/wkeeling/selenium-wire) for inspecting browser requests. (``--wire``) * Can load Chrome Extension ZIP files. (``--extension-zip=ZIP``) * Can load Chrome Extension folders. (``--extension-dir=DIR``) @@ -57,4 +58,4 @@

SeleniumBase

-[](https://seleniumbase.io/) + diff --git a/help_docs/handling_iframes.md b/help_docs/handling_iframes.md index 88d46255b87..b8996d0479b 100644 --- a/help_docs/handling_iframes.md +++ b/help_docs/handling_iframes.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) How to handle iframes +

How to handle iframes

πŸ–ΌοΈ iframes follow the same principle as new windows: You must first switch to the iframe if you want to perform actions in there: diff --git a/help_docs/how_it_works.md b/help_docs/how_it_works.md index 4e6b8f6f834..ff5f6765f70 100644 --- a/help_docs/how_it_works.md +++ b/help_docs/how_it_works.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) How SeleniumBase Works πŸ‘οΈ +

How SeleniumBase Works πŸ‘οΈ

diff --git a/help_docs/html_inspector.md b/help_docs/html_inspector.md index 6d18c8ccefc..5c0c98206cc 100644 --- a/help_docs/html_inspector.md +++ b/help_docs/html_inspector.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) The HTML Inspector πŸ•΅οΈ +

The HTML Inspector πŸ•΅οΈ

πŸ•΅οΈ HTML Inspector provides useful info about a web page. diff --git a/help_docs/install.md b/help_docs/install.md index 470473eee1e..a7d987a719b 100644 --- a/help_docs/install.md +++ b/help_docs/install.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Installation +

SeleniumBase Installation

If installing seleniumbase directly from PyPI, (the Python Package Index), use:

diff --git a/help_docs/js_package_manager.md b/help_docs/js_package_manager.md index 270f122ab18..eeade9efb5e 100644 --- a/help_docs/js_package_manager.md +++ b/help_docs/js_package_manager.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) JS Package Manager and Code Generators +

JS Package Manager and Code Generators

πŸ•ΉοΈ SeleniumBase lets you load JavaScript packages from any CDN link into any website via Python.

@@ -131,6 +131,8 @@ pytest test_dialog_boxes.py -------- + + [](https://github.com/seleniumbase/SeleniumBase/)
To learn more about SeleniumBase, check out the Docs Site:
diff --git a/help_docs/locale_codes.md b/help_docs/locale_codes.md index a9aae6af33c..44e80ce0f43 100644 --- a/help_docs/locale_codes.md +++ b/help_docs/locale_codes.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Language Locale Codes +

Language Locale Codes

You can specify a Language Locale Code to customize web pages on supported websites. With SeleniumBase, you can change the web browser's Locale on the command-line by doing this: @@ -14,7 +14,7 @@ From the ``SB()`` and ``Driver()`` formats, you can also set the ``locale_code`` locale_code="CODE" # Example: SB(locale_code="en") ``` -

List of Language Locale Codes:

+

List of Language Locale Codes:

diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 1e397998b9e..760974b421f 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) SeleniumBase Methods (API Reference) +

SeleniumBase Methods (API Reference)

(Common API Methods on YouTube)

diff --git a/help_docs/mobile_testing.md b/help_docs/mobile_testing.md index 72c9e258a44..413282309e6 100644 --- a/help_docs/mobile_testing.md +++ b/help_docs/mobile_testing.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Mobile Mode / Mobile Testing +

Mobile Mode / Mobile Testing

Use ``--mobile`` to run SeleniumBase tests using Chrome's mobile device emulator with default values for Device Metrics and User-Agent. @@ -42,7 +42,7 @@ To find real User-Agent strings, see: pytest test_swag_labs.py --mobile ``` -[SeleniumBase Mobile Testing](https://seleniumbase.github.io/cdn/gif/swag_mobile.gif) +SeleniumBase Mobile TestingHere's an example of configuring mobile settings for that test: diff --git a/help_docs/recorder_mode.md b/help_docs/recorder_mode.md index 3ef50de4e18..f8d549ea7ba 100644 --- a/help_docs/recorder_mode.md +++ b/help_docs/recorder_mode.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Recorder Mode πŸ”΄/⏺️ +

Recorder Mode πŸ”΄/⏺️

(Watch the tutorial on YouTube)

diff --git a/help_docs/translations.md b/help_docs/translations.md index 0206f3f9499..8070a34bff5 100644 --- a/help_docs/translations.md +++ b/help_docs/translations.md @@ -2,7 +2,7 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) 🌏 Translated Tests 🈺 +

🌏 Translated Tests 🈺

SeleniumBase supports the following 10 languages: English, Chinese, Dutch, French, Italian, Japanese, Korean, Portuguese, Russian, and Spanish. (Examples can be found in SeleniumBase/examples/translations) diff --git a/help_docs/uc_mode.md b/help_docs/uc_mode.md index 1b3c6503aad..c5ef46d7d18 100644 --- a/help_docs/uc_mode.md +++ b/help_docs/uc_mode.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) UC Mode πŸ‘€ +

UC Mode πŸ‘€

πŸ‘€ SeleniumBaseUC Mode (Undetected-Chromedriver Mode) allows bots to appear human, which lets them evade detection from anti-bot services that try to block them or trigger CAPTCHAs on various websites. diff --git a/help_docs/verify_webdriver.md b/help_docs/verify_webdriver.md index 2c3203d8c8b..0cef3db055b 100644 --- a/help_docs/verify_webdriver.md +++ b/help_docs/verify_webdriver.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Verifying that web drivers are installed +

Verifying that web drivers are installed

On newer versions of SeleniumBase, the driver is automatically downloaded to the ``seleniumbase/drivers`` folder as needed, and does not need to be on the System Path when running tests. diff --git a/help_docs/webdriver_installation.md b/help_docs/webdriver_installation.md index 2ce7e703dd0..c7aebbf72be 100644 --- a/help_docs/webdriver_installation.md +++ b/help_docs/webdriver_installation.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Installing webdrivers +

Installing webdrivers

To run web automation, you need webdrivers for each browser you plan on using. With SeleniumBase, drivers are downloaded automatically (as needed) into the SeleniumBase `drivers/` folder. @@ -102,4 +102,4 @@ To use the binaries from there in SeleniumBase scripts, set the `binary_location -------- -[](https://github.com/seleniumbase/SeleniumBase) + diff --git a/integrations/github/workflows/extras.md b/integrations/github/workflows/extras.md index a5d67509d15..569bb7a7f84 100644 --- a/integrations/github/workflows/extras.md +++ b/integrations/github/workflows/extras.md @@ -12,7 +12,7 @@ ### Slack Notifications - [rtCamp/action-slack-notify](https://github.com/rtCamp/action-slack-notify) can be used to send notifications to Slack. -**Usage:** +Usage: * Create a slack integration webhook if you don't have one already. * Create a ``SLACK_WEBHOOK`` secret on your repository with the webhook token value. * For this particular action, ``SLACK_CHANNEL`` is an optional environment variable that defaults to the webhook token channel if not specified. diff --git a/seleniumbase/console_scripts/ReadMe.md b/seleniumbase/console_scripts/ReadMe.md index 48856616697..1c75bef04ca 100644 --- a/seleniumbase/console_scripts/ReadMe.md +++ b/seleniumbase/console_scripts/ReadMe.md @@ -1,6 +1,6 @@ -## [](https://github.com/seleniumbase/SeleniumBase/) Console Scripts 🌠 +

Console Scripts 🌠

🌟 SeleniumBase console scripts can do many things, such as downloading web drivers, creating test directories with config files, activating the SeleniumBase Recorder, launching the SeleniumBase Commander, translating tests into other languages, running a Selenium Grid, and more. @@ -769,4 +769,4 @@ You can start, restart, or stop the Grid node. -------- -[](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md) + diff --git a/seleniumbase/utilities/selenium_grid/ReadMe.md b/seleniumbase/utilities/selenium_grid/ReadMe.md index 9bff1fb02b9..cf8d314aff3 100644 --- a/seleniumbase/utilities/selenium_grid/ReadMe.md +++ b/seleniumbase/utilities/selenium_grid/ReadMe.md @@ -1,6 +1,6 @@ -[](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md) +

The Selenium Grid Hub:

From 3b9f8fc4f3d0099a780c50a5f8dd2251c3cc7300 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 29 Nov 2025 15:32:10 -0500 Subject: [PATCH 3/4] Update CDP Mode examples --- examples/cdp_mode/raw_basic_mobile.py | 13 +++++++++++++ examples/cdp_mode/raw_cdp_mobile.py | 2 +- examples/cdp_mode/raw_mobile_async.py | 2 +- examples/cdp_mode/raw_mobile_gitlab.py | 2 +- examples/cdp_mode/raw_mobile_roblox.py | 2 +- examples/cdp_mode/raw_stopandshop.py | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 examples/cdp_mode/raw_basic_mobile.py diff --git a/examples/cdp_mode/raw_basic_mobile.py b/examples/cdp_mode/raw_basic_mobile.py new file mode 100644 index 00000000000..476732ee6f0 --- /dev/null +++ b/examples/cdp_mode/raw_basic_mobile.py @@ -0,0 +1,13 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, mobile=True) as sb: + url = "https://gitlab.com/users/sign_in" + sb.activate_cdp_mode(url) + sb.sleep(2) + sb.solve_captcha() + # (The rest is for testing and demo purposes) + sb.assert_text("Username", '[for="user_login"]', timeout=3) + sb.assert_element('label[for="user_login"]') + sb.highlight('button:contains("Sign in")') + sb.highlight('h1:contains("GitLab")') + sb.post_message("SeleniumBase wasn't detected", duration=4) diff --git a/examples/cdp_mode/raw_cdp_mobile.py b/examples/cdp_mode/raw_cdp_mobile.py index d872078fc6b..6e669987674 100644 --- a/examples/cdp_mode/raw_cdp_mobile.py +++ b/examples/cdp_mode/raw_cdp_mobile.py @@ -7,7 +7,7 @@ loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( - width=411, height=731, device_scale_factor=3, mobile=True + width=412, height=732, device_scale_factor=3, mobile=True ) ) ) diff --git a/examples/cdp_mode/raw_mobile_async.py b/examples/cdp_mode/raw_mobile_async.py index 68ba3121910..66e5e204405 100644 --- a/examples/cdp_mode/raw_mobile_async.py +++ b/examples/cdp_mode/raw_mobile_async.py @@ -10,7 +10,7 @@ async def main(): driver = await cdp_driver.start_async() await driver.main_tab.send( mycdp.emulation.set_device_metrics_override( - width=411, height=731, device_scale_factor=3, mobile=True + width=412, height=732, device_scale_factor=3, mobile=True ) ) page = await driver.get(url, lang="en") diff --git a/examples/cdp_mode/raw_mobile_gitlab.py b/examples/cdp_mode/raw_mobile_gitlab.py index f3d856be48e..cccf9917ed0 100644 --- a/examples/cdp_mode/raw_mobile_gitlab.py +++ b/examples/cdp_mode/raw_mobile_gitlab.py @@ -9,7 +9,7 @@ loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( - width=411, height=731, device_scale_factor=3, mobile=True + width=412, height=732, device_scale_factor=3, mobile=True ) ) ) diff --git a/examples/cdp_mode/raw_mobile_roblox.py b/examples/cdp_mode/raw_mobile_roblox.py index a67431b4d97..02c2d8aef6b 100644 --- a/examples/cdp_mode/raw_mobile_roblox.py +++ b/examples/cdp_mode/raw_mobile_roblox.py @@ -13,7 +13,7 @@ loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( - width=411, height=731, device_scale_factor=3, mobile=True + width=412, height=732, device_scale_factor=3, mobile=True ) ) ) diff --git a/examples/cdp_mode/raw_stopandshop.py b/examples/cdp_mode/raw_stopandshop.py index f2ccf98c961..4e489129f27 100644 --- a/examples/cdp_mode/raw_stopandshop.py +++ b/examples/cdp_mode/raw_stopandshop.py @@ -1,7 +1,7 @@ """Test Stop & Shop search. Non-US IPs might be blocked.""" from seleniumbase import SB -with SB(uc=True, test=True, guest=True, ad_block=True) as sb: +with SB(uc=True, test=True, ad_block=True) as sb: url = "https://stopandshop.com/" sb.activate_cdp_mode(url) sb.sleep(2.6) From edf83adf0fab6872a19d354318c9a2fd38298af7 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 29 Nov 2025 15:32:26 -0500 Subject: [PATCH 4/4] Version 4.44.20 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 26308a72fba..d09453272e8 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.44.19" +__version__ = "4.44.20"
LanguageCode