From 740990ea23d4e8fe9a79aa5d53199b332f3ac9f8 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:04:40 -0400 Subject: [PATCH 01/10] Suppress exceptions caused by cdp.dom.enable() --- seleniumbase/undetected/cdp_driver/element.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seleniumbase/undetected/cdp_driver/element.py b/seleniumbase/undetected/cdp_driver/element.py index 14c21c2bb08..984bc600bd0 100644 --- a/seleniumbase/undetected/cdp_driver/element.py +++ b/seleniumbase/undetected/cdp_driver/element.py @@ -1268,7 +1268,8 @@ async def highlight_overlay_async(self): await self.tab.send(cdp.dom.disable()) await self.tab.send(cdp.overlay.disable()) return - await self.tab.send(cdp.dom.enable()) + with suppress(Exception): + await self.tab.send(cdp.dom.enable()) await self.tab.send(cdp.overlay.enable()) conf = cdp.overlay.HighlightConfig( show_info=True, show_extension_lines=True, show_styles=True From f39fa56c8e038054890945382134bb24b0152126 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:09:45 -0400 Subject: [PATCH 02/10] Use `verify=True` by default when calling `requests.head()` --- seleniumbase/fixtures/base_case.py | 2 +- seleniumbase/fixtures/page_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index c80823e4046..aeff3c134e6 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -7632,7 +7632,7 @@ def get_link_status_code( link, allow_redirects=False, timeout=5, - verify=False, + verify=True, ): """Get the status code of a link. If the timeout is set to less than 1, it becomes 1. diff --git a/seleniumbase/fixtures/page_utils.py b/seleniumbase/fixtures/page_utils.py index 55fc005c8af..9506eda4d66 100644 --- a/seleniumbase/fixtures/page_utils.py +++ b/seleniumbase/fixtures/page_utils.py @@ -260,7 +260,7 @@ def _get_link_status_code( link, allow_redirects=False, timeout=5, - verify=False, + verify=True, ): """Get the status code of a link. If the timeout is exceeded, will return a 404. From 7fb67bf450a074fde72d0332f5502bad45e8414f Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:14:12 -0400 Subject: [PATCH 03/10] Simplify "headless" mode in the docs --- README.md | 17 ++++++++--------- SKILLS.md | 2 +- help_docs/customizing_test_runs.md | 29 ++++++++++++----------------- help_docs/method_summary.md | 2 -- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 11443b40d4a..46968424c04 100755 --- a/README.md +++ b/README.md @@ -797,10 +797,15 @@ pytest test_coffee_cart.py --trace --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa # (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) ---headless # (The default headless mode. Linux uses this mode by default.) ---headless1 # (Use Chrome's old headless mode. Fast, but has limitations.) ---headless2 # (Use Chrome's new headless mode, which supports extensions.) +--headless # (Use headless mode. Linux uses this mode by default.) +--chs # (Use Chrome-Headless-Shell, a limited Chrome without stealth.) --headed # (Run tests in headed/GUI mode on Linux OS, where not default.) +--incognito # (Enable Chrome's Incognito mode.) +--guest # (Enable Chrome's Guest mode.) +--uc # (Use undetected-chromedriver to evade bot-detection.) +--uc-cdp-events # (Capture CDP events in UC Mode. NOT related to CDP Mode.) +--log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) +--remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --xvfb # (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.) @@ -834,17 +839,11 @@ pytest test_coffee_cart.py --trace --disable-ws # (Disable Web Security on Chromium-based browsers.) --enable-ws # (Enable Web Security on Chromium-based browsers.) --enable-sync # (Enable "Chrome Sync" on websites.) ---uc | --undetected # (Use undetected-chromedriver to evade bot-detection.) ---uc-cdp-events # (Capture CDP events when running in "--undetected" mode.) ---log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) ---remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --ftrace | --final-trace # (Debug Mode after each test. Don't use with CI!) --dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) --dash-title=STRING # (Set the title shown for the generated dashboard.) --enable-3d-apis # (Enables WebGL and 3D APIs.) --swiftshader # (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") ---incognito # (Enable Chrome's Incognito mode.) ---guest # (Enable Chrome's Guest mode.) --dark # (Enable Chrome's Dark mode.) --devtools # (Open Chrome's DevTools when the browser opens.) --rs | --reuse-session # (Reuse browser session for all tests.) diff --git a/SKILLS.md b/SKILLS.md index 0ac539f85dc..923482bc301 100644 --- a/SKILLS.md +++ b/SKILLS.md @@ -192,7 +192,7 @@ pytest tests/ -n=4 --headless # parallel across 4 workers pytest tests/ --xvfb # Linux virtual display when a headed browser is needed ``` -- Linux runs headless by default; use `--headed` to force a GUI. `--headless2` supports extensions. +- Linux runs headless by default; use `--headed` to force a GUI. - `--rs` reuses one browser session for all tests (faster, but state leaks between tests; use `--crumbs` to clear cookies between them). - Set `--driver-version=VER` to pin the driver. - No `--pdb`, `--trace`, or `--show-report` in CI. diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md index 575d6c4f254..2ceb52730ec 100644 --- a/help_docs/customizing_test_runs.md +++ b/help_docs/customizing_test_runs.md @@ -171,10 +171,15 @@ pytest my_first_test.py --settings-file=custom_settings.py --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa # (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) ---headless # (The default headless mode. Linux uses this mode by default.) ---headless1 # (Use Chrome's old headless mode. Fast, but has limitations.) ---headless2 # (Use Chrome's new headless mode, which supports extensions.) +--headless # (Use headless mode. Linux uses this mode by default.) +--chs # (Use Chrome-Headless-Shell, a limited Chrome without stealth.) --headed # (Run tests in headed/GUI mode on Linux OS, where not default.) +--incognito # (Enable Chrome's Incognito mode.) +--guest # (Enable Chrome's Guest mode.) +--uc # (Use undetected-chromedriver to evade bot-detection.) +--uc-cdp-events # (Capture CDP events in UC Mode. NOT related to CDP Mode.) +--log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) +--remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --xvfb # (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.) @@ -208,17 +213,11 @@ pytest my_first_test.py --settings-file=custom_settings.py --disable-ws # (Disable Web Security on Chromium-based browsers.) --enable-ws # (Enable Web Security on Chromium-based browsers.) --enable-sync # (Enable "Chrome Sync" on websites.) ---uc | --undetected # (Use undetected-chromedriver to evade bot-detection.) ---uc-cdp-events # (Capture CDP events when running in "--undetected" mode.) ---log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) ---remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --ftrace | --final-trace # (Debug Mode after each test. Don't use with CI!) --dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) --dash-title=STRING # (Set the title shown for the generated dashboard.) --enable-3d-apis # (Enables WebGL and 3D APIs.) --swiftshader # (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") ---incognito # (Enable Chrome's Incognito mode.) ---guest # (Enable Chrome's Guest mode.) --dark # (Enable Chrome's Dark mode.) --devtools # (Open Chrome's DevTools when the browser opens.) --rs | --reuse-session # (Reuse browser session for all tests.) @@ -461,7 +460,7 @@ You can specify a Language Locale Code to customize web pages on supported websi pytest --locale=CODE # Example: --locale=ru ``` -Visit 🗾 Locales for a full list of codes. +Visit **[🗾 Locales](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/locale_codes.md))** for a full list of codes. -------- @@ -650,9 +649,7 @@ test=None # Test Mode: Output, Logging, Continue on failure unless "rtf". rtf=None # Shortcut / Duplicate of "raise_test_failure". raise_test_failure=None # If "test" mode, raise Exception on 1st failure. browser=None # Choose from "chrome", "edge", "firefox", or "safari". -headless=None # Use the default headless mode for Chromium and Firefox. -headless1=None # Use Chromium's old headless mode. (Fast, but limited) -headless2=None # Use Chromium's new headless mode. (Has more features) +headless=None # Use headless mode for Chromium/Firefox. (headless=True) locale_code=None # Set the Language Locale Code for the web browser. protocol=None # The Selenium Grid protocol: "http" or "https". servername=None # The Selenium Grid server/IP used for tests. @@ -761,9 +758,7 @@ Example: [SeleniumBase/examples/raw_robot.py](https://github.com/seleniumbase/Se ```python browser=None # Choose from "chrome", "edge", "firefox", or "safari". -headless=None # Use the default headless mode for Chromium and Firefox. -headless1=None # Use Chromium's old headless mode. (Fast, but limited) -headless2=None # Use Chromium's new headless mode. (Has more features) +headless=None # Use headless mode for Chromium/Firefox. (headless=True) headed=None # Run tests in headed/GUI mode on Linux, where not default. locale_code=None # Set the Language Locale Code for the web browser. protocol=None # The Selenium Grid protocol: "http" or "https". @@ -846,7 +841,7 @@ Example: [SeleniumBase/examples/raw_driver_manager.py](https://github.com/seleni ```python url: Optional[str] = None user_data_dir: Optional[PathLike] = None -headless: Optional[bool] = False +headless: Optional[bool] = False # Set `headless=True` to activate incognito: Optional[bool] = False guest: Optional[bool] = False browser_executable_path: Optional[PathLike] = None diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 0df9584098c..135b91d7b66 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -215,8 +215,6 @@ self.get_new_driver( log_cdp_events=None, no_sandbox=None, disable_gpu=None, - headless1=None, - headless2=None, incognito=None, guest_mode=None, dark_mode=None, From 164c36da8bb90673563a7f0aaa3fe320e63fcaae Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:33:41 -0400 Subject: [PATCH 04/10] Update examples --- examples/cdp_mode/raw_planetmc.py | 4 ++-- .../{example_test.py => example_script.py} | 0 examples/raw_cdp_mode.py | 7 ++++--- examples/test_apple_site.py | 2 -- examples/test_download_files.py | 16 ++++++++-------- examples/test_download_images.py | 7 +++++-- 6 files changed, 19 insertions(+), 17 deletions(-) rename examples/migration/protractor/{example_test.py => example_script.py} (100%) diff --git a/examples/cdp_mode/raw_planetmc.py b/examples/cdp_mode/raw_planetmc.py index faf3401ead5..fd69152e276 100644 --- a/examples/cdp_mode/raw_planetmc.py +++ b/examples/cdp_mode/raw_planetmc.py @@ -7,8 +7,8 @@ sb.assert_text("Sign in to Planet Minecraft") sb.type('input[name="email"]', "test@example.com") sb.type('input[name="password"]', "Fake_Password") - sb.click("input#autologin") # The checkbox + sb.click("input#autologin") # Unchecks checkbox if sb.is_element_visible("input[disabled]"): - sb.solve_captcha() # Enables the input button + sb.solve_captcha() # Enables input button sb.assert_element_absent("input[disabled]") sb.sleep(1.5) diff --git a/examples/migration/protractor/example_test.py b/examples/migration/protractor/example_script.py similarity index 100% rename from examples/migration/protractor/example_test.py rename to examples/migration/protractor/example_script.py diff --git a/examples/raw_cdp_mode.py b/examples/raw_cdp_mode.py index 1354c3ecb43..9cc792e9514 100644 --- a/examples/raw_cdp_mode.py +++ b/examples/raw_cdp_mode.py @@ -1,7 +1,7 @@ """Bypass bot-detection with SeleniumBase CDP Mode""" from seleniumbase import sb_cdp -sb = sb_cdp.Chrome() +sb = sb_cdp.Chrome() # Launch a stealth browser sb.goto("https://browserscan.net/bot-detection") sb.assert_element('strong:contains("Normal")') sb.sleep(1) @@ -14,8 +14,9 @@ sb.goto("https://www.planetminecraft.com/account") sb.type('[name="email"]', "Name123") sb.type('[name="password"]', "Pass") -sb.solve_captcha() +sb.click("input#autologin") +sb.solve_captcha() # Works on different CAPTCHAs sb.assert_element('input[name="login"]') sb.assert_element_not_visible("input[disabled]") sb.sleep(1) -sb.quit() +sb.quit() # Quit browser before end-of-program diff --git a/examples/test_apple_site.py b/examples/test_apple_site.py index 86fcd69d998..44153d19da5 100644 --- a/examples/test_apple_site.py +++ b/examples/test_apple_site.py @@ -15,8 +15,6 @@ def test_apple_developer_site_webdriver_instructions(self): self.goto_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") - elif self.is_chromium(): - self.get_new_driver(browser=self.browser, headless2=True) self.goto("https://developer.apple.com/search/") title = "Testing with WebDriver in Safari" self.type("input#searchField", title + "\n") diff --git a/examples/test_download_files.py b/examples/test_download_files.py index 0820588d20b..11fc0721a5b 100644 --- a/examples/test_download_files.py +++ b/examples/test_download_files.py @@ -27,11 +27,11 @@ def test_download_chromedriver_notes(self): def test_download_files_from_pypi(self): if self._multithreaded or not self.undetectable: self.goto_if_not_url("about:blank") - self.skip("Skipping test in multi-threaded mode.") + self.skip("Skip: Requires stealth and not multi-threaded.") self.goto("https://pypi.org/project/sbvirtualdisplay/#files") self.assert_element("span#pip-command") - self.assert_text("Download files", "div#files h2.page-title") - self.assert_text("Download files", "a#files-tab") + self.assert_text("Release files", "div#files h2.page-title") + self.assert_text("Release files", "a#files-tab") pkg_header = self.get_text('h1[class*="header__name"]').strip() pkg_name = pkg_header.replace(" ", "-") whl_file = pkg_name + "-py3-none-any.whl" @@ -48,7 +48,7 @@ def test_download_files_from_pypi(self): or self.browser == "ie" or self.browser == "edge" or (self.is_chromium() and self.guest_mode) - or (self.is_chromium() and (self.headless or self.headless2)) + or (self.is_chromium() and self.headless) ): whl_href = self.get_attribute(whl_selector, "href") tar_href = self.get_attribute(tar_selector, "href") @@ -85,12 +85,12 @@ def test_download_files_from_pypi(self): # Get file sizes in kB to compare actual values with displayed values whl_file_kb = whl_file_bytes / 1000.0 whl_line_fi = self.get_text('a[href$=".whl"]').strip() - whl_line = self.get_text('div.file:contains("%s")' % whl_line_fi) - whl_display_kb = float(whl_line.split("(")[1].split(" ")[0]) + whl_line = self.get_text('td:contains("%s")' % whl_line_fi) + whl_display_kb = float(whl_line.split(" ")[1].split(" ")[0]) tar_gz_file_kb = tar_gz_file_bytes / 1000.0 tar_gz_line_fi = self.get_text('a[href$=".tar.gz"]').strip() - tar_gz_line = self.get_text('div.file:contains("%s")' % tar_gz_line_fi) - tar_gz_display_kb = float(tar_gz_line.split("(")[1].split(" ")[0]) + tar_gz_line = self.get_text('tr:contains("%s")' % tar_gz_line_fi) + tar_gz_display_kb = float(tar_gz_line.split(" ")[1].split(" ")[0]) # Verify downloaded files are the correct size (account for rounding) self.assert_true( diff --git a/examples/test_download_images.py b/examples/test_download_images.py index 3f76999b824..dd10169971a 100644 --- a/examples/test_download_images.py +++ b/examples/test_download_images.py @@ -42,8 +42,6 @@ def test_download_images_via_screenshot(self): count = 0 for src in unique_src_values: self.goto(src) - if not self.headless and not self.headless2: - self.sleep(0.3) image = self.find_element("img") if src.startswith("data:") or ";base64" in src: # Special Cases: SVGs, etc. Convert to PNG. @@ -53,6 +51,11 @@ def test_download_images_via_screenshot(self): filename = src.split("/")[-1] folder = "downloaded_files" file_path = os.path.join(folder, filename) + # Too much screen flicker if not slowed + if not self.headless: + self.sleep(0.15) image.screenshot(file_path) + if not self.headless: + self.sleep(0.15) self.assert_downloaded_file(filename) print(file_path) From 9786d7bf8b5039769b61b9fd0bbc945f9a160241 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:34:06 -0400 Subject: [PATCH 05/10] Update the mkdocs prepare script --- mkdocs_build/prepare.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkdocs_build/prepare.py b/mkdocs_build/prepare.py index 103f7f3004e..8c560573180 100644 --- a/mkdocs_build/prepare.py +++ b/mkdocs_build/prepare.py @@ -168,6 +168,9 @@ def main(*args, **kwargs): "/help_docs/uc_mode/", "/../help_docs/uc_mode/" ) changed = True + if ".md#-" in line: + line = line.replace(".md#-", ".md#") + changed = True if "" in line: changed = True continue From 835cb808c0094e098b817e2dc1141282a672df40 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:36:31 -0400 Subject: [PATCH 06/10] Refresh Python dependencies --- mkdocs_build/requirements.txt | 7 ++++--- requirements.txt | 6 ++++-- setup.py | 10 ++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index e74fc59d6ed..7aa11c1fd9f 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -2,14 +2,15 @@ # For docs generation, you'll need Python 3.11 or newer regex>=2026.9.29 -pipdeptree>=4.2.9 -platformdirs>=4.13.5 -nab>=0.0.22 +platformdirs>=4.14.0 +nab>=0.0.23 backrefs>=8.1 pymdown-extensions>=12.3 python-dateutil>=3.0.0 click>=8.5.0 +tzdata>=2026.5 Markdown>=3.11.2 +pipdeptree==4.2.9 ghp-import==2.1.0 watchdog==6.0.0 cairocffi==1.7.1 diff --git a/requirements.txt b/requirements.txt index 242daa35c44..127d202e275 100755 --- a/requirements.txt +++ b/requirements.txt @@ -71,7 +71,9 @@ rich>=15.0.0,<16 coverage>=7.16.2;python_version=="3.10" coverage>=7.17.0;python_version>="3.11" pytest-cov>=7.1.0 -flake8==7.4.1 +flake8==7.4.1;python_version=="3.10" +flake8==7.5.0;python_version>="3.11" mccabe==0.7.0 -pyflakes==4.0.3 +pyflakes==4.0.3;python_version=="3.10" +pyflakes==4.1.0;python_version>="3.11" pycodestyle==2.15.0 diff --git a/setup.py b/setup.py index 6cb18c22f2e..9f544da4c7f 100755 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ print("You are currently using Python %s\n" % current_ver) sys.exit() print("\n*** Checking code health with flake8:\n") - os.system("python -m pip install 'flake8==7.4.1'") + os.system("python -m pip install 'flake8==7.5.0'") flake8_status = os.system( "flake8 --exclude=recordings,temp,venv,.venv" ) @@ -250,9 +250,11 @@ # pip install -e .[flake8] # Usage: flake8 "flake8": [ - 'flake8==7.4.1', + 'flake8==7.4.1;python_version=="3.10"', + 'flake8==7.5.0;python_version>="3.11"', 'mccabe==0.7.0', - 'pyflakes==4.0.3', + 'pyflakes==4.0.3;python_version=="3.10"', + 'pyflakes==4.1.0;python_version>="3.11"', 'pycodestyle==2.15.0', ], # pip install -e .[mcp] @@ -310,7 +312,7 @@ # pip install -e .[uv] # (For the MCP integration and more.) "uv": [ - "uv>=0.13.5" + "uv>=0.13.6" ], }, packages=[ From 821d87f4372cb9059bc80349c77b976eb1fd4c4e Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:37:39 -0400 Subject: [PATCH 07/10] Update the method_summary --- help_docs/method_summary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 135b91d7b66..00956232ad4 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -325,7 +325,7 @@ self.get_domain_url(url) self.get_active_element_css() self.get_beautiful_soup(source=None) self.get_unique_links() -self.get_link_status_code(link, allow_redirects=False, timeout=5, verify=False) +self.get_link_status_code(link, allow_redirects=False, timeout=5, verify=True) self.assert_link_status_code_is_not_404(link) self.assert_no_404_errors(multithreaded=True, timeout=None) # Duplicates: From 18a808c9f9c9efcaa66904e118ef0f25c5151f74 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:38:38 -0400 Subject: [PATCH 08/10] Update the docs --- README.md | 59 +++++++++++++------------------------ help_docs/syntax_formats.md | 4 +-- help_docs/uc_mode.md | 13 ++++---- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 46968424c04..ee1b20192b4 100755 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

Includes stealth options for bypassing bot-detection and CAPTCHAs.

-PyPI version SeleniumBase GitHub Actions SeleniumBase Docs SeleniumBase PyPI downloads
+PyPI version SeleniumBase GitHub Actions SeleniumBase Docs SeleniumBase PyPI downloads
YouTube Channel SeleniumBase Facebook Followers SeleniumBase X Followers

@@ -66,20 +66,30 @@ -------- -📝 Here's an example with seleniumbase sb_cdp (Pure CDP Mode):
(It bypasses both BrowserScan AND Sannysoft Bot-Detection) +📝 [This example](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_cdp_mode.py) uses seleniumbase sb_cdp (*[Pure CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md#Pure_CDP_Mode)*):
(It bypasses BrowserScan, Sannysoft, and Cloudflare bot-detection) ```python from seleniumbase import sb_cdp -sb = sb_cdp.Chrome() +sb = sb_cdp.Chrome() # Launch a stealth browser sb.goto("https://browserscan.net/bot-detection") sb.assert_element('strong:contains("Normal")') sb.sleep(1) + sb.goto("https://bot.sannysoft.com/") sb.assert_element(".passed") sb.assert_element_not_visible(".failed") sb.sleep(1) -sb.quit() + +sb.goto("https://www.planetminecraft.com/account") +sb.type('[name="email"]', "Name123") +sb.type('[name="password"]', "Pass") +sb.click("input#autologin") +sb.solve_captcha() # Works on different CAPTCHAs +sb.assert_element('input[name="login"]') +sb.assert_element_not_visible("input[disabled]") +sb.sleep(1) +sb.quit() # Quit browser before end-of-program ```

@@ -92,37 +102,16 @@ sb.quit()
(All Sannysoft bot-detection tests passed successfully)

--------- - -📝 Here's another example with seleniumbase sb_cdp (Pure CDP Mode):
(It performs various browser actions and bypasses a Cloudflare Turnstile) - -```python -from seleniumbase import sb_cdp - -sb = sb_cdp.Chrome(incognito=True) -sb.goto("https://www.planetminecraft.com/account") -sb.assert_element('input[name="login"]') -sb.type('input[name="email"]', "test@example.com") -sb.type('input[name="password"]', "Fake_Password") -sb.click("input#autologin") # The checkbox -if sb.is_element_visible("input[disabled]"): - sb.solve_captcha() # Enables the input -sb.assert_element_absent("input[disabled]") -sb.sleep(1.5) -sb.quit() -``` - -``` -python raw_cdp_planetmc.py -``` - -

+

Cloudflare Turnstile bypassed successfully +
(Cloudflare Turnstile bypassed successfully on Planet Minecraft)

+-------- + ✅ sb.solve_captcha() handles CAPTCHAs that aren't bypassed automatically.
(If called when there's no CAPTCHA on the page, then nothing happens / no-op.) -â„šī¸ sb.assert_* methods wait up to 7 seconds for the expected condition to occur. +â„šī¸ sb.assert_* methods wait up to 7 seconds for the expected condition.
(Raises an exception if the condition isn't met in time.) -------- @@ -635,7 +624,7 @@ self.assert_no_js_errors() # Verify there are no JS errors. self.type("input", "dogs\n") # (The "\n" presses ENTER) ``` -Most SeleniumBase scripts can be run with pytest, pynose, or pure python. Not all test runners can run all test formats. For example, tests that use the `sb` pytest fixture can only be run with `pytest`. (See Syntax Formats) There's also a Gherkin test format that runs with behave. +Most SeleniumBase scripts can be run with pytest, pynose, or pure python. Not all test runners can run all test formats. For example, tests that use the `sb` pytest fixture can only be run with `pytest`. (See Syntax Formats) There's also a Gherkin test format that runs with behave. ```zsh pytest coffee_cart_tests.py --rs @@ -1412,21 +1401,13 @@ self.activate_jquery() ```python self.execute_script("jQuery, window.scrollTo(0, 600)") # Scrolling the page - self.execute_script("jQuery('#annoying-widget').hide()") # Hiding elements on a page - self.execute_script("jQuery('#hidden-widget').show(0)") # Showing hidden elements on a page - self.execute_script("jQuery('#annoying-button a').remove()") # Removing elements on a page - self.execute_script("jQuery('%s').mouseover()" % (mouse_over_item)) # Mouse-over elements on a page - self.execute_script("jQuery('input#the_id').val('my_text')") # Fast text input on a page - self.execute_script("jQuery('div#dropdown a.link').click()") # Click elements on a page - self.execute_script("return jQuery('div#amazing')[0].text") # Returns the css "text" of the element given - self.execute_script("return jQuery('textarea')[2].value") # Returns the css "value" of the 3rd textarea element on the page ``` diff --git a/help_docs/syntax_formats.md b/help_docs/syntax_formats.md index 2289ed6de28..98c2303ea4e 100644 --- a/help_docs/syntax_formats.md +++ b/help_docs/syntax_formats.md @@ -645,7 +645,7 @@ class MiClaseDePrueba(CasoDePrueba):

20. Gherkin syntax with "behave" BDD runner

-With [Behave's BDD Gherkin format](https://behave.readthedocs.io/en/stable/gherkin.html), you can use natural language to write tests that work with SeleniumBase methods. Behave tests are run by calling `behave` on the command-line. This requires some special files in a specific directory structure. Here's an example of that structure: +With [Behave's BDD Gherkin format](https://behave.readthedocs.io/en/latest/gherkin/), you can use natural language to write tests that work with SeleniumBase methods. Behave tests are run by calling `behave` on the command-line. This requires some special files in a specific directory structure. Here's an example of that structure: ```zsh features/ @@ -742,7 +742,7 @@ def login_to_swag_labs(context, user): sb.type("#password", "secret_sauce\n") ``` -(For more information, see the SeleniumBase Behave BDD ReadMe.) +(For more information, see the [SeleniumBase Behave BDD ReadMe](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md).)

21. SeleniumBase SB (Python context manager)

diff --git a/help_docs/uc_mode.md b/help_docs/uc_mode.md index 0cafb9d2a7b..93f32d7d743 100644 --- a/help_docs/uc_mode.md +++ b/help_docs/uc_mode.md @@ -107,13 +107,12 @@ with SB(uc=True, test=True, incognito=True, locale="en") as sb: 👤 On Linux, use `sb.solve_captcha()` to handle CAPTCHAs: ```python -from seleniumbase import SB - -with SB(uc=True, test=True) as sb: - url = "https://www.virtualmanager.com/en/login" - sb.goto(url) +with SB(uc=True, test=True, incognito=True) as sb: + sb.goto("https://www.virtualmanager.com/en/login") + sb.sleep(1.5) print(sb.get_page_title()) sb.solve_captcha() # Only used if needed + sb.sleep(2) sb.assert_element('input[name*="email"]') print(sb.get_page_title()) sb.assert_element('input[name*="login"]') @@ -121,9 +120,9 @@ with SB(uc=True, test=True) as sb: sb.post_message("SeleniumBase wasn't detected!") ``` -uc_gui_click_captcha on Linux +solve_captcha() on Linux -The 2nd print() should output Virtual Manager, which means that the automation successfully passed the Turnstile. +The 2nd print() outputs the expected page title, which means that the automation successfully clicked & bypassed the Turnstile. (Note: UC Mode has a special virtual display on Linux: Xvfb, which is enabled by default when not changing headed/headless settings.) From 36125f29a48e7cc863b856f477602ef107491f78 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:39:20 -0400 Subject: [PATCH 09/10] Update MCP server versioning --- mcp_servers/pyproject.toml | 2 +- server.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp_servers/pyproject.toml b/mcp_servers/pyproject.toml index 9dc48906062..a735e844b06 100644 --- a/mcp_servers/pyproject.toml +++ b/mcp_servers/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seleniumbase-mcp" -version = "1.5.4dev0" +version = "1.5.5dev0" description = "MCP server exposing SeleniumBase CDP Mode as tools for MCP clients." readme = "README.md" requires-python = ">=3.10" diff --git a/server.json b/server.json index 388a49f0d5e..8b16d721c78 100644 --- a/server.json +++ b/server.json @@ -3,7 +3,7 @@ "name": "io.github.seleniumbase/seleniumbase", "title": "SeleniumBase MCP", "description": "Stealthy browser automation, testing, and web-scraping via CDP Mode.", - "version": "4.56.4", + "version": "4.56.5", "repository": { "url": "https://github.com/seleniumbase/SeleniumBase", "source": "github" @@ -13,7 +13,7 @@ { "registryType": "pypi", "identifier": "seleniumbase", - "version": "4.56.4", + "version": "4.56.5", "transport": { "type": "stdio" }, From fa2b7989527371569cf9689e58bf443c0c9342f4 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 29 Oct 2026 00:39:34 -0400 Subject: [PATCH 10/10] Version 4.56.5 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 8a64abdb2f2..2fac590e366 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.56.4" +__version__ = "4.56.5"