diff --git a/README.md b/README.md index 9b441ca6564..e7850ff690b 100755 --- a/README.md +++ b/README.md @@ -662,6 +662,7 @@ pytest test_coffee_cart.py --trace --firefox-pref=SET # (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP # (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.) +--disable-features="F1,F2" # (Disable features, comma-separated, no spaces.) --binary-location=PATH # (Set path of the Chromium browser binary to use.) --driver-version=VER # (Set the chromedriver or uc_driver version to use.) --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) diff --git a/examples/boilerplates/boilerplate_test.py b/examples/boilerplates/boilerplate_test.py index fc043b520c0..a40b7430e8a 100644 --- a/examples/boilerplates/boilerplate_test.py +++ b/examples/boilerplates/boilerplate_test.py @@ -1,5 +1,10 @@ -from .base_test_case import BaseTestCase -from .page_objects import Page +try: # Run with "pytest" (relative imports are valid) + from .base_test_case import BaseTestCase + from .page_objects import Page +except (ImportError, ValueError): # Run with "python" + from base_test_case import BaseTestCase + from page_objects import Page + BaseTestCase.main(__name__, __file__) class MyTestClass(BaseTestCase): diff --git a/examples/raw_parameter_script.py b/examples/raw_parameter_script.py index d1436fa5bf1..89a5fcd3394 100644 --- a/examples/raw_parameter_script.py +++ b/examples/raw_parameter_script.py @@ -81,6 +81,7 @@ sb.window_size = None sb.maximize_option = False sb.visual_baseline = False + sb.disable_features = None sb._disable_beforeunload = False sb.save_screenshot_after_test = False sb.no_screenshot_after_test = False diff --git a/examples/test_todomvc.py b/examples/test_todomvc.py index 4d9b118acba..1ed61ca16c3 100644 --- a/examples/test_todomvc.py +++ b/examples/test_todomvc.py @@ -8,7 +8,7 @@ class TodoMVC(BaseCase): def test_todomvc(self, framework): self.open("https://todomvc.com/") self.clear_local_storage() - self.click('a[href="examples/%s"]' % framework) + self.click('a[href*="examples/%s/dist"]' % framework) self.assert_element("section.todoapp") self.assert_text("todos", "header h1") self.wait_for_ready_state_complete() @@ -25,5 +25,5 @@ def test_todomvc(self, framework): self.check_if_unchecked("ul.todo-list li:nth-of-type(2) input") self.check_if_unchecked("ul.todo-list li:nth-of-type(3) input") self.assert_text("0 items left", todo_count_span) - self.click('label[for="toggle-all"]') - self.assert_text("3 items left", todo_count_span) + self.click("button.clear-completed") + self.assert_element_not_visible(todo_count_span) diff --git a/examples/translations/russian_test_1.py b/examples/translations/russian_test_1.py index 1cfb057147b..e5800a98aef 100644 --- a/examples/translations/russian_test_1.py +++ b/examples/translations/russian_test_1.py @@ -7,7 +7,7 @@ class МойТестовыйКласс(ТестНаСелен): def test_пример_1(self): self.открыть("https://ru.wikipedia.org/wiki/") self.подтвердить_элемент('[title="Русский язык"]') - self.подтвердить_текст("Википедия", "h2.main-wikimedia-header") + self.подтвердить_текст("Википедия", "div.main-wikimedia-header") self.введите("#searchInput", "МГУ") self.нажмите("#searchButton") self.подтвердить_текст("университет", "#firstHeading") diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md index 2a8ed61498d..6f4af19cc72 100644 --- a/help_docs/customizing_test_runs.md +++ b/help_docs/customizing_test_runs.md @@ -138,6 +138,7 @@ pytest my_first_test.py --settings-file=custom_settings.py --firefox-pref=SET # (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP # (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.) +--disable-features="F1,F2" # (Disable features, comma-separated, no spaces.) --binary-location=PATH # (Set path of the Chromium browser binary to use.) --driver-version=VER # (Set the chromedriver or uc_driver version to use.) --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 7921b55a513..8525dba1d96 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -3,12 +3,12 @@ regex>=2023.12.25 pymdown-extensions>=10.7 -pipdeptree>=2.13.1 +pipdeptree>=2.13.2 python-dateutil>=2.8.2 -Markdown==3.5.1 +Markdown==3.5.2 markdown2==2.4.12 MarkupSafe==2.1.3 -Jinja2==3.1.2 +Jinja2==3.1.3 click==8.1.7 ghp-import==2.1.0 watchdog==3.0.0 @@ -16,11 +16,11 @@ cairocffi==1.6.1 pathspec==0.12.1 Babel==2.14.0 paginate==0.5.6 -lxml==5.0.0 +lxml==5.1.0 pyquery==2.0.0 readtime==3.0.0 mkdocs==1.5.3 -mkdocs-material==9.5.3 +mkdocs-material==9.5.4 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 7329a17ee39..7392461f024 100755 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,7 @@ sniffio==1.3.0 h11==0.14.0 outcome==1.3.0.post0 trio==0.22.2;python_version<"3.8" -trio==0.23.2;python_version>="3.8" +trio==0.24.0;python_version>="3.8" trio-websocket==0.11.1 wsproto==1.2.0 selenium==4.11.2;python_version<"3.8" @@ -48,7 +48,7 @@ sbvirtualdisplay==1.3.0 behave==1.2.6 soupsieve==2.4.1;python_version<"3.8" soupsieve==2.5;python_version>="3.8" -beautifulsoup4==4.12.2 +beautifulsoup4==4.12.3 pygments==2.17.2 pyreadline3==3.4.1;platform_system=="Windows" tabcompleter==1.3.0 @@ -70,9 +70,9 @@ coverage==7.4.0;python_version>="3.8" pytest-cov==4.0.0;python_version<"3.7" pytest-cov==4.1.0;python_version>="3.7" flake8==5.0.4;python_version<"3.9" -flake8==6.1.0;python_version>="3.9" +flake8==7.0.0;python_version>="3.9" mccabe==0.7.0 pyflakes==2.5.0;python_version<"3.9" -pyflakes==3.1.0;python_version>="3.9" +pyflakes==3.2.0;python_version>="3.9" pycodestyle==2.9.1;python_version<"3.9" pycodestyle==2.11.1;python_version>="3.9" diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index d825fe11b03..c30f7b44354 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.22.5" +__version__ = "4.22.6" diff --git a/seleniumbase/behave/behave_sb.py b/seleniumbase/behave/behave_sb.py index 9dc2a4741e1..c3208df0023 100644 --- a/seleniumbase/behave/behave_sb.py +++ b/seleniumbase/behave/behave_sb.py @@ -226,6 +226,7 @@ def get_configured_sb(context): sb.chromium_arg = None sb.firefox_arg = None sb.firefox_pref = None + sb.disable_features = None sb.proxy_string = None sb.proxy_bypass_list = None sb.proxy_pac_url = None @@ -748,6 +749,13 @@ def get_configured_sb(context): firefox_pref = sb.firefox_pref # revert to default sb.firefox_pref = firefox_pref continue + # Handle: -D disable-features="F1,F2" / disable_features="F1,F2" + if low_key in ["disable-features", "disable_features"]: + disable_features = userdata[key] + if disable_features == "true": + disable_features = sb.disable_features # revert to default + sb.disable_features = disable_features + continue # Handle: -D proxy=SERVER:PORT / proxy=USERNAME:PASSWORD@SERVER:PORT if low_key in ["proxy", "proxy-server", "proxy-string"]: proxy_string = userdata[key] diff --git a/seleniumbase/console_scripts/run.py b/seleniumbase/console_scripts/run.py index e614dcfccf0..1d6c45c35f1 100644 --- a/seleniumbase/console_scripts/run.py +++ b/seleniumbase/console_scripts/run.py @@ -75,10 +75,11 @@ def show_basic_usage(): seleniumbase_logo = logo_helper.get_seleniumbase_logo() print(seleniumbase_logo) print("") - time.sleep(0.25) # Enough time to see the logo + time.sleep(0.28) # Enough time to see the logo show_package_location() show_version_info() print("") + time.sleep(0.72) # Enough time to see the version sc = "" sc += ' * USAGE: "seleniumbase [COMMAND] [PARAMETERS]"\n' sc += ' * OR: "sbase [COMMAND] [PARAMETERS]"\n' diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 5ae567b3559..01ef9cb3174 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -763,6 +763,7 @@ def _set_chrome_options( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -1044,6 +1045,7 @@ def _set_chrome_options( binary_loc = detect_b_ver.get_binary_location(br_app, True) if os.path.exists(binary_loc): binary_location = binary_loc + extra_disabled_features = [] if chromium_arg: # Can be a comma-separated list of Chromium args chromium_arg_list = chromium_arg.split(",") @@ -1069,8 +1071,13 @@ def _set_chrome_options( ) if os.path.exists(binary_loc): binary_location = binary_loc + elif "disable-features=" in chromium_arg_item: + d_f = chromium_arg_item.split("disable-features=")[-1] + extra_disabled_features.append(d_f) elif len(chromium_arg_item) >= 3: chrome_options.add_argument(chromium_arg_item) + if disable_features: + extra_disabled_features.extend(disable_features.split(",")) if devtools and not headless: chrome_options.add_argument("--auto-open-devtools-for-tabs") if user_agent: @@ -1089,19 +1096,36 @@ def _set_chrome_options( if headless or headless2 or is_using_uc(undetectable, browser_name): chrome_options.add_argument("--disable-renderer-backgrounding") chrome_options.add_argument("--disable-backgrounding-occluded-windows") + chrome_options.add_argument("--disable-client-side-phishing-detection") + chrome_options.add_argument("--disable-oopr-debug-crash-dump") + chrome_options.add_argument("--disable-top-sites") chrome_options.add_argument("--ash-no-nudges") + chrome_options.add_argument("--no-crash-upload") chrome_options.add_argument("--deny-permission-prompts") + included_disabled_features = [] if user_data_dir: - chrome_options.add_argument( - "--disable-features=OptimizationHintsFetching,Translate," - "OptimizationTargetPrediction,PrivacySandboxSettings4," - "DownloadBubble,DownloadBubbleV2" - ) + included_disabled_features.append("OptimizationHintsFetching") + included_disabled_features.append("Translate") + included_disabled_features.append("OptimizationTargetPrediction") + included_disabled_features.append("PrivacySandboxSettings4") + included_disabled_features.append("DownloadBubble") + included_disabled_features.append("DownloadBubbleV2") + for item in extra_disabled_features: + if item not in included_disabled_features: + included_disabled_features.append(item) + d_f_string = ",".join(included_disabled_features) + chrome_options.add_argument("--disable-features=%s" % d_f_string) else: - chrome_options.add_argument( - "--disable-features=OptimizationHintsFetching,Translate," - "OptimizationTargetPrediction,DownloadBubble,DownloadBubbleV2" - ) + included_disabled_features.append("OptimizationHintsFetching") + included_disabled_features.append("Translate") + included_disabled_features.append("OptimizationTargetPrediction") + included_disabled_features.append("DownloadBubble") + included_disabled_features.append("DownloadBubbleV2") + for item in extra_disabled_features: + if item not in included_disabled_features: + included_disabled_features.append(item) + d_f_string = ",".join(included_disabled_features) + chrome_options.add_argument("--disable-features=%s" % d_f_string) if ( is_using_uc(undetectable, browser_name) and ( @@ -1338,6 +1362,7 @@ def get_driver( user_data_dir=None, extension_zip=None, extension_dir=None, + disable_features=None, binary_location=None, driver_version=None, page_load_strategy=None, @@ -1550,6 +1575,7 @@ def get_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -1605,6 +1631,7 @@ def get_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -1664,6 +1691,7 @@ def get_remote_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -1784,6 +1812,7 @@ def get_remote_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -1955,6 +1984,7 @@ def get_remote_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -2075,6 +2105,7 @@ def get_local_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -2570,18 +2601,6 @@ def get_local_driver( edge_options.add_argument( "--disable-autofill-keyboard-accessory-view[8]" ) - edge_options.add_argument("--ash-no-nudges") - edge_options.add_argument("--deny-permission-prompts") - if user_data_dir: - edge_options.add_argument( - "--disable-features=OptimizationHintsFetching,Translate," - "OptimizationTargetPrediction,PrivacySandboxSettings4" - ) - else: - edge_options.add_argument( - "--disable-features=OptimizationHintsFetching,Translate," - "OptimizationTargetPrediction" - ) edge_options.add_argument("--disable-browser-side-navigation") edge_options.add_argument("--disable-translate") if not enable_ws: @@ -2596,6 +2615,12 @@ def get_local_driver( if headless or headless2 or is_using_uc(undetectable, browser_name): edge_options.add_argument("--disable-renderer-backgrounding") edge_options.add_argument("--disable-backgrounding-occluded-windows") + edge_options.add_argument("--disable-client-side-phishing-detection") + edge_options.add_argument("--disable-oopr-debug-crash-dump") + edge_options.add_argument("--disable-top-sites") + edge_options.add_argument("--ash-no-nudges") + edge_options.add_argument("--no-crash-upload") + edge_options.add_argument("--deny-permission-prompts") if ( page_load_strategy and page_load_strategy.lower() in ["eager", "none"] @@ -2677,6 +2702,7 @@ def get_local_driver( edge_options.add_argument("--disable-gpu") if IS_LINUX: edge_options.add_argument("--disable-dev-shm-usage") + extra_disabled_features = [] set_binary = False if chromium_arg: # Can be a comma-separated list of Chromium args @@ -2690,8 +2716,33 @@ def get_local_driver( chromium_arg_item = "--" + chromium_arg_item if "set-binary" in chromium_arg_item: set_binary = True + elif "disable-features=" in chromium_arg_item: + d_f = chromium_arg_item.split("disable-features=")[-1] + extra_disabled_features.append(d_f) elif len(chromium_arg_item) >= 3: edge_options.add_argument(chromium_arg_item) + if disable_features: + extra_disabled_features.extend(disable_features.split(",")) + included_disabled_features = [] + if user_data_dir: + included_disabled_features.append("OptimizationHintsFetching") + included_disabled_features.append("Translate") + included_disabled_features.append("OptimizationTargetPrediction") + included_disabled_features.append("PrivacySandboxSettings4") + for item in extra_disabled_features: + if item not in included_disabled_features: + included_disabled_features.append(item) + d_f_string = ",".join(included_disabled_features) + edge_options.add_argument("--disable-features=%s" % d_f_string) + else: + included_disabled_features.append("OptimizationHintsFetching") + included_disabled_features.append("Translate") + included_disabled_features.append("OptimizationTargetPrediction") + for item in extra_disabled_features: + if item not in included_disabled_features: + included_disabled_features.append(item) + d_f_string = ",".join(included_disabled_features) + edge_options.add_argument("--disable-features=%s" % d_f_string) if (set_binary or IS_LINUX) and not binary_location: br_app = "edge" binary_loc = detect_b_ver.get_binary_location(br_app) @@ -2831,6 +2882,7 @@ def get_local_driver( user_data_dir, extension_zip, extension_dir, + disable_features, binary_location, driver_version, page_load_strategy, @@ -3348,6 +3400,7 @@ def get_local_driver( None, # user_data_dir None, # extension_zip None, # extension_dir + None, # disable_features binary_location, driver_version, page_load_strategy, @@ -3565,6 +3618,7 @@ def get_local_driver( None, # user_data_dir None, # extension_zip None, # extension_dir + None, # disable_features binary_location, driver_version, page_load_strategy, diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 408625d5ae8..9c47869655e 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -3777,6 +3777,7 @@ def get_new_driver( user_data_dir=None, extension_zip=None, extension_dir=None, + disable_features=None, binary_location=None, driver_version=None, page_load_strategy=None, @@ -3835,6 +3836,7 @@ def get_new_driver( user_data_dir - Chrome's User Data Directory to use (Chrome-only) extension_zip - A Chrome Extension ZIP file to use (Chrome-only) extension_dir - A Chrome Extension folder to use (Chrome-only) + disable_features - the option to disable features on Chrome/Edge 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) @@ -3960,6 +3962,8 @@ def get_new_driver( extension_zip = self.extension_zip if extension_dir is None: extension_dir = self.extension_dir + if disable_features is None: + disable_features = self.disable_features if binary_location is None: binary_location = self.binary_location if driver_version is None: @@ -4040,6 +4044,7 @@ def get_new_driver( user_data_dir=user_data_dir, extension_zip=extension_zip, extension_dir=extension_dir, + disable_features=disable_features, binary_location=binary_location, driver_version=driver_version, page_load_strategy=page_load_strategy, @@ -14331,6 +14336,7 @@ def setUp(self, masterqa_mode=False): self.user_data_dir = sb_config.user_data_dir self.extension_zip = sb_config.extension_zip self.extension_dir = sb_config.extension_dir + self.disable_features = sb_config.disable_features self.binary_location = sb_config.binary_location self.driver_version = sb_config.driver_version self.page_load_strategy = sb_config.page_load_strategy @@ -14652,6 +14658,7 @@ def setUp(self, masterqa_mode=False): user_data_dir=self.user_data_dir, extension_zip=self.extension_zip, extension_dir=self.extension_dir, + disable_features=self.disable_features, binary_location=self.binary_location, driver_version=self.driver_version, page_load_strategy=self.page_load_strategy, diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py index 122e463de95..0ee03d02abc 100644 --- a/seleniumbase/plugins/driver_manager.py +++ b/seleniumbase/plugins/driver_manager.py @@ -108,6 +108,7 @@ def Driver( user_data_dir=None, # Set the Chrome user data directory to use. extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None, # Load a Chrome Extension directory, comma-separated. + 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". @@ -264,6 +265,30 @@ def Driver( proxy_string = proxy_string[1:-1] elif proxy_string.startswith("'") and proxy_string.endswith("'"): proxy_string = proxy_string[1:-1] + c_a = chromium_arg + if c_a is None and "--chromium-arg" in arg_join: + if "--chromium-arg=" in arg_join: + c_a = arg_join.split("--chromium-arg=")[1].split(" ")[0] + elif "--chromium-arg " in arg_join: + c_a = arg_join.split("--chromium-arg ")[1].split(" ")[0] + if c_a: + if c_a.startswith('"') and c_a.endswith('"'): + c_a = c_a[1:-1] + elif c_a.startswith("'") and c_a.endswith("'"): + c_a = c_a[1:-1] + chromium_arg = c_a + d_f = disable_features + if d_f is None and "--disable-features" in arg_join: + if "--disable-features=" in arg_join: + d_f = arg_join.split("--disable-features=")[1].split(" ")[0] + elif "--disable-features " in arg_join: + d_f = arg_join.split("--disable-features ")[1].split(" ")[0] + if d_f: + if d_f.startswith('"') and d_f.endswith('"'): + d_f = d_f[1:-1] + elif c_a.startswith("'") and d_f.endswith("'"): + d_f = d_f[1:-1] + disable_features = d_f user_agent = agent recorder_mode = False if recorder_ext: @@ -505,6 +530,7 @@ def Driver( user_data_dir=user_data_dir, extension_zip=extension_zip, extension_dir=extension_dir, + disable_features=disable_features, binary_location=binary_location, driver_version=driver_version, page_load_strategy=page_load_strategy, diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index ca0686e89eb..c9e952f9e43 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -55,6 +55,7 @@ def pytest_addoption(parser): --firefox-pref=SET (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR (Load a Chrome Extension directory, comma-separated.) + --disable-features="F1,F2" (Disable features, comma-separated, no spaces.) --binary-location=PATH (Set path of the Chromium browser binary to use.) --driver-version=VER (Set the chromedriver or uc_driver version to use.) --sjw (Skip JS Waits for readyState to be "complete" or Angular to load.) @@ -199,6 +200,8 @@ def pytest_addoption(parser): constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.PERFORMANCE, + constants.Environment.REPLICA, + constants.Environment.FEDRAMP, constants.Environment.OFFLINE, constants.Environment.ONLINE, constants.Environment.MASTER, @@ -208,6 +211,7 @@ def pytest_addoption(parser): constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.DEMO, + constants.Environment.GDPR, constants.Environment.MAIN, constants.Environment.TEST, constants.Environment.GOV, @@ -624,6 +628,16 @@ def pytest_addoption(parser): (Can also be a comma-separated list of directories.) Default: None.""", ) + parser.addoption( + "--disable_features", + "--disable-features", + action="store", + dest="disable_features", + default=None, + help="""Disable Chromium features from Chrome/Edge browsers. + Format: A comma-separated list of Chromium features. + Default: None.""", + ) parser.addoption( "--binary_location", "--binary-location", @@ -1477,6 +1491,7 @@ def pytest_configure(config): sb_config.firefox_pref = config.getoption("firefox_pref") sb_config.extension_zip = config.getoption("extension_zip") sb_config.extension_dir = config.getoption("extension_dir") + sb_config.disable_features = config.getoption("disable_features") sb_config.binary_location = config.getoption("binary_location") sb_config.driver_version = config.getoption("driver_version") sb_config.page_load_strategy = config.getoption("page_load_strategy") diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index e0b1336857e..2fdcbd007b0 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -72,6 +72,7 @@ def SB( user_data_dir=None, # Set the Chrome user data directory to use. extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None, # Load a Chrome Extension directory, comma-separated. + 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. skip_js_waits=None, # Skip JS Waits (readyState=="complete" and Angular). @@ -325,6 +326,30 @@ def SB( proxy_string = proxy_string[1:-1] elif proxy_string.startswith("'") and proxy_string.endswith("'"): proxy_string = proxy_string[1:-1] + c_a = chromium_arg + if c_a is None and "--chromium-arg" in arg_join: + if "--chromium-arg=" in arg_join: + c_a = arg_join.split("--chromium-arg=")[1].split(" ")[0] + elif "--chromium-arg " in arg_join: + c_a = arg_join.split("--chromium-arg ")[1].split(" ")[0] + if c_a: + if c_a.startswith('"') and c_a.endswith('"'): + c_a = c_a[1:-1] + elif c_a.startswith("'") and c_a.endswith("'"): + c_a = c_a[1:-1] + chromium_arg = c_a + d_f = disable_features + if d_f is None and "--disable-features" in arg_join: + if "--disable-features=" in arg_join: + d_f = arg_join.split("--disable-features=")[1].split(" ")[0] + elif "--disable-features " in arg_join: + d_f = arg_join.split("--disable-features ")[1].split(" ")[0] + if d_f: + if d_f.startswith('"') and d_f.endswith('"'): + d_f = d_f[1:-1] + elif c_a.startswith("'") and d_f.endswith("'"): + d_f = d_f[1:-1] + disable_features = d_f user_agent = agent recorder_mode = False if recorder_ext: @@ -731,6 +756,7 @@ def SB( sb_config.chromium_arg = chromium_arg sb_config.firefox_arg = firefox_arg sb_config.firefox_pref = firefox_pref + sb_config.disable_features = disable_features sb_config.proxy_string = proxy_string sb_config.proxy_bypass_list = proxy_bypass_list sb_config.proxy_pac_url = proxy_pac_url @@ -833,6 +859,7 @@ def SB( sb.chromium_arg = sb_config.chromium_arg sb.firefox_arg = sb_config.firefox_arg sb.firefox_pref = sb_config.firefox_pref + sb.disable_features = sb_config.disable_features sb.proxy_string = sb_config.proxy_string sb.proxy_bypass_list = sb_config.proxy_bypass_list sb.proxy_pac_url = sb_config.proxy_pac_url diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index 3e9af8bc0ff..4adc70f2a1b 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -36,6 +36,7 @@ class SeleniumBrowser(Plugin): --firefox-pref=SET (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR (Load a Chrome Extension directory, comma-separated.) + --disable-features="F1,F2" (Disable features, comma-separated, no spaces.) --binary-location=PATH (Set path of the Chromium browser binary to use.) --driver-version=VER (Set the chromedriver or uc_driver version to use.) --sjw (Skip JS Waits for readyState to be "complete" or Angular to load.) @@ -365,6 +366,16 @@ def options(self, parser, env): (Can also be a comma-separated list of directories.) Default: None.""", ) + parser.addoption( + "--disable_features", + "--disable-features", + action="store", + dest="disable_features", + default=None, + help="""Disable Chromium features from Chrome/Edge browsers. + Format: A comma-separated list of Chromium features. + Default: None.""", + ) parser.addoption( "--binary_location", "--binary-location", @@ -1089,6 +1100,7 @@ def beforeTest(self, test): test.test.user_data_dir = self.options.user_data_dir test.test.extension_zip = self.options.extension_zip test.test.extension_dir = self.options.extension_dir + test.test.disable_features = self.options.disable_features test.test.binary_location = self.options.binary_location test.test.driver_version = self.options.driver_version test.test.page_load_strategy = self.options.page_load_strategy diff --git a/setup.py b/setup.py index 1dc8932cd4b..4bb2b6f1868 100755 --- a/setup.py +++ b/setup.py @@ -157,7 +157,7 @@ 'h11==0.14.0', 'outcome==1.3.0.post0', 'trio==0.22.2;python_version<"3.8"', - 'trio==0.23.2;python_version>="3.8"', + 'trio==0.24.0;python_version>="3.8"', 'trio-websocket==0.11.1', 'wsproto==1.2.0', 'selenium==4.11.2;python_version<"3.8"', @@ -181,7 +181,7 @@ "behave==1.2.6", 'soupsieve==2.4.1;python_version<"3.8"', 'soupsieve==2.5;python_version>="3.8"', - "beautifulsoup4==4.12.2", + "beautifulsoup4==4.12.3", 'pygments==2.17.2', 'pyreadline3==3.4.1;platform_system=="Windows"', "tabcompleter==1.3.0", @@ -214,10 +214,10 @@ # Usage: flake8 "flake8": [ 'flake8==5.0.4;python_version<"3.9"', - 'flake8==6.1.0;python_version>="3.9"', + 'flake8==7.0.0;python_version>="3.9"', "mccabe==0.7.0", 'pyflakes==2.5.0;python_version<"3.9"', - 'pyflakes==3.1.0;python_version>="3.9"', + 'pyflakes==3.2.0;python_version>="3.9"', 'pycodestyle==2.9.1;python_version<"3.9"', 'pycodestyle==2.11.1;python_version>="3.9"', ],