Skip to content

Commit 7d0921b

Browse files
authored
Merge pull request #4048 from seleniumbase/advanced-chromium-support
Advanced Chromium Support
2 parents 4d0e168 + 1a58504 commit 7d0921b

File tree

20 files changed

+657
-33
lines changed

20 files changed

+657
-33
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,10 @@ pytest test_coffee_cart.py --trace
652652
--edge # (Shortcut for "--browser=edge".)
653653
--firefox # (Shortcut for "--browser=firefox".)
654654
--safari # (Shortcut for "--browser=safari".)
655+
--opera # (Shortcut for "--browser=opera".)
656+
--brave # (Shortcut for "--browser=brave".)
657+
--comet # (Shortcut for "--browser=comet".)
658+
--atlas # (Shortcut for "--browser=atlas".)
655659
--settings-file=FILE # (Override default SeleniumBase settings.)
656660
--env=ENV # (Set the test env. Access with "self.env" in tests.)
657661
--account=STR # (Set account. Access with "self.account" in tests.)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from seleniumbase import sb_cdp
2+
3+
url = "https://seleniumbase.io/apps/recaptcha"
4+
sb = sb_cdp.Chrome(url)
5+
sb.gui_click_captcha()
6+
sb.assert_element("img#captcha-success")
7+
sb.set_messenger_theme(location="top_left")
8+
sb.post_message("SeleniumBase wasn't detected", duration=3)
9+
sb.driver.stop()

examples/test_docs_site.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ def test_docs(self):
1818
self.assert_text("API Reference", "h1")
1919
self.js_click('a[href$="/uc_mode/"]')
2020
self.assert_text("UC Mode", "h1")
21-
self.click('img[alt="logo"]')
21+
self.js_click('img[alt="logo"]')
2222
self.assert_text("SeleniumBase", "h1")

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ packages = [
4343
"seleniumbase.drivers",
4444
"seleniumbase.drivers.cft_drivers",
4545
"seleniumbase.drivers.chs_drivers",
46+
"seleniumbase.drivers.opera_drivers",
47+
"seleniumbase.drivers.brave_drivers",
48+
"seleniumbase.drivers.comet_drivers",
49+
"seleniumbase.drivers.atlas_drivers",
4650
"seleniumbase.extensions",
4751
"seleniumbase.fixtures",
4852
"seleniumbase.js_code",

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.43.2"
2+
__version__ = "4.43.3"

seleniumbase/core/browser_launcher.py

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
2929
from seleniumbase.drivers import cft_drivers # chrome-for-testing
3030
from seleniumbase.drivers import chs_drivers # chrome-headless-shell
31+
from seleniumbase.drivers import opera_drivers # still uses chromedriver
32+
from seleniumbase.drivers import brave_drivers # still uses chromedriver
33+
from seleniumbase.drivers import comet_drivers # still uses chromedriver
34+
from seleniumbase.drivers import atlas_drivers # still uses chromedriver
3135
from seleniumbase import extensions # browser extensions storage folder
3236
from seleniumbase.config import settings
3337
from seleniumbase.core import detect_b_ver
@@ -44,6 +48,10 @@
4448
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
4549
DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__))
4650
DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__))
51+
DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__))
52+
DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__))
53+
DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__))
54+
DRIVER_DIR_ATLAS = os.path.dirname(os.path.realpath(atlas_drivers.__file__))
4755
# Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH
4856
# (Changes to the System PATH with os.environ only last during the test run)
4957
if not os.environ["PATH"].startswith(DRIVER_DIR):
@@ -1218,8 +1226,8 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
12181226
driver.cdp.minimize()
12191227
driver.cdp.set_window_rect(win_x, win_y, width, height)
12201228
if IS_WINDOWS:
1221-
x = x * width_ratio
1222-
y = y * width_ratio
1229+
x = x * (width_ratio + 0.03)
1230+
y = y * (width_ratio - 0.03)
12231231
_uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
12241232
return
12251233
with suppress(Exception):
@@ -1260,7 +1268,7 @@ def _uc_gui_click_captcha(
12601268
ctype=None,
12611269
):
12621270
cdp_mode_on_at_start = __is_cdp_swap_needed(driver)
1263-
if cdp_mode_on_at_start and (not ctype or ctype == "cf_t"):
1271+
if cdp_mode_on_at_start:
12641272
return driver.cdp.gui_click_captcha()
12651273
_on_a_captcha_page = None
12661274
if ctype == "cf_t":
@@ -1952,6 +1960,15 @@ def get_valid_binary_names_for_browser(browser):
19521960
raise Exception("Invalid combination for OS browser binaries!")
19531961

19541962

1963+
def _special_binary_exists(location, name):
1964+
filename = str(location).split("/")[-1].split("\\")[-1]
1965+
return (
1966+
location
1967+
and str(name).lower() in filename.lower()
1968+
and os.path.exists(location)
1969+
)
1970+
1971+
19551972
def _repair_chromedriver(chrome_options, headless_options, mcv=None):
19561973
if mcv:
19571974
subprocess.check_call(
@@ -2960,6 +2977,14 @@ def get_driver(
29602977
and sb_config.binary_location == "chs"
29612978
):
29622979
driver_dir = DRIVER_DIR_CHS
2980+
if _special_binary_exists(binary_location, "opera"):
2981+
driver_dir = DRIVER_DIR_OPERA
2982+
if _special_binary_exists(binary_location, "brave"):
2983+
driver_dir = DRIVER_DIR_BRAVE
2984+
if _special_binary_exists(binary_location, "comet"):
2985+
driver_dir = DRIVER_DIR_COMET
2986+
if _special_binary_exists(binary_location, "atlas"):
2987+
driver_dir = DRIVER_DIR_ATLAS
29632988
if (
29642989
hasattr(sb_config, "settings")
29652990
and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
@@ -3919,6 +3944,18 @@ def get_local_driver(
39193944
):
39203945
special_chrome = True
39213946
driver_dir = DRIVER_DIR_CHS
3947+
if _special_binary_exists(binary_location, "opera"):
3948+
special_chrome = True
3949+
driver_dir = DRIVER_DIR_OPERA
3950+
if _special_binary_exists(binary_location, "brave"):
3951+
special_chrome = True
3952+
driver_dir = DRIVER_DIR_BRAVE
3953+
if _special_binary_exists(binary_location, "comet"):
3954+
special_chrome = True
3955+
driver_dir = DRIVER_DIR_COMET
3956+
if _special_binary_exists(binary_location, "atlas"):
3957+
special_chrome = True
3958+
driver_dir = DRIVER_DIR_ATLAS
39223959
if (
39233960
hasattr(sb_config, "settings")
39243961
and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
@@ -4756,6 +4793,27 @@ def get_local_driver(
47564793
)
47574794
return extend_driver(driver)
47584795
elif browser_name == constants.Browser.GOOGLE_CHROME:
4796+
set_chromium = None
4797+
if _special_binary_exists(binary_location, "opera"):
4798+
set_chromium = "opera"
4799+
local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver"
4800+
if IS_WINDOWS:
4801+
local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver.exe"
4802+
if _special_binary_exists(binary_location, "brave"):
4803+
set_chromium = "brave"
4804+
local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver"
4805+
if IS_WINDOWS:
4806+
local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver.exe"
4807+
if _special_binary_exists(binary_location, "comet"):
4808+
set_chromium = "comet"
4809+
local_chromedriver = DRIVER_DIR_COMET + "/chromedriver"
4810+
if IS_WINDOWS:
4811+
local_chromedriver = DRIVER_DIR_COMET + "/chromedriver.exe"
4812+
if _special_binary_exists(binary_location, "atlas"):
4813+
set_chromium = "atlas"
4814+
local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver"
4815+
if IS_WINDOWS:
4816+
local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver.exe"
47594817
try:
47604818
chrome_options = _set_chrome_options(
47614819
browser_name,
@@ -4877,6 +4935,12 @@ def get_local_driver(
48774935
major_chrome_version = None
48784936
if major_chrome_version:
48794937
use_version = major_chrome_version
4938+
if (
4939+
set_chromium == "opera"
4940+
and use_version.isnumeric()
4941+
and int(use_version) < 130
4942+
):
4943+
use_version = "130" # Special case
48804944
ch_driver_version = None
48814945
path_chromedriver = chromedriver_on_path()
48824946
if os.path.exists(local_chromedriver):
@@ -4894,7 +4958,7 @@ def get_local_driver(
48944958
ch_driver_version = output
48954959
if driver_version == "keep":
48964960
driver_version = ch_driver_version
4897-
elif path_chromedriver:
4961+
elif path_chromedriver and not set_chromium:
48984962
try:
48994963
make_driver_executable_if_not(path_chromedriver)
49004964
except Exception as e:

seleniumbase/core/detect_b_ver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def opera_on_windows_path(browser_type=None):
282282
"Opera/Application",
283283
):
284284
try:
285-
candidates.append(os.sep.join((item, subitem, "launcher.exe")))
285+
candidates.append(os.sep.join((item, subitem, "opera.exe")))
286286
except TypeError:
287287
pass
288288
for candidate in candidates:

seleniumbase/core/sb_cdp.py

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,17 @@ def get_gui_element_rect(self, selector, timeout=None):
13331333
x = window_rect["x"] + element_rect["x"]
13341334
y = w_bottom_y - viewport_height + element_rect["y"]
13351335
y_scroll_offset = window_rect["pageYOffset"]
1336+
if (
1337+
hasattr(sb_config, "_cdp_browser")
1338+
and sb_config._cdp_browser == "opera"
1339+
):
1340+
# Handle special case where Opera side panel shifts coordinates
1341+
x_offset = window_rect["outerWidth"] - window_rect["innerWidth"]
1342+
if x_offset > 56:
1343+
x_offset = 56
1344+
elif x_offset < 22:
1345+
x_offset = 0
1346+
x = x + x_offset
13361347
y = y - y_scroll_offset
13371348
x = x + window_rect["scrollX"]
13381349
y = y + window_rect["scrollY"]
@@ -1674,11 +1685,6 @@ def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
16741685
import pyautogui
16751686
pyautogui = self.__get_configured_pyautogui(pyautogui)
16761687
screen_width, screen_height = pyautogui.size()
1677-
if (
1678-
hasattr(sb_config, "_cdp_browser")
1679-
and sb_config._cdp_browser == "opera"
1680-
):
1681-
x = x + 55
16821688
if x < 0 or y < 0 or x > screen_width or y > screen_height:
16831689
raise Exception(
16841690
"PyAutoGUI cannot click on point (%s, %s)"
@@ -1731,8 +1737,8 @@ def gui_click_x_y(self, x, y, timeframe=0.25):
17311737
self.__add_light_pause()
17321738
self.__set_window_rect(win_x, win_y, width, height)
17331739
self.__add_light_pause()
1734-
x = x * width_ratio
1735-
y = y * width_ratio
1740+
x = x * (width_ratio + 0.03)
1741+
y = y * (width_ratio - 0.03)
17361742
self.bring_active_window_to_front()
17371743
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
17381744

@@ -1760,8 +1766,43 @@ def _on_a_cf_turnstile_page(self):
17601766
return True
17611767
return False
17621768

1769+
def _on_a_g_recaptcha_page(self):
1770+
time.sleep(0.042)
1771+
source = self.get_page_source()
1772+
if not source or len(source) < 400:
1773+
time.sleep(0.22)
1774+
source = self.get_page_source()
1775+
if (
1776+
'id="recaptcha-token"' in source
1777+
or 'title="reCAPTCHA"' in source
1778+
):
1779+
return True
1780+
return False
1781+
1782+
def __gui_click_recaptcha(self):
1783+
selector = None
1784+
if self.is_element_visible('iframe[title="reCAPTCHA"]'):
1785+
selector = 'iframe[title="reCAPTCHA"]'
1786+
else:
1787+
return
1788+
with suppress(Exception):
1789+
time.sleep(0.08)
1790+
element_rect = self.get_gui_element_rect(selector, timeout=1)
1791+
e_x = element_rect["x"]
1792+
e_y = element_rect["y"]
1793+
x = e_x + 29
1794+
y = e_y + 35
1795+
sb_config._saved_cf_x_y = (x, y)
1796+
time.sleep(0.08)
1797+
self.gui_click_x_y(x, y)
1798+
17631799
def gui_click_captcha(self):
1764-
if not self._on_a_cf_turnstile_page():
1800+
if self._on_a_cf_turnstile_page():
1801+
pass
1802+
elif self._on_a_g_recaptcha_page():
1803+
self.__gui_click_recaptcha()
1804+
return
1805+
else:
17651806
return
17661807
selector = None
17671808
if (
@@ -1926,7 +1967,7 @@ def gui_click_captcha(self):
19261967
if not shared_utils.is_windows():
19271968
y = e_y + 32
19281969
else:
1929-
y = e_y + 22
1970+
y = e_y + 28
19301971
sb_config._saved_cf_x_y = (x, y)
19311972
time.sleep(0.08)
19321973
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):
19361977
import pyautogui
19371978
pyautogui = self.__get_configured_pyautogui(pyautogui)
19381979
screen_width, screen_height = pyautogui.size()
1939-
if (
1940-
hasattr(sb_config, "_cdp_browser")
1941-
and sb_config._cdp_browser == "opera"
1942-
):
1943-
x1 = x1 + 55
1944-
x2 = x2 + 55
19451980
if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height:
19461981
raise Exception(
19471982
"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):
20332068
import pyautogui
20342069
pyautogui = self.__get_configured_pyautogui(pyautogui)
20352070
screen_width, screen_height = pyautogui.size()
2036-
if (
2037-
hasattr(sb_config, "_cdp_browser")
2038-
and sb_config._cdp_browser == "opera"
2039-
):
2040-
x = x + 55
20412071
if x < 0 or y < 0 or x > screen_width or y > screen_height:
20422072
raise Exception(
20432073
"PyAutoGUI cannot hover on point (%s, %s)"
@@ -2118,7 +2148,7 @@ def gui_hover_element(self, selector, timeframe=0.25):
21182148
if width > 0 and height > 0:
21192149
x, y = self.get_gui_element_center(selector)
21202150
self.bring_active_window_to_front()
2121-
self.__gui_hover_x_y(x, y, timeframe=timeframe)
2151+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
21222152
self.__slow_mode_pause_if_set()
21232153
self.loop.run_until_complete(self.page.wait())
21242154

seleniumbase/drivers/atlas_drivers/__init__.py

Whitespace-only changes.

seleniumbase/drivers/brave_drivers/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)