diff --git a/README.md b/README.md index 604050a08bf..ac0f892ddc1 100755 --- a/README.md +++ b/README.md @@ -652,6 +652,10 @@ pytest test_coffee_cart.py --trace --edge # (Shortcut for "--browser=edge".) --firefox # (Shortcut for "--browser=firefox".) --safari # (Shortcut for "--browser=safari".) +--opera # (Shortcut for "--browser=opera".) +--brave # (Shortcut for "--browser=brave".) +--comet # (Shortcut for "--browser=comet".) +--atlas # (Shortcut for "--browser=atlas".) --settings-file=FILE # (Override default SeleniumBase settings.) --env=ENV # (Set the test env. Access with "self.env" in tests.) --account=STR # (Set account. Access with "self.account" in tests.) diff --git a/examples/cdp_mode/raw_cdp_recaptcha.py b/examples/cdp_mode/raw_cdp_recaptcha.py new file mode 100644 index 00000000000..707800ed967 --- /dev/null +++ b/examples/cdp_mode/raw_cdp_recaptcha.py @@ -0,0 +1,9 @@ +from seleniumbase import sb_cdp + +url = "https://seleniumbase.io/apps/recaptcha" +sb = sb_cdp.Chrome(url) +sb.gui_click_captcha() +sb.assert_element("img#captcha-success") +sb.set_messenger_theme(location="top_left") +sb.post_message("SeleniumBase wasn't detected", duration=3) +sb.driver.stop() diff --git a/examples/test_docs_site.py b/examples/test_docs_site.py index e4f3bf872d8..8a3855600e8 100644 --- a/examples/test_docs_site.py +++ b/examples/test_docs_site.py @@ -18,5 +18,5 @@ def test_docs(self): self.assert_text("API Reference", "h1") self.js_click('a[href$="/uc_mode/"]') self.assert_text("UC Mode", "h1") - self.click('img[alt="logo"]') + self.js_click('img[alt="logo"]') self.assert_text("SeleniumBase", "h1") diff --git a/pyproject.toml b/pyproject.toml index 4c6c33d07b9..b846942f3c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,10 @@ packages = [ "seleniumbase.drivers", "seleniumbase.drivers.cft_drivers", "seleniumbase.drivers.chs_drivers", + "seleniumbase.drivers.opera_drivers", + "seleniumbase.drivers.brave_drivers", + "seleniumbase.drivers.comet_drivers", + "seleniumbase.drivers.atlas_drivers", "seleniumbase.extensions", "seleniumbase.fixtures", "seleniumbase.js_code", diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index a61c32f5597..276ca19001c 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.43.2" +__version__ = "4.43.3" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 1216bb57939..c8336f73b1f 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -28,6 +28,10 @@ from seleniumbase import drivers # webdriver storage folder for SeleniumBase from seleniumbase.drivers import cft_drivers # chrome-for-testing from seleniumbase.drivers import chs_drivers # chrome-headless-shell +from seleniumbase.drivers import opera_drivers # still uses chromedriver +from seleniumbase.drivers import brave_drivers # still uses chromedriver +from seleniumbase.drivers import comet_drivers # still uses chromedriver +from seleniumbase.drivers import atlas_drivers # still uses chromedriver from seleniumbase import extensions # browser extensions storage folder from seleniumbase.config import settings from seleniumbase.core import detect_b_ver @@ -44,6 +48,10 @@ DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__)) DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__)) DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__)) +DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__)) +DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__)) +DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__)) +DRIVER_DIR_ATLAS = os.path.dirname(os.path.realpath(atlas_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) if not os.environ["PATH"].startswith(DRIVER_DIR): @@ -1218,8 +1226,8 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25): driver.cdp.minimize() driver.cdp.set_window_rect(win_x, win_y, width, height) if IS_WINDOWS: - x = x * width_ratio - y = y * width_ratio + x = x * (width_ratio + 0.03) + y = y * (width_ratio - 0.03) _uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False) return with suppress(Exception): @@ -1260,7 +1268,7 @@ def _uc_gui_click_captcha( ctype=None, ): cdp_mode_on_at_start = __is_cdp_swap_needed(driver) - if cdp_mode_on_at_start and (not ctype or ctype == "cf_t"): + if cdp_mode_on_at_start: return driver.cdp.gui_click_captcha() _on_a_captcha_page = None if ctype == "cf_t": @@ -1952,6 +1960,15 @@ def get_valid_binary_names_for_browser(browser): raise Exception("Invalid combination for OS browser binaries!") +def _special_binary_exists(location, name): + filename = str(location).split("/")[-1].split("\\")[-1] + return ( + location + and str(name).lower() in filename.lower() + and os.path.exists(location) + ) + + def _repair_chromedriver(chrome_options, headless_options, mcv=None): if mcv: subprocess.check_call( @@ -2960,6 +2977,14 @@ def get_driver( and sb_config.binary_location == "chs" ): driver_dir = DRIVER_DIR_CHS + if _special_binary_exists(binary_location, "opera"): + driver_dir = DRIVER_DIR_OPERA + if _special_binary_exists(binary_location, "brave"): + driver_dir = DRIVER_DIR_BRAVE + if _special_binary_exists(binary_location, "comet"): + driver_dir = DRIVER_DIR_COMET + if _special_binary_exists(binary_location, "atlas"): + driver_dir = DRIVER_DIR_ATLAS if ( hasattr(sb_config, "settings") and hasattr(sb_config.settings, "NEW_DRIVER_DIR") @@ -3919,6 +3944,18 @@ def get_local_driver( ): special_chrome = True driver_dir = DRIVER_DIR_CHS + if _special_binary_exists(binary_location, "opera"): + special_chrome = True + driver_dir = DRIVER_DIR_OPERA + if _special_binary_exists(binary_location, "brave"): + special_chrome = True + driver_dir = DRIVER_DIR_BRAVE + if _special_binary_exists(binary_location, "comet"): + special_chrome = True + driver_dir = DRIVER_DIR_COMET + if _special_binary_exists(binary_location, "atlas"): + special_chrome = True + driver_dir = DRIVER_DIR_ATLAS if ( hasattr(sb_config, "settings") and hasattr(sb_config.settings, "NEW_DRIVER_DIR") @@ -4756,6 +4793,27 @@ def get_local_driver( ) return extend_driver(driver) elif browser_name == constants.Browser.GOOGLE_CHROME: + set_chromium = None + if _special_binary_exists(binary_location, "opera"): + set_chromium = "opera" + local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver" + if IS_WINDOWS: + local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver.exe" + if _special_binary_exists(binary_location, "brave"): + set_chromium = "brave" + local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver" + if IS_WINDOWS: + local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver.exe" + if _special_binary_exists(binary_location, "comet"): + set_chromium = "comet" + local_chromedriver = DRIVER_DIR_COMET + "/chromedriver" + if IS_WINDOWS: + local_chromedriver = DRIVER_DIR_COMET + "/chromedriver.exe" + if _special_binary_exists(binary_location, "atlas"): + set_chromium = "atlas" + local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver" + if IS_WINDOWS: + local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver.exe" try: chrome_options = _set_chrome_options( browser_name, @@ -4877,6 +4935,12 @@ def get_local_driver( major_chrome_version = None if major_chrome_version: use_version = major_chrome_version + if ( + set_chromium == "opera" + and use_version.isnumeric() + and int(use_version) < 130 + ): + use_version = "130" # Special case ch_driver_version = None path_chromedriver = chromedriver_on_path() if os.path.exists(local_chromedriver): @@ -4894,7 +4958,7 @@ def get_local_driver( ch_driver_version = output if driver_version == "keep": driver_version = ch_driver_version - elif path_chromedriver: + elif path_chromedriver and not set_chromium: try: make_driver_executable_if_not(path_chromedriver) except Exception as e: diff --git a/seleniumbase/core/detect_b_ver.py b/seleniumbase/core/detect_b_ver.py index 0125cdab15c..1006c810b9a 100644 --- a/seleniumbase/core/detect_b_ver.py +++ b/seleniumbase/core/detect_b_ver.py @@ -282,7 +282,7 @@ def opera_on_windows_path(browser_type=None): "Opera/Application", ): try: - candidates.append(os.sep.join((item, subitem, "launcher.exe"))) + candidates.append(os.sep.join((item, subitem, "opera.exe"))) except TypeError: pass for candidate in candidates: diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index 981a23cf8ce..9d364aab64d 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -1333,6 +1333,17 @@ def get_gui_element_rect(self, selector, timeout=None): x = window_rect["x"] + element_rect["x"] y = w_bottom_y - viewport_height + element_rect["y"] y_scroll_offset = window_rect["pageYOffset"] + if ( + hasattr(sb_config, "_cdp_browser") + and sb_config._cdp_browser == "opera" + ): + # Handle special case where Opera side panel shifts coordinates + x_offset = window_rect["outerWidth"] - window_rect["innerWidth"] + if x_offset > 56: + x_offset = 56 + elif x_offset < 22: + x_offset = 0 + x = x + x_offset y = y - y_scroll_offset x = x + window_rect["scrollX"] y = y + window_rect["scrollY"] @@ -1674,11 +1685,6 @@ def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False): import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() - if ( - hasattr(sb_config, "_cdp_browser") - and sb_config._cdp_browser == "opera" - ): - x = x + 55 if x < 0 or y < 0 or x > screen_width or y > screen_height: raise Exception( "PyAutoGUI cannot click on point (%s, %s)" @@ -1731,8 +1737,8 @@ def gui_click_x_y(self, x, y, timeframe=0.25): self.__add_light_pause() self.__set_window_rect(win_x, win_y, width, height) self.__add_light_pause() - x = x * width_ratio - y = y * width_ratio + x = x * (width_ratio + 0.03) + y = y * (width_ratio - 0.03) self.bring_active_window_to_front() self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False) @@ -1760,8 +1766,43 @@ def _on_a_cf_turnstile_page(self): return True return False + def _on_a_g_recaptcha_page(self): + time.sleep(0.042) + source = self.get_page_source() + if not source or len(source) < 400: + time.sleep(0.22) + source = self.get_page_source() + if ( + 'id="recaptcha-token"' in source + or 'title="reCAPTCHA"' in source + ): + return True + return False + + def __gui_click_recaptcha(self): + selector = None + if self.is_element_visible('iframe[title="reCAPTCHA"]'): + selector = 'iframe[title="reCAPTCHA"]' + else: + return + with suppress(Exception): + time.sleep(0.08) + element_rect = self.get_gui_element_rect(selector, timeout=1) + e_x = element_rect["x"] + e_y = element_rect["y"] + x = e_x + 29 + y = e_y + 35 + sb_config._saved_cf_x_y = (x, y) + time.sleep(0.08) + self.gui_click_x_y(x, y) + def gui_click_captcha(self): - if not self._on_a_cf_turnstile_page(): + if self._on_a_cf_turnstile_page(): + pass + elif self._on_a_g_recaptcha_page(): + self.__gui_click_recaptcha() + return + else: return selector = None if ( @@ -1926,7 +1967,7 @@ def gui_click_captcha(self): if not shared_utils.is_windows(): y = e_y + 32 else: - y = e_y + 22 + y = e_y + 28 sb_config._saved_cf_x_y = (x, y) time.sleep(0.08) self.gui_click_x_y(x, y) @@ -1936,12 +1977,6 @@ def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False): import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() - if ( - hasattr(sb_config, "_cdp_browser") - and sb_config._cdp_browser == "opera" - ): - x1 = x1 + 55 - x2 = x2 + 55 if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height: raise Exception( "PyAutoGUI cannot drag-drop from point (%s, %s)" @@ -2033,11 +2068,6 @@ def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False): import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() - if ( - hasattr(sb_config, "_cdp_browser") - and sb_config._cdp_browser == "opera" - ): - x = x + 55 if x < 0 or y < 0 or x > screen_width or y > screen_height: raise Exception( "PyAutoGUI cannot hover on point (%s, %s)" @@ -2118,7 +2148,7 @@ def gui_hover_element(self, selector, timeframe=0.25): if width > 0 and height > 0: x, y = self.get_gui_element_center(selector) self.bring_active_window_to_front() - self.__gui_hover_x_y(x, y, timeframe=timeframe) + self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) diff --git a/seleniumbase/drivers/atlas_drivers/__init__.py b/seleniumbase/drivers/atlas_drivers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seleniumbase/drivers/brave_drivers/__init__.py b/seleniumbase/drivers/brave_drivers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seleniumbase/drivers/comet_drivers/__init__.py b/seleniumbase/drivers/comet_drivers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seleniumbase/drivers/opera_drivers/__init__.py b/seleniumbase/drivers/opera_drivers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index 75f7562c195..58f754e9658 100644 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -387,6 +387,20 @@ class ValidBrowsers: "ie", "safari", "remote", + "opera", + "brave", + "comet", + "atlas", + ] + + +class ChromiumSubs: + # Chromium browsers that still use chromedriver + chromium_subs = [ + "opera", + "brave", + "comet", + "atlas", ] @@ -430,8 +444,12 @@ class ValidBinaries: "Google Chrome Beta", "Google Chrome Dev", "Brave Browser", + "Brave", + "Opera Browser", "Opera", + "Comet Browser", "Comet", + "Atlas Browser", "Atlas", ] valid_edge_binaries_on_macos = [ diff --git a/seleniumbase/fixtures/js_utils.py b/seleniumbase/fixtures/js_utils.py index fcf678c53dd..f37e3ce2e5d 100644 --- a/seleniumbase/fixtures/js_utils.py +++ b/seleniumbase/fixtures/js_utils.py @@ -1278,7 +1278,7 @@ def scroll_to_element(driver, element): try: screen_width = driver.get_window_size()["width"] except Exception: - screen_width = execute_script("return window.innerWidth;") + screen_width = execute_script(driver, "return window.innerWidth;") element_location_y = element_location_y - constants.Scroll.Y_OFFSET if element_location_y < 0: element_location_y = 0 @@ -1327,7 +1327,7 @@ def slow_scroll_to_element(driver, element, *args, **kwargs): try: screen_width = driver.get_window_size()["width"] except Exception: - screen_width = execute_script("return window.innerWidth;") + screen_width = execute_script(driver, "return window.innerWidth;") element_location_y = element_location_y - constants.Scroll.Y_OFFSET if element_location_y < 0: element_location_y = 0 diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py index 2b3bcc5212e..6eeee58ef77 100644 --- a/seleniumbase/plugins/driver_manager.py +++ b/seleniumbase/plugins/driver_manager.py @@ -239,6 +239,7 @@ def Driver( from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import browser_launcher + from seleniumbase.core import detect_b_ver from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils @@ -271,10 +272,37 @@ def Driver( ) elif existing_runner: sb_config._context_of_runner = True + sb_config._browser_shortcut = None + sb_config._cdp_browser = None + sb_config._cdp_bin_loc = None browser_changes = 0 browser_set = None browser_text = None browser_list = [] + # Check if binary-location in options + bin_loc_in_options = False + if ( + binary_location + and len(str(binary_location)) > 5 + and os.path.exists(str(binary_location)) + ): + bin_loc_in_options = True + else: + for arg in sys_argv: + if arg in ["--binary-location", "--binary_location", "--bl"]: + bin_loc_in_options = True + if ( + browser + and browser in constants.ChromiumSubs.chromium_subs + and not bin_loc_in_options + ): + bin_loc = detect_b_ver.get_binary_location(browser) + if bin_loc and os.path.exists(bin_loc): + if browser in bin_loc.lower().split("/")[-1].split("\\")[-1]: + sb_config._cdp_browser = browser + sb_config._cdp_bin_loc = bin_loc + binary_location = bin_loc + bin_loc_in_options = True # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser for tests. (Default: chrome) if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: @@ -301,6 +329,46 @@ def Driver( browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") + if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=opera") + if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=brave") + if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=comet") + if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -322,6 +390,46 @@ def Driver( browser_changes += 1 browser_text = "safari" browser_list.append("--safari") + if "--opera" in sys_argv and not browser_set == "opera": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--opera") + if "--brave" in sys_argv and not browser_set == "brave": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--brave") + if "--comet" in sys_argv and not browser_set == "comet": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--comet") + if "--atlas" in sys_argv and not browser_set == "atlas": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -345,6 +453,10 @@ def Driver( "Browser: {%s} is not a valid browser option. " "Valid options = {%s}" % (browser, valid_browsers) ) + if sb_config._browser_shortcut: + browser = sb_config._browser_shortcut + if browser in constants.ChromiumSubs.chromium_subs: + browser = "chrome" # Still uses chromedriver if headless is None: if "--headless" in sys_argv: headless = True @@ -534,6 +646,8 @@ def Driver( count += 1 user_agent = agent found_bl = None + if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: + binary_location = sb_config._cdp_bin_loc if binary_location is None and "--binary-location" in arg_join: count = 0 for arg in sys_argv: diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index e67d5dc4548..0a34b304a68 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -7,6 +7,7 @@ from contextlib import suppress from seleniumbase import config as sb_config from seleniumbase.config import settings +from seleniumbase.core import detect_b_ver from seleniumbase.core import log_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils @@ -28,6 +29,10 @@ def pytest_addoption(parser): --edge (Shortcut for "--browser=edge".) --firefox (Shortcut for "--browser=firefox".) --safari (Shortcut for "--browser=safari".) + --opera (Shortcut for "--browser=opera".) + --brave (Shortcut for "--browser=brave".) + --comet (Shortcut for "--browser=comet".) + --atlas (Shortcut for "--browser=atlas".) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) --settings-file=FILE (Override default SeleniumBase settings.) @@ -186,6 +191,34 @@ def pytest_addoption(parser): default=False, help="""Shortcut for --browser=safari""", ) + parser.addoption( + "--opera", + action="store_true", + dest="use_opera", + default=False, + help="""Shortcut for --browser=opera""", + ) + parser.addoption( + "--brave", + action="store_true", + dest="use_brave", + default=False, + help="""Shortcut for --browser=brave""", + ) + parser.addoption( + "--comet", + action="store_true", + dest="use_comet", + default=False, + help="""Shortcut for --browser=comet""", + ) + parser.addoption( + "--atlas", + action="store_true", + dest="use_atlas", + default=False, + help="""Shortcut for --browser=atlas""", + ) parser.addoption( "--cft", action="store_true", @@ -1388,8 +1421,14 @@ def pytest_addoption(parser): arg_join = " ".join(sys_argv) sb_config._browser_shortcut = None + sb_config._cdp_browser = None + sb_config._cdp_bin_loc = None sb_config._vd_list = [] - + # Check if binary-location in options + bin_loc_in_options = False + for arg in sys_argv: + if arg in ["--binary-location", "--binary_location", "--bl"]: + bin_loc_in_options = True # SeleniumBase does not support pytest-timeout due to hanging browsers. for arg in sys_argv: if "--timeout=" in arg: @@ -1465,6 +1504,46 @@ def pytest_addoption(parser): browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") + if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=opera") + if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=brave") + if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=comet") + if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -1491,6 +1570,46 @@ def pytest_addoption(parser): browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") + if "--opera" in sys_argv and not browser_set == "opera": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--opera") + if "--brave" in sys_argv and not browser_set == "brave": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--brave") + if "--comet" in sys_argv and not browser_set == "comet": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--comet") + if "--atlas" in sys_argv and not browser_set == "atlas": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--atlas") if browser_changes > 1: message = "\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -1525,7 +1644,7 @@ def pytest_addoption(parser): undetectable = True if ( browser_changes == 1 - and browser_text not in ["chrome"] + and browser_text not in ["chrome", "opera", "brave", "comet", "atlas"] and undetectable ): message = ( @@ -1557,6 +1676,8 @@ def pytest_configure(config): sb_config.browser = config.getoption("browser") if sb_config._browser_shortcut: sb_config.browser = sb_config._browser_shortcut + if sb_config.browser in constants.ChromiumSubs.chromium_subs: + sb_config.browser = "chrome" # Still uses chromedriver sb_config.account = config.getoption("account") sb_config.data = config.getoption("data") sb_config.var1 = config.getoption("var1") @@ -1591,6 +1712,8 @@ def pytest_configure(config): sb_config.extension_dir = config.getoption("extension_dir") sb_config.disable_features = config.getoption("disable_features") sb_config.binary_location = config.getoption("binary_location") + if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: + sb_config.binary_location = sb_config._cdp_bin_loc if config.getoption("use_cft") and not sb_config.binary_location: sb_config.binary_location = "cft" elif config.getoption("use_chs") and not sb_config.binary_location: diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index dc5ef38c854..ad58dbbeb21 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -267,6 +267,7 @@ def SB( import traceback from seleniumbase import config as sb_config from seleniumbase.config import settings + from seleniumbase.core import detect_b_ver from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils @@ -334,10 +335,37 @@ def SB( raise_test_failure = True # Exit on first error or failed test. else: raise_test_failure = False + sb_config._browser_shortcut = None + sb_config._cdp_browser = None + sb_config._cdp_bin_loc = None browser_changes = 0 browser_set = None browser_text = None browser_list = [] + # Check if binary-location in options + bin_loc_in_options = False + if ( + binary_location + and len(str(binary_location)) > 5 + and os.path.exists(str(binary_location)) + ): + bin_loc_in_options = True + else: + for arg in sys_argv: + if arg in ["--binary-location", "--binary_location", "--bl"]: + bin_loc_in_options = True + if ( + browser + and browser in constants.ChromiumSubs.chromium_subs + and not bin_loc_in_options + ): + bin_loc = detect_b_ver.get_binary_location(browser) + if bin_loc and os.path.exists(bin_loc): + if browser in bin_loc.lower().split("/")[-1].split("\\")[-1]: + sb_config._cdp_browser = browser + sb_config._cdp_bin_loc = bin_loc + binary_location = bin_loc + bin_loc_in_options = True # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser for tests. (Default: chrome) if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: @@ -364,6 +392,46 @@ def SB( browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") + if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=opera") + if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=brave") + if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=comet") + if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -390,6 +458,46 @@ def SB( browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") + if "--opera" in sys_argv and not browser_set == "opera": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--opera") + if "--brave" in sys_argv and not browser_set == "brave": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--brave") + if "--comet" in sys_argv and not browser_set == "comet": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--comet") + if "--atlas" in sys_argv and not browser_set == "atlas": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -692,7 +800,10 @@ def SB( uc_cdp_events = True else: uc_cdp_events = False - if undetectable and browser != "chrome": + if ( + undetectable + and browser not in ["chrome", "opera", "brave", "comet", "atlas"] + ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chrome!' '\n ("uc=True" / "undetectable=True" / "--uc")' @@ -1019,6 +1130,10 @@ def SB( sb_config.with_testing_base = with_testing_base sb_config.browser = browser + if sb_config._browser_shortcut: + sb_config.browser = sb_config._browser_shortcut + if sb_config.browser in constants.ChromiumSubs.chromium_subs: + sb_config.browser = "chrome" # Still uses chromedriver if not hasattr(sb_config, "is_behave"): sb_config.is_behave = False if not hasattr(sb_config, "is_pytest"): @@ -1082,6 +1197,8 @@ def SB( sb_config.save_screenshot = save_screenshot sb_config.no_screenshot = no_screenshot sb_config.binary_location = binary_location + if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: + sb_config.binary_location = sb_config._cdp_bin_loc sb_config.driver_version = driver_version sb_config.page_load_strategy = page_load_strategy sb_config.timeout_multiplier = timeout_multiplier diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index c64f503431a..5df85ab9f8d 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -1,9 +1,11 @@ """Selenium Plugin for SeleniumBase tests that run with pynose / nosetests""" +import os import sys from contextlib import suppress from nose.plugins import Plugin from seleniumbase import config as sb_config from seleniumbase.config import settings +from seleniumbase.core import detect_b_ver from seleniumbase.core import proxy_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils @@ -16,6 +18,10 @@ class SeleniumBrowser(Plugin): --edge (Shortcut for "--browser=edge".) --firefox (Shortcut for "--browser=firefox".) --safari (Shortcut for "--browser=safari".) + --opera (Shortcut for "--browser=opera".) + --brave (Shortcut for "--browser=brave".) + --comet (Shortcut for "--browser=comet".) + --atlas (Shortcut for "--browser=atlas".) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) --user-data-dir=DIR (Set the Chrome user data directory to use.) @@ -146,6 +152,34 @@ def options(self, parser, env): default=False, help="""Shortcut for --browser=safari""", ) + parser.addoption( + "--opera", + action="store_true", + dest="use_opera", + default=False, + help="""Shortcut for --browser=opera""", + ) + parser.addoption( + "--brave", + action="store_true", + dest="use_brave", + default=False, + help="""Shortcut for --browser=brave""", + ) + parser.addoption( + "--comet", + action="store_true", + dest="use_comet", + default=False, + help="""Shortcut for --browser=comet""", + ) + parser.addoption( + "--atlas", + action="store_true", + dest="use_atlas", + default=False, + help="""Shortcut for --browser=atlas""", + ) parser.addoption( "--cft", action="store_true", @@ -1057,6 +1091,11 @@ def beforeTest(self, test): browser_set = None browser_text = None browser_list = [] + # Check if binary-location in options + bin_loc_in_options = False + for arg in sys_argv: + if arg in ["--binary-location", "--binary_location", "--bl"]: + bin_loc_in_options = True if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: browser_changes += 1 browser_set = "chrome" @@ -1081,6 +1120,46 @@ def beforeTest(self, test): browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") + if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=opera") + if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=brave") + if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=comet") + if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_set = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 @@ -1107,6 +1186,46 @@ def beforeTest(self, test): browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") + if "--opera" in sys_argv and not browser_set == "opera": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("opera") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "opera" + sb_config._browser_shortcut = "opera" + sb_config._cdp_browser = "opera" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--opera") + if "--brave" in sys_argv and not browser_set == "brave": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("brave") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "brave" + sb_config._browser_shortcut = "brave" + sb_config._cdp_browser = "brave" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--brave") + if "--comet" in sys_argv and not browser_set == "comet": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("comet") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "comet" + sb_config._browser_shortcut = "comet" + sb_config._cdp_browser = "comet" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--comet") + if "--atlas" in sys_argv and not browser_set == "atlas": + if not bin_loc_in_options: + bin_loc = detect_b_ver.get_binary_location("atlas") + if os.path.exists(bin_loc): + browser_changes += 1 + browser_text = "atlas" + sb_config._browser_shortcut = "atlas" + sb_config._cdp_browser = "atlas" + sb_config._cdp_bin_loc = bin_loc + browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( @@ -1183,6 +1302,8 @@ def beforeTest(self, test): if sb_config._browser_shortcut: self.options.browser = sb_config._browser_shortcut test.test.browser = sb_config._browser_shortcut + if test.test.browser in constants.ChromiumSubs.chromium_subs: + test.test.browser = "chrome" # Still uses chromedriver test.test.cap_file = self.options.cap_file test.test.cap_string = self.options.cap_string test.test.headless = self.options.headless @@ -1219,6 +1340,8 @@ def beforeTest(self, test): test.test.extension_dir = self.options.extension_dir test.test.disable_features = self.options.disable_features test.test.binary_location = self.options.binary_location + if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: + test.test.binary_location = sb_config._cdp_bin_loc if self.options.use_cft and not test.test.binary_location: test.test.binary_location = "cft" elif self.options.use_chs and not test.test.binary_location: diff --git a/seleniumbase/undetected/cdp_driver/cdp_util.py b/seleniumbase/undetected/cdp_driver/cdp_util.py index a0e592a9cba..aeadd7f5e20 100644 --- a/seleniumbase/undetected/cdp_driver/cdp_util.py +++ b/seleniumbase/undetected/cdp_driver/cdp_util.py @@ -593,6 +593,20 @@ async def start( browser_binary = detect_b_ver.get_binary_location(browser) if browser_binary and os.path.exists(browser_binary): browser_executable_path = browser_binary + else: + bin_loc = str(browser_executable_path).lower() + if bin_loc.endswith("opera") or bin_loc.endswith("opera.exe"): + sb_config._cdp_browser = "opera" + elif bin_loc.endswith("edge") or bin_loc.endswith("edge.exe"): + sb_config._cdp_browser = "edge" + elif bin_loc.endswith("brave") or bin_loc.endswith("brave.exe"): + sb_config._cdp_browser = "brave" + elif bin_loc.endswith("comet") or bin_loc.endswith("comet.exe"): + sb_config._cdp_browser = "comet" + elif bin_loc.endswith("atlas") or bin_loc.endswith("atlas.exe"): + sb_config._cdp_browser = "atlas" + else: + sb_config._cdp_browser = "chrome" if not config: config = Config( user_data_dir, diff --git a/setup.py b/setup.py index 5eeaacccb42..3cb0a08561d 100755 --- a/setup.py +++ b/setup.py @@ -344,6 +344,10 @@ "seleniumbase.drivers", "seleniumbase.drivers.cft_drivers", "seleniumbase.drivers.chs_drivers", + "seleniumbase.drivers.opera_drivers", + "seleniumbase.drivers.brave_drivers", + "seleniumbase.drivers.comet_drivers", + "seleniumbase.drivers.atlas_drivers", "seleniumbase.extensions", "seleniumbase.fixtures", "seleniumbase.js_code",