Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
9 changes: 9 additions & 0 deletions examples/cdp_mode/raw_cdp_recaptcha.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion examples/test_docs_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.43.2"
__version__ = "4.43.3"
72 changes: 68 additions & 4 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/core/detect_b_ver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
72 changes: 51 additions & 21 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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)"
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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)
Expand All @@ -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)"
Expand Down Expand Up @@ -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)"
Expand Down Expand Up @@ -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())

Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions seleniumbase/fixtures/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]


Expand Down Expand Up @@ -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 = [
Expand Down
4 changes: 2 additions & 2 deletions seleniumbase/fixtures/js_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading