From 5119ee05da7d9acf6d2127bb55ee8b2ead20c614 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 17:41:04 -0500 Subject: [PATCH 01/10] Update `uc_gui_click_captcha()` --- seleniumbase/core/browser_launcher.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 8c94ece6..161b722c 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -1217,6 +1217,13 @@ def _uc_gui_click_captcha( and driver.is_element_present("#challenge-form div > div") ): frame = "#challenge-form div > div" + elif ( + driver.is_element_present('[name*="cf-turnstile-"]') + and driver.is_element_present( + '[style="display: grid;"] div div' + ) + ): + frame = '[style="display: grid;"] div div' elif ( driver.is_element_present('[name*="cf-turnstile-"]') and driver.is_element_present("[class*=spacer] + div div") From fcdcf834677a89f9038dc68b4f473a6ea2651e68 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:00:14 -0500 Subject: [PATCH 02/10] Refactor Headless Mode --- seleniumbase/core/browser_launcher.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 161b722c..24174883 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -3845,6 +3845,12 @@ def get_local_driver( edge_options.add_argument("--guest") if dark_mode: edge_options.add_argument("--enable-features=WebContentsForceDark") + if headless1: + # developer.chrome.com/blog/removing-headless-old-from-chrome + with suppress(Exception): + if int(str(use_version).split(".")[0]) >= 132: + headless1 = False + headless2 = True if headless2: try: if use_version == "latest" or int(use_version) >= 109: @@ -4386,6 +4392,12 @@ def get_local_driver( use_version = find_chromedriver_version_to_use( use_version, driver_version ) + if headless1: + # developer.chrome.com/blog/removing-headless-old-from-chrome + with suppress(Exception): + if int(str(use_version).split(".")[0]) >= 132: + headless1 = False + headless2 = True if headless2: try: if ( From 08f8f3adff93c03b87fd600fe1f953b4f8836e5a Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:09:55 -0500 Subject: [PATCH 03/10] Refactor BaseCase --- seleniumbase/fixtures/base_case.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 08961aa1..d7fa6db9 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -95,6 +95,7 @@ logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("urllib3").setLevel(logging.ERROR) urllib3.disable_warnings() LOGGER.setLevel(logging.WARNING) +is_linux = shared_utils.is_linux() is_windows = shared_utils.is_windows() python3_11_or_newer = False if sys.version_info >= (3, 11): @@ -4828,7 +4829,7 @@ class BaseCase(unittest.TestCase): from seleniumbase.js_code.recorder_js import recorder_js if not self.is_chromium(): - if "linux" not in sys.platform: + if not is_linux: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL @@ -5658,7 +5659,7 @@ class BaseCase(unittest.TestCase): c1 = "" c2 = "" cr = "" - if "linux" not in sys.platform: + if not is_linux: c1 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL @@ -5760,7 +5761,7 @@ class BaseCase(unittest.TestCase): c1 = "" c2 = "" cr = "" - if "linux" not in sys.platform: + if not is_linux: c1 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL From b539039dedf3a31f774c2249f994a99f6426ae37 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:17:24 -0500 Subject: [PATCH 04/10] Fix --xvfb compatibility with --reuse-session / --rs --- seleniumbase/core/browser_launcher.py | 7 +++++++ seleniumbase/fixtures/base_case.py | 21 +++++++++++++++++-- seleniumbase/plugins/pytest_plugin.py | 30 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 24174883..2ac488f4 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -888,6 +888,13 @@ def __install_pyautogui_if_missing(): _xvfb_display.start() sb_config._virtual_display = _xvfb_display sb_config.headless_active = True + if ( + hasattr(sb_config, "reuse_session") + and sb_config.reuse_session + and hasattr(sb_config, "_vd_list") + and isinstance(sb_config._vd_list, list) + ): + sb_config._vd_list.append(_xvfb_display) def install_pyautogui_if_missing(driver): diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index d7fa6db9..7c9d6595 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -14010,6 +14010,9 @@ class BaseCase(unittest.TestCase): if not self.undetectable: sb_config._virtual_display = self._xvfb_display sb_config.headless_active = True + if self._reuse_session and hasattr(sb_config, "_vd_list"): + if isinstance(sb_config._vd_list, list): + sb_config._vd_list.append(self._xvfb_display) def __activate_virtual_display(self): if self.undetectable and not (self.headless or self.headless2): @@ -14034,6 +14037,9 @@ class BaseCase(unittest.TestCase): self.__activate_standard_virtual_display() else: self.headless_active = True + if self._reuse_session and hasattr(sb_config, "_vd_list"): + if isinstance(sb_config._vd_list, list): + sb_config._vd_list.append(self._xvfb_display) except Exception as e: if hasattr(e, "msg"): print("\n" + str(e.msg)) @@ -14088,7 +14094,7 @@ class BaseCase(unittest.TestCase): """This is only needed on Linux. The "--xvfb" arg is still useful, as it prevents headless mode, which is the default mode on Linux unless using another arg.""" - if "linux" in sys.platform and (not self.headed or self.xvfb): + if is_linux and (not self.headed or self.xvfb): pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) @@ -16605,7 +16611,11 @@ class BaseCase(unittest.TestCase): # (Pynose / Behave / Pure Python) Close all open browser windows self.__quit_all_drivers() # Resume tearDown() for all test runners, (Pytest / Pynose / Behave) - if hasattr(self, "_xvfb_display") and self._xvfb_display: + if ( + hasattr(self, "_xvfb_display") + and self._xvfb_display + and not self._reuse_session + ): # Stop the Xvfb virtual display launched from BaseCase try: if hasattr(self._xvfb_display, "stop"): @@ -16620,6 +16630,13 @@ class BaseCase(unittest.TestCase): hasattr(sb_config, "_virtual_display") and sb_config._virtual_display and hasattr(sb_config._virtual_display, "stop") + and ( + not hasattr(sb_config, "reuse_session") + or ( + hasattr(sb_config, "reuse_session") + and not sb_config.reuse_session + ) + ) ): # CDP Mode may launch a 2nd Xvfb virtual display try: diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index a729070c..04b14a82 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -1371,6 +1371,7 @@ def pytest_addoption(parser): arg_join = " ".join(sys_argv) sb_config._browser_shortcut = None + sb_config._vd_list = [] # SeleniumBase does not support pytest-timeout due to hanging browsers. for arg in sys_argv: @@ -2017,6 +2018,13 @@ def pytest_runtest_teardown(item): hasattr(self, "_xvfb_display") and self._xvfb_display and hasattr(self._xvfb_display, "stop") + and ( + not hasattr(sb_config, "reuse_session") + or ( + hasattr(sb_config, "reuse_session") + and not sb_config.reuse_session + ) + ) ): self.headless_active = False sb_config.headless_active = False @@ -2026,6 +2034,13 @@ def pytest_runtest_teardown(item): hasattr(sb_config, "_virtual_display") and sb_config._virtual_display and hasattr(sb_config._virtual_display, "stop") + and ( + not hasattr(sb_config, "reuse_session") + or ( + hasattr(sb_config, "reuse_session") + and not sb_config.reuse_session + ) + ) ): sb_config._virtual_display.stop() sb_config._virtual_display = None @@ -2139,6 +2154,21 @@ def _perform_pytest_unconfigure_(config): except Exception: pass sb_config.shared_driver = None + with suppress(Exception): + if ( + hasattr(sb_config, "_virtual_display") + and sb_config._virtual_display + and hasattr(sb_config._virtual_display, "stop") + ): + sb_config._virtual_display.stop() + sb_config._virtual_display = None + sb_config.headless_active = False + if hasattr(sb_config, "_vd_list") and sb_config._vd_list: + if isinstance(sb_config._vd_list, list): + for display in sb_config._vd_list: + if display: + with suppress(Exception): + display.stop() if hasattr(sb_config, "log_path") and sb_config.item_count > 0: log_helper.archive_logs_if_set( constants.Logs.LATEST + "/", sb_config.archive_logs From 6ea000a1df31b3a835c5fa9d05d2eef20313878c Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:19:45 -0500 Subject: [PATCH 05/10] Update "sbase gui" / "sbase commander" settings --- seleniumbase/console_scripts/sb_commander.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/seleniumbase/console_scripts/sb_commander.py b/seleniumbase/console_scripts/sb_commander.py index 9219efef..0ea30632 100644 --- a/seleniumbase/console_scripts/sb_commander.py +++ b/seleniumbase/console_scripts/sb_commander.py @@ -156,9 +156,9 @@ def do_pytest_run( if save_screenshots: full_run_command += " --screenshot" - dash_s_needed = False - if "-s" not in additional_options.split(" "): - dash_s_needed = True + capture_needed = False + if "--capture" not in additional_options: + capture_needed = True additional_options = additional_options.strip() if additional_options: @@ -168,8 +168,8 @@ def do_pytest_run( if verbose: full_run_command += " -v" - if dash_s_needed: - full_run_command += " -s" + if capture_needed: + full_run_command += " --capture=tee-sys" print(full_run_command) subprocess.Popen(full_run_command, shell=True) From 28eef66b375c16866990f2c49c278e0ca9d7c0c3 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:21:36 -0500 Subject: [PATCH 06/10] Refactor completed pytest-html reports --- seleniumbase/plugins/pytest_plugin.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index 04b14a82..e18a5736 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -2223,6 +2223,9 @@ def _perform_pytest_unconfigure_(config): the_html_r = the_html_r.replace( ph_link, "%s and %s" % (sb_link, ph_link) ) + the_html_r = the_html_r.replace( + "findAll('.collapsible", "//findAll('.collapsible" + ) the_html_r = the_html_r.replace( "mediaName.innerText", "//mediaName.innerText" ) @@ -2258,6 +2261,9 @@ def _perform_pytest_unconfigure_(config): html_style = html_style.replace( "- 80px);", "- 80px);\n margin-bottom: -42px;" ) + html_style = html_style.replace(".collapsible", ".oldc") + html_style = html_style.replace(" (hide details)", "") + html_style = html_style.replace(" (show details)", "") with open(assets_style, "w", encoding="utf-8") as f: f.write(html_style) with suppress(Exception): @@ -2357,6 +2363,9 @@ def _perform_pytest_unconfigure_(config): html_style = html_style.replace( "- 80px);", "- 80px);\n margin-bottom: -42px;" ) + html_style = html_style.replace(".collapsible", ".oldc") + html_style = html_style.replace(" (hide details)", "") + html_style = html_style.replace(" (show details)", "") with open(assets_style, "w", encoding="utf-8") as f: f.write(html_style) with suppress(Exception): @@ -2424,6 +2433,9 @@ def _perform_pytest_unconfigure_(config): the_html_r = the_html_r.replace( ph_link, "%s and %s" % (sb_link, ph_link) ) + the_html_r = the_html_r.replace( + "findAll('.collapsible", "//findAll('.collapsible" + ) the_html_r = the_html_r.replace( "mediaName.innerText", "//mediaName.innerText" ) From c4d2d1ab10f0a28da5d9d51bc5eb39c409396c6a Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:28:52 -0500 Subject: [PATCH 07/10] Refresh Python dependencies --- mkdocs_build/requirements.txt | 2 +- requirements.txt | 8 +++++--- setup.py | 8 +++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 4379c934..865cb756 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -18,7 +18,7 @@ lxml==5.3.0 pyquery==2.0.1 readtime==3.0.0 mkdocs==1.6.1 -mkdocs-material==9.5.49 +mkdocs-material==9.5.50 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-material-extensions==1.3.1 diff --git a/requirements.txt b/requirements.txt index b0b6de70..d3037254 100755 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,9 @@ attrs>=24.3.0 certifi>=2024.12.14 exceptiongroup>=1.2.2 websockets~=13.1;python_version<"3.9" -websockets>=14.1;python_version>="3.9" -filelock>=3.16.1 +websockets>=14.2;python_version>="3.9" +filelock~=3.16.1;python_version<"3.9" +filelock>=3.17.0;python_version>="3.9" fasteners>=0.19 mycdp>=1.1.0 pynose>=1.5.3 @@ -41,7 +42,8 @@ trio==0.28.0;python_version>="3.9" trio-websocket==0.11.1 wsproto==1.2.0 websocket-client==1.8.0 -selenium==4.27.1 +selenium==4.27.1;python_version<"3.9" +selenium==4.28.0;python_version>="3.9" cssselect==1.2.0 sortedcontainers==2.4.0 execnet==2.1.1 diff --git a/setup.py b/setup.py index c9308093..5b4929ca 100755 --- a/setup.py +++ b/setup.py @@ -156,8 +156,9 @@ setup( "certifi>=2024.12.14", "exceptiongroup>=1.2.2", 'websockets~=13.1;python_version<"3.9"', - 'websockets>=14.1;python_version>="3.9"', - 'filelock>=3.16.1', + 'websockets>=14.2;python_version>="3.9"', + 'filelock~=3.16.1;python_version<"3.9"', + 'filelock>=3.17.0;python_version>="3.9"', 'fasteners>=0.19', "mycdp>=1.1.0", "pynose>=1.5.3", @@ -190,7 +191,8 @@ setup( 'trio-websocket==0.11.1', 'wsproto==1.2.0', 'websocket-client==1.8.0', - 'selenium==4.27.1', + 'selenium==4.27.1;python_version<"3.9"', + 'selenium==4.28.0;python_version>="3.9"', 'cssselect==1.2.0', "sortedcontainers==2.4.0", 'execnet==2.1.1', From 70be9b0dc6eff680ed5196c0ae22165e03ca7d7d Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:30:05 -0500 Subject: [PATCH 08/10] Update examples --- examples/cdp_mode/ReadMe.md | 9 +++++++-- examples/cdp_mode/raw_glassdoor.py | 11 +++++++++++ examples/raw_invisible_captcha.py | 9 +++++++++ examples/raw_recaptcha.py | 9 --------- examples/uc_cdp_events.py | 2 +- examples/verify_undetected.py | 2 +- 6 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 examples/cdp_mode/raw_glassdoor.py create mode 100644 examples/raw_invisible_captcha.py diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 970d4539..88e3d6f2 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -6,16 +6,21 @@ -------- - +

(Watch the CDP Mode tutorial on YouTube! ▶️)

-------- - +

(Watch "Hacking websites with CDP" on YouTube! ▶️)

-------- + +

(Watch "Web-Scraping with GitHub Actions" on YouTube! ▶️)

+ +-------- + 👤 UC Mode avoids bot-detection by first disconnecting WebDriver from the browser at strategic times, calling special PyAutoGUI methods to bypass CAPTCHAs (as needed), and finally reconnecting the driver afterwards so that WebDriver actions can be performed again. Although this approach works for bypassing simple CAPTCHAs, more flexibility is needed for bypassing bot-detection on websites with advanced protection. (That's where CDP Mode comes in.) 🐙 CDP Mode is based on python-cdp, trio-cdp, and nodriver. trio-cdp is an early implementation of python-cdp, and nodriver is a modern implementation of python-cdp. (Refactored Python-CDP code is imported from MyCDP.) diff --git a/examples/cdp_mode/raw_glassdoor.py b/examples/cdp_mode/raw_glassdoor.py new file mode 100644 index 00000000..5fc31491 --- /dev/null +++ b/examples/cdp_mode/raw_glassdoor.py @@ -0,0 +1,11 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, ad_block=True) as sb: + url = "https://www.glassdoor.com/Reviews/index.htm" + sb.activate_cdp_mode(url) + sb.uc_gui_click_captcha() + sb.highlight('[data-test="global-nav-glassdoor-logo"]') + sb.highlight('[data-test="site-header-companies"]') + sb.highlight('[data-test="search-button"]') + sb.highlight('[data-test="sign-in-button"]') + sb.highlight('[data-test="company-search-autocomplete"]') diff --git a/examples/raw_invisible_captcha.py b/examples/raw_invisible_captcha.py new file mode 100644 index 00000000..54989667 --- /dev/null +++ b/examples/raw_invisible_captcha.py @@ -0,0 +1,9 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, incognito=True) as sb: + url = "https://seleniumbase.io/apps/invisible_recaptcha" + sb.activate_cdp_mode(url) + sb.sleep(1) + sb.assert_element("img#captcha-success", timeout=3) + sb.set_messenger_theme(location="top_left") + sb.post_message("SeleniumBase wasn't detected", duration=3) diff --git a/examples/raw_recaptcha.py b/examples/raw_recaptcha.py index 51cfc164..8277f567 100644 --- a/examples/raw_recaptcha.py +++ b/examples/raw_recaptcha.py @@ -1,14 +1,5 @@ from seleniumbase import SB -with SB(uc=True, test=True, incognito=True) as sb: - url = "https://seleniumbase.io/apps/recaptcha" - sb.activate_cdp_mode(url) - sb.sleep(1) - sb.uc_gui_handle_captcha() # Try with TAB + SPACEBAR - sb.assert_element("img#captcha-success", timeout=3) - sb.set_messenger_theme(location="top_left") - sb.post_message("SeleniumBase wasn't detected", duration=3) - with SB(uc=True, test=True, incognito=True) as sb: url = "https://seleniumbase.io/apps/recaptcha" sb.activate_cdp_mode(url) diff --git a/examples/uc_cdp_events.py b/examples/uc_cdp_events.py index 6fcaf2c2..1e596a1f 100644 --- a/examples/uc_cdp_events.py +++ b/examples/uc_cdp_events.py @@ -1,6 +1,6 @@ from rich.pretty import pprint from seleniumbase import BaseCase -BaseCase.main(__name__, __file__, "--uc", "--uc-cdp", "-s") +BaseCase.main(__name__, __file__, "--uc", "--uc-cdp") class CDPTests(BaseCase): diff --git a/examples/verify_undetected.py b/examples/verify_undetected.py index e00ab35a..555c485d 100644 --- a/examples/verify_undetected.py +++ b/examples/verify_undetected.py @@ -2,7 +2,7 @@ Some sites use scripts to detect Selenium, and then block you. To evade detection, add --uc as a pytest command-line option.""" from seleniumbase import BaseCase -BaseCase.main(__name__, __file__, "--uc", "-s") +BaseCase.main(__name__, __file__, "--uc") class UndetectedTest(BaseCase): From c9c5984b20fcd207f1786573984ca950f5bc1663 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:31:05 -0500 Subject: [PATCH 09/10] Update the docs --- help_docs/uc_mode.md | 8 +- integrations/docker/ReadMe.md | 4 +- seleniumbase/console_scripts/ReadMe.md | 150 +++++++++++++++---------- 3 files changed, 99 insertions(+), 63 deletions(-) diff --git a/help_docs/uc_mode.md b/help_docs/uc_mode.md index 02921e89..ef9d37f7 100644 --- a/help_docs/uc_mode.md +++ b/help_docs/uc_mode.md @@ -8,22 +8,22 @@ --- - +

(Watch the 1st UC Mode tutorial on YouTube! ▶️)

---- - +

(Watch the 2nd UC Mode tutorial on YouTube! ▶️)

---- - +

(Watch the 3rd UC Mode tutorial on YouTube! ▶️)

---- - +

(Watch the 4th UC Mode tutorial on YouTube! ▶️)

---- diff --git a/integrations/docker/ReadMe.md b/integrations/docker/ReadMe.md index 455be9b4..024b4e24 100644 --- a/integrations/docker/ReadMe.md +++ b/integrations/docker/ReadMe.md @@ -14,11 +14,11 @@ https://docs.docker.com/engine/install/ docker build -t seleniumbase . -If running on an Apple M1/M2 Mac, use this instead: +**(NOTE) - If running on an Apple M1/M2 Mac, use this instead:** docker build --platform linux/amd64 -t seleniumbase . -M1/M2 Mac users should also see [StackOverflow.com/a/76586216/7058266](https://stackoverflow.com/a/76586216/7058266) to **Enable Rosetta in Docker Desktop**. (Otherwise **you will** encounter errors like this when Chrome tries to launch: `"Chrome failed to start: crashed."`) +**M1/M2 Mac users** should also see [StackOverflow.com/a/76586216/7058266](https://stackoverflow.com/a/76586216/7058266) to **Enable Rosetta in Docker Desktop**. (Otherwise **you will** encounter errors like this when Chrome tries to launch: `"Chrome failed to start: crashed."`) #### 4. Run [the example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) with Chrome inside your Docker: (Once the test completes after a few seconds, you'll automatically exit the Docker shell) diff --git a/seleniumbase/console_scripts/ReadMe.md b/seleniumbase/console_scripts/ReadMe.md index 04dcc5b5..43faa057 100644 --- a/seleniumbase/console_scripts/ReadMe.md +++ b/seleniumbase/console_scripts/ReadMe.md @@ -290,7 +290,9 @@ sbase mkdir ui_tests * Options: -``-b`` / ``--basic`` (Only config files. No tests added.) +```bash +-b / --basic (Only config files. No tests added.) +``` * Output: @@ -350,27 +352,33 @@ sbase mkfile new_test.py * Options: -``--uc`` (UC Mode boilerplate using SB context manager) -`-b` / `--basic` (Basic boilerplate / single-line test) -`-r` / `--rec` (Adds Pdb+ breakpoint for Recorder Mode) -``--url=URL`` (Makes the test start on a specific page) +```bash +--uc (UC Mode boilerplate using SB context manager) +-b / --basic (Basic boilerplate / single-line test) +-r / --rec (Adds Pdb+ breakpoint for Recorder Mode) +--url=URL (Makes the test start on a specific page) +``` * Language Options: -``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` -``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` -``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` -``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` -``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` +```bash +--en / --English | --zh / --Chinese +--nl / --Dutch | --fr / --French +--it / --Italian | --ja / --Japanese +--ko / --Korean | --pt / --Portuguese +--ru / --Russian | --es / --Spanish +``` * Syntax Formats: -``--bc`` / ``--basecase`` (BaseCase class inheritance) -``--pf`` / ``--pytest-fixture`` (sb pytest fixture) -``--cf`` / ``--class-fixture`` (class + sb pytest fixture) -``--cm`` / ``--context-manager`` (SB context manager) -``--dc`` / ``--driver-context`` (DriverContext manager) -``--dm`` / ``--driver-manager`` (Driver manager) +```bash +--bc / --basecase (BaseCase class inheritance) +--pf / --pytest-fixture (sb pytest fixture) +--cf / --class-fixture (class + sb pytest fixture) +--cm / --context-manager (SB context manager) +--dc / --driver-context (DriverContext manager) +--dm / --driver-manager (Driver manager) +``` * Output: @@ -404,13 +412,15 @@ sbase codegen new_test.py --url=wikipedia.org * Options: -``--url=URL`` (Sets the initial start page URL.) -``--edge`` (Use Edge browser instead of Chrome.) -``--gui`` / ``--headed`` (Use headed mode on Linux.) -``--uc`` / ``--undetected`` (Use undetectable mode.) -``--ee`` (Use SHIFT + ESC to end the recording.) -``--overwrite`` (Overwrite file when it exists.) -``--behave`` (Also output Behave/Gherkin files.) +```bash +--url=URL (Sets the initial start page URL.) +--edge (Use Edge browser instead of Chrome.) +--gui / --headed (Use headed mode on Linux.) +--uc / --undetected (Use undetectable mode.) +--ee (Use SHIFT + ESC to end the recording.) +--overwrite (Overwrite file when it exists.) +--behave (Also output Behave/Gherkin files.) +``` * Output: @@ -427,8 +437,10 @@ sbase recorder [OPTIONS] * Options: -``--uc`` / ``--undetected`` (Use undetectable mode.) -``--behave`` (Also output Behave/Gherkin files.) +```bash +--uc / --undetected (Use undetectable mode.) +--behave (Also output Behave/Gherkin files.) +``` * Output: @@ -450,11 +462,13 @@ sbase mkpres new_presentation.py --en * Language Options: -``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` -``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` -``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` -``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` -``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` +```bash +--en / --English | --zh / --Chinese +--nl / --Dutch | --fr / --French +--it / --Italian | --ja / --Japanese +--ko / --Korean | --pt / --Portuguese +--ru / --Russian | --es / --Spanish +``` * Output: @@ -480,11 +494,13 @@ sbase mkchart new_chart.py --en * Language Options: -``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` -``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` -``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` -``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` -``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` +```bash +--en / --English | --zh / --Chinese +--nl / --Dutch | --fr / --French +--it / --Italian | --ja / --Japanese +--ko / --Korean | --pt / --Portuguese +--ru / --Russian | --es / --Spanish +``` * Output: @@ -504,7 +520,9 @@ sbase print [FILE] [OPTIONS] * Options: -``-n`` (Add line Numbers to the rows) +```bash +-n (Add line Numbers to the rows) +``` * Output: @@ -521,21 +539,27 @@ sbase translate [SB_FILE.py] [LANGUAGE] [ACTION] * Languages: -``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` -``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` -``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` -``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` -``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` +```bash +--en / --English | --zh / --Chinese +--nl / --Dutch | --fr / --French +--it / --Italian | --ja / --Japanese +--ko / --Korean | --pt / --Portuguese +--ru / --Russian | --es / --Spanish +``` * Actions: -``-p`` / ``--print`` (Print translation output to the screen) -``-o`` / ``--overwrite`` (Overwrite the file being translated) -``-c`` / ``--copy`` (Copy the translation to a new ``.py`` file) +```bash +-p / --print (Print translation output to the screen) +-o / --overwrite (Overwrite the file being translated) +-c / --copy (Copy the translation to a new ``.py`` file) +``` * Options: -``-n`` (include line Numbers when using the Print action) +```bash +-n (include line Numbers when using the Print action) +``` * Output: @@ -573,7 +597,9 @@ sbase inject-objects [SB_FILE.py] [OPTIONS] * Options: -``-c``, ``--comments`` (Add object selectors to the comments.) +```bash +-c / --comments (Add object selectors to the comments.) +``` * Output: @@ -591,7 +617,9 @@ sbase objectify [SB_FILE.py] [OPTIONS] * Options: -``-c``, ``--comments`` (Add object selectors to the comments.) +```bash +-c / --comments (Add object selectors to the comments.) +``` * Output: @@ -611,7 +639,9 @@ sbase revert-objects [SB_FILE.py] [OPTIONS] * Options: -``-c``, ``--comments`` (Keep existing comments for the lines.) +```bash +-c / --comments (Keep existing comments for the lines.) +``` * Output: @@ -639,7 +669,7 @@ Works on both Selenium IDE & Katalon Recorder scripts. * Usage: -``sbase encrypt`` OR ``sbase obfuscate`` +``sbase encrypt`` / ``sbase obfuscate`` * Output: @@ -650,7 +680,7 @@ Runs the password encryption/obfuscation tool. * Usage: -``sbase decrypt`` OR ``sbase unobfuscate`` +``sbase decrypt`` / ``sbase unobfuscate`` * Output: @@ -667,9 +697,11 @@ sbase proxy [OPTIONS] * Options: -``--hostname=HOSTNAME`` (Set ``hostname``) (Default: ``127.0.0.1``) -``--port=PORT`` (Set ``port``) (Default: ``8899``) -``--help`` / ``-h`` (Display list of all available ``proxy`` options.) +```bash +--hostname=HOSTNAME (Set `hostname`) (Default: `127.0.0.1`) +--port=PORT (Set `port`) (Default: `8899`) +--help / -h (Display available `proxy` options.) +``` * Output: @@ -699,8 +731,10 @@ sbase grid-hub {start|stop|restart} [OPTIONS] * Options: -``-v``, ``--verbose`` (Increases verbosity of logging output.) -``--timeout=TIMEOUT`` (Close idle browser windows after TIMEOUT seconds.) +```bash +-v / --verbose (Increases verbosity of logging output.) +--timeout=TIMEOUT (Close idle browser windows after TIMEOUT seconds.) +``` * Output: @@ -720,8 +754,10 @@ sbase grid-node {start|stop|restart} [OPTIONS] * Options: -``--hub=HUB_IP`` (The Grid Hub IP Address to connect to.) (Default: ``127.0.0.1``) -``-v``, ``--verbose`` (Increases verbosity of logging output.) +```bash +--hub=HUB_IP (Grid Hub IP Address. Default: `127.0.0.1`) +-v / --verbose (Increases verbosity of logging output.) +``` * Output: From 2d0a322f247329f8cd356b9eff9802acaa03ed1f Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Tue, 21 Jan 2025 18:31:29 -0500 Subject: [PATCH 10/10] Version 4.34.0 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 905605c9..d897bc15 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.33.15" +__version__ = "4.34.0"