diff --git a/requirements.txt b/requirements.txt index b6ae1b2f830..e5f25c72fed 100755 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ packaging>=20.9;python_version<"3.6" packaging>=21.2;python_version>="3.6" setuptools>=44.1.1;python_version<"3.5" setuptools>=50.3.2;python_version>="3.5" and python_version<"3.6" -setuptools>=59.0.1;python_version>="3.6" +setuptools>=59.1.1;python_version>="3.6" setuptools-scm>=5.0.2;python_version<"3.6" setuptools-scm>=6.3.2;python_version>="3.6" tomli>=1.2.2;python_version>="3.6" @@ -20,7 +20,7 @@ ipdb==0.13.9;python_version>="3.5" parso==0.7.1;python_version<"3.6" parso==0.8.2;python_version>="3.6" jedi==0.17.2;python_version<"3.6" -jedi==0.18.0;python_version>="3.6" +jedi==0.18.1;python_version>="3.6" idna==2.10;python_version<"3.6" idna==3.3;python_version>="3.6" chardet==3.0.4;python_version<"3.5" @@ -44,7 +44,7 @@ more-itertools==8.11.0;python_version>="3.5" cssselect==1.1.0 sortedcontainers==2.4.0 filelock==3.2.1;python_version<"3.6" -filelock==3.3.2;python_version>="3.6" +filelock==3.4.0;python_version>="3.6" fasteners==0.16;python_version<"3.5" fasteners==0.16.3;python_version>="3.5" execnet==1.9.0 @@ -108,7 +108,7 @@ Pillow==6.2.2;python_version<"3.5" Pillow==7.2.0;python_version>="3.5" and python_version<"3.6" Pillow==8.4.0;python_version>="3.6" typing-extensions==3.10.0.2;python_version<"3.8" -rich==10.13.0;python_version>="3.6" and python_version<"4.0" +rich==10.14.0;python_version>="3.6" and python_version<"4.0" tornado==5.1.1;python_version<"3.5" tornado==6.1;python_version>="3.5" pdfminer.six==20191110;python_version<"3.5" diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 6b7f5791132..70dd8e1e5d9 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "2.1.8" +__version__ = "2.1.9" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 8565a9dbcd8..060da3f007d 100755 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -19,6 +19,8 @@ selenium4 = False if sys.version_info[0] == 3 and sys.version_info[1] >= 7: selenium4 = True + from selenium.webdriver.common.options import ArgOptions + DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__)) # Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH # (Changes to the System PATH with os.environ only last during the test run) @@ -962,15 +964,28 @@ def get_remote_driver( device_height, device_pixel_ratio, ) - capabilities = chrome_options.to_capabilities() + capabilities = None + if selenium4: + capabilities = webdriver.ChromeOptions().to_capabilities() + else: + capabilities = chrome_options.to_capabilities() + # Set custom desired capabilities for key in desired_caps.keys(): capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + chrome_options.set_capability("cloud:options", capabilities) + return webdriver.Remote( + command_executor=address, + options=chrome_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.FIREFOX: firefox_options = _set_firefox_options( downloads_path, @@ -982,60 +997,115 @@ def get_remote_driver( firefox_arg, firefox_pref, ) - capabilities = firefox_options.to_capabilities() + capabilities = None + if selenium4: + capabilities = webdriver.FirefoxOptions().to_capabilities() + else: + capabilities = firefox_options.to_capabilities() capabilities["marionette"] = True if "linux" in PLATFORM: if headless: capabilities["moz:firefoxOptions"] = {"args": ["-headless"]} + # Set custom desired capabilities for key in desired_caps.keys(): capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + firefox_options.set_capability("cloud:options", capabilities) + return webdriver.Remote( + command_executor=address, + options=firefox_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.INTERNET_EXPLORER: capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.EDGE: capabilities = webdriver.DesiredCapabilities.EDGE - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.SAFARI: capabilities = webdriver.DesiredCapabilities.SAFARI - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.OPERA: capabilities = webdriver.DesiredCapabilities.OPERA - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - warnings.simplefilter("ignore", category=DeprecationWarning) - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.PHANTOM_JS: + if selenium4: + message = ( + "\n" + "PhantomJS is no longer available for Selenium 4!\n" + 'Try using "--headless" mode with Chrome instead!') + raise Exception(message) capabilities = webdriver.DesiredCapabilities.PHANTOMJS for key in desired_caps.keys(): capabilities[key] = desired_caps[key] @@ -1049,37 +1119,77 @@ def get_remote_driver( ) elif browser_name == constants.Browser.ANDROID: capabilities = webdriver.DesiredCapabilities.ANDROID - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.IPHONE: capabilities = webdriver.DesiredCapabilities.IPHONE - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.IPAD: capabilities = webdriver.DesiredCapabilities.IPAD - for key in desired_caps.keys(): - capabilities[key] = desired_caps[key] - return webdriver.Remote( - command_executor=address, - desired_capabilities=capabilities, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + for key in desired_caps.keys(): + capabilities[key] = desired_caps[key] + return webdriver.Remote( + command_executor=address, + desired_capabilities=capabilities, + keep_alive=True, + ) elif browser_name == constants.Browser.REMOTE: - return webdriver.Remote( - command_executor=address, - desired_capabilities=desired_caps, - keep_alive=True, - ) + if selenium4: + remote_options = ArgOptions() + remote_options.set_capability("cloud:options", desired_caps) + return webdriver.Remote( + command_executor=address, + options=remote_options, + keep_alive=True, + ) + else: + warnings.simplefilter("ignore", category=DeprecationWarning) + return webdriver.Remote( + command_executor=address, + desired_capabilities=desired_caps, + keep_alive=True, + ) def get_local_driver( @@ -1559,6 +1669,12 @@ def get_local_driver( except Exception: return webdriver.Opera() elif browser_name == constants.Browser.PHANTOM_JS: + if selenium4: + message = ( + "\n" + "PhantomJS is no longer available for Selenium 4!\n" + 'Try using "--headless" mode with Chrome instead!') + raise Exception(message) with warnings.catch_warnings(): # Ignore "PhantomJS has been deprecated" UserWarning warnings.simplefilter("ignore", category=UserWarning) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 5e53df7cdba..2e0bbd6e54f 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -8528,6 +8528,8 @@ def post_message(self, message, duration=None, pause=True, style="info"): duration = settings.DEFAULT_MESSAGE_DURATION else: duration = self.message_duration + if (self.headless or self.xvfb) and float(duration) > 0.75: + duration = 0.75 try: js_utils.post_message(self.driver, message, duration, style=style) except Exception: @@ -8562,6 +8564,8 @@ def post_success_message(self, message, duration=None, pause=True): duration = settings.DEFAULT_MESSAGE_DURATION else: duration = self.message_duration + if (self.headless or self.xvfb) and float(duration) > 0.75: + duration = 0.75 try: js_utils.post_message( self.driver, message, duration, style="success" @@ -8586,6 +8590,8 @@ def post_error_message(self, message, duration=None, pause=True): duration = settings.DEFAULT_MESSAGE_DURATION else: duration = self.message_duration + if (self.headless or self.xvfb) and float(duration) > 0.75: + duration = 0.75 try: js_utils.post_message( self.driver, message, duration, style="error" @@ -10406,13 +10412,23 @@ def __highlight_with_assert_success( time.sleep(0.065) def __highlight_with_js_2(self, message, selector, o_bs): + duration = self.message_duration + if not duration: + duration = settings.DEFAULT_MESSAGE_DURATION + if (self.headless or self.xvfb) and float(duration) > 0.75: + duration = 0.75 js_utils.highlight_with_js_2( - self.driver, message, selector, o_bs, self.message_duration + self.driver, message, selector, o_bs, duration ) def __highlight_with_jquery_2(self, message, selector, o_bs): + duration = self.message_duration + if not duration: + duration = settings.DEFAULT_MESSAGE_DURATION + if (self.headless or self.xvfb) and float(duration) > 0.75: + duration = 0.75 js_utils.highlight_with_jquery_2( - self.driver, message, selector, o_bs, self.message_duration + self.driver, message, selector, o_bs, duration ) ############ diff --git a/setup.py b/setup.py index 7035eb0d83c..816659b0837 100755 --- a/setup.py +++ b/setup.py @@ -43,14 +43,17 @@ sys.exit() else: print("*** No flake8 issues detected. Continuing...") - print("\n*** Rebuilding distribution packages: ***\n") + print("\n*** Removing existing distribution packages: ***\n") os.system("rm -f dist/*.egg; rm -f dist/*.tar.gz; rm -f dist/*.whl") os.system("rm -rf build/bdist.*; rm -rf build/lib") - os.system("python setup.py sdist bdist_wheel") # Create new tar/wheel + print("\n*** Installing build: *** (Required for PyPI uploads)\n") + os.system("python -m pip install --upgrade 'build>=0.7.0'") print("\n*** Installing twine: *** (Required for PyPI uploads)\n") - os.system("python -m pip install --upgrade 'twine>=1.15.0'") + os.system("python -m pip install --upgrade 'twine>=3.6.0'") print("\n*** Installing tqdm: *** (Required for PyPI uploads)\n") os.system("python -m pip install --upgrade 'tqdm>=4.62.3'") + print("\n*** Rebuilding distribution packages: ***\n") + os.system("python -m build") # Create new tar/wheel print("\n*** Publishing The Release to PyPI: ***\n") os.system("python -m twine upload dist/*") # Requires ~/.pypirc Keys print("\n*** The Release was PUBLISHED SUCCESSFULLY to PyPI! :) ***\n") @@ -120,7 +123,7 @@ 'packaging>=21.2;python_version>="3.6"', 'setuptools>=44.1.1;python_version<"3.5"', 'setuptools>=50.3.2;python_version>="3.5" and python_version<"3.6"', - 'setuptools>=59.0.1;python_version>="3.6"', + 'setuptools>=59.1.1;python_version>="3.6"', 'setuptools-scm>=5.0.2;python_version<"3.6"', 'setuptools-scm>=6.3.2;python_version>="3.6"', 'tomli>=1.2.2;python_version>="3.6"', @@ -136,7 +139,7 @@ 'parso==0.7.1;python_version<"3.6"', 'parso==0.8.2;python_version>="3.6"', 'jedi==0.17.2;python_version<"3.6"', - 'jedi==0.18.0;python_version>="3.6"', + 'jedi==0.18.1;python_version>="3.6"', 'idna==2.10;python_version<"3.6"', # Must stay in sync with "requests" 'idna==3.3;python_version>="3.6"', # Must stay in sync with "requests" 'chardet==3.0.4;python_version<"3.5"', # Stay in sync with "requests" @@ -160,7 +163,7 @@ "cssselect==1.1.0", "sortedcontainers==2.4.0", 'filelock==3.2.1;python_version<"3.6"', - 'filelock==3.3.2;python_version>="3.6"', + 'filelock==3.4.0;python_version>="3.6"', 'fasteners==0.16;python_version<"3.5"', 'fasteners==0.16.3;python_version>="3.5"', "execnet==1.9.0", @@ -224,7 +227,7 @@ 'Pillow==7.2.0;python_version>="3.5" and python_version<"3.6"', 'Pillow==8.4.0;python_version>="3.6"', 'typing-extensions==3.10.0.2;python_version<"3.8"', # Sync with "rich" - 'rich==10.13.0;python_version>="3.6" and python_version<"4.0"', + 'rich==10.14.0;python_version>="3.6" and python_version<"4.0"', 'tornado==5.1.1;python_version<"3.5"', 'tornado==6.1;python_version>="3.5"', 'pdfminer.six==20191110;python_version<"3.5"',