Skip to content

Commit 6f332bf

Browse files
authored
Merge pull request #4106 from seleniumbase/updates-for-pytest-and-cdp-mode
Updates for pytest and CDP Mode
2 parents b17615d + daea854 commit 6f332bf

File tree

11 files changed

+136
-24
lines changed

11 files changed

+136
-24
lines changed

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ iniconfig==2.3.0;python_version>="3.10"
6565
pluggy==1.5.0;python_version<"3.9"
6666
pluggy==1.6.0;python_version>="3.9"
6767
pytest==8.3.5;python_version<"3.9"
68-
pytest==8.4.2;python_version>="3.9"
68+
pytest==8.4.2;python_version>="3.9" and python_version<"3.11"
69+
pytest==9.0.1;python_version>="3.11"
6970
pytest-html==4.0.2
7071
pytest-metadata==3.1.1
7172
pytest-ordering==0.6

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.44.11"
2+
__version__ = "4.44.12"

seleniumbase/core/browser_launcher.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,21 @@ def extend_driver(
159159
# Extend the driver with new methods
160160
driver.default_find_element = driver.find_element
161161
driver.default_find_elements = driver.find_elements
162+
driver.default_add_cookie = driver.add_cookie
163+
driver.default_get_cookie = driver.get_cookie
164+
driver.default_delete_cookie = driver.delete_cookie
165+
driver.default_back = driver.back
166+
driver.default_forward = driver.forward
167+
driver.default_refresh = driver.refresh
162168
DM = sb_driver.DriverMethods(driver)
163169
driver.find_element = DM.find_element
164170
driver.find_elements = DM.find_elements
171+
driver.add_cookie = DM.add_cookie
172+
driver.get_cookie = DM.get_cookie
173+
driver.delete_cookie = DM.delete_cookie
174+
driver.back = DM.back
175+
driver.forward = DM.forward
176+
driver.refresh = DM.refresh
165177
driver.locator = DM.locator
166178
page = types.SimpleNamespace()
167179
page.open = DM.open_url

seleniumbase/core/log_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
192192
and test._outcome.errors
193193
):
194194
try:
195-
exc_message = test._outcome.errors[0][1][1]
196-
traceback_address = test._outcome.errors[0][1][2]
195+
exc_message = test._outcome.errors[-1][1][1]
196+
traceback_address = test._outcome.errors[-1][1][2]
197197
traceback_list = traceback.format_list(
198198
traceback.extract_tb(traceback_address)[1:]
199199
)

seleniumbase/core/report_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def process_failures(test, test_count, duration):
111111
and test._outcome.errors
112112
):
113113
try:
114-
exc_message = test._outcome.errors[0][1][1]
114+
exc_message = test._outcome.errors[-1][1][1]
115115
except Exception:
116116
exc_message = "(Unknown Exception)"
117117
else:

seleniumbase/core/sb_cdp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,10 @@ def click(self, selector, timeout=None):
713713
tag_name in ["a", "button", "canvas", "div", "input", "li", "span"]
714714
and "contains(" not in selector
715715
):
716-
element.mouse_click() # Simulated click (NOT PyAutoGUI)
716+
try:
717+
element.mouse_click() # Simulated click (NOT PyAutoGUI)
718+
except Exception:
719+
element.click() # Standard CDP click
717720
else:
718721
element.click() # Standard CDP click
719722
self.__slow_mode_pause_if_set()

seleniumbase/core/sb_driver.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
class DriverMethods(WebDriver):
1313
def __init__(self, driver):
1414
self.driver = driver
15+
if hasattr(driver, "session_id"):
16+
self.session_id = driver.session_id
17+
if hasattr(driver, "command_executor"):
18+
self.command_executor = driver.command_executor
1519

1620
def __is_cdp_swap_needed(self):
1721
"""If the driver is disconnected, use a CDP method when available."""
@@ -37,6 +41,36 @@ def find_elements(self, by=None, value=None):
3741
value, by = page_utils.swap_selector_and_by_if_reversed(value, by)
3842
return self.driver.default_find_elements(by=by, value=value)
3943

44+
def add_cookie(self, *args, **kwargs):
45+
page_actions._reconnect_if_disconnected(self.driver)
46+
self.driver.default_add_cookie(*args, **kwargs)
47+
48+
def get_cookie(self, *args, **kwargs):
49+
page_actions._reconnect_if_disconnected(self.driver)
50+
self.driver.default_get_cookie(*args, **kwargs)
51+
52+
def delete_cookie(self, *args, **kwargs):
53+
page_actions._reconnect_if_disconnected(self.driver)
54+
self.driver.default_delete_cookie(*args, **kwargs)
55+
56+
def back(self):
57+
if self.__is_cdp_swap_needed():
58+
self.driver.cdp.go_back()
59+
return
60+
self.driver.default_back()
61+
62+
def forward(self):
63+
if self.__is_cdp_swap_needed():
64+
self.driver.cdp.go_forward()
65+
return
66+
self.driver.default_forward()
67+
68+
def refresh(self, *args, **kwargs):
69+
if self.__is_cdp_swap_needed():
70+
self.driver.cdp.refresh(*args, **kwargs)
71+
return
72+
self.driver.default_refresh()
73+
4074
def locator(self, selector, by=None):
4175
if not by:
4276
by = "css selector"

seleniumbase/fixtures/base_case.py

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3969,8 +3969,23 @@ def set_content_to_parent_frame(self):
39693969

39703970
def open_new_window(self, switch_to=True):
39713971
"""Opens a new browser tab/window and switches to it by default."""
3972+
url = None
3973+
if self.__looks_like_a_page_url(str(switch_to)):
3974+
# Different API for CDP Mode: First arg is a `url`.
3975+
# (Also, don't break backwards compat for reg mode)
3976+
url = switch_to
3977+
switch_to = True
39723978
if self.__is_cdp_swap_needed():
3973-
self.cdp.open_new_tab(switch_to=switch_to)
3979+
self.cdp.open_new_tab(url=url, switch_to=switch_to)
3980+
return
3981+
elif (
3982+
hasattr(self.driver, "_is_using_uc")
3983+
and self.driver._is_using_uc
3984+
and hasattr(self.driver, "_is_using_cdp")
3985+
and self.driver._is_using_cdp
3986+
):
3987+
self.disconnect()
3988+
self.cdp.open_new_tab(url=url, switch_to=switch_to)
39743989
return
39753990
self.wait_for_ready_state_complete()
39763991
if switch_to:
@@ -5040,9 +5055,8 @@ def activate_cdp_mode(self, url=None, **kwargs):
50405055
if hasattr(self.cdp, "find_element_by_text"):
50415056
self.find_element_by_text = self.cdp.find_element_by_text
50425057
if (
5043-
hasattr(sb_config, "_cdp_proxy")
5044-
and sb_config._cdp_proxy
5045-
and "@" in sb_config._cdp_proxy
5058+
hasattr(self.driver, "_is_using_auth")
5059+
and self.driver._is_using_auth
50465060
):
50475061
with suppress(Exception):
50485062
self.cdp.loop.run_until_complete(self.cdp.page.wait(0.25))
@@ -6364,6 +6378,7 @@ def press_up_arrow(self, selector="body", times=1, by="css selector"):
63646378
By default, "html" will be used as the CSS Selector target.
63656379
You can specify how many times in-a-row the action happens."""
63666380
self.__check_scope()
6381+
self._check_browser()
63676382
if times < 1:
63686383
return
63696384
element = self.wait_for_element_present(selector)
@@ -6386,6 +6401,7 @@ def press_down_arrow(self, selector="body", times=1, by="css selector"):
63866401
By default, "html" will be used as the CSS Selector target.
63876402
You can specify how many times in-a-row the action happens."""
63886403
self.__check_scope()
6404+
self._check_browser()
63896405
if times < 1:
63906406
return
63916407
element = self.wait_for_element_present(selector)
@@ -6408,6 +6424,7 @@ def press_left_arrow(self, selector="body", times=1, by="css selector"):
64086424
By default, "html" will be used as the CSS Selector target.
64096425
You can specify how many times in-a-row the action happens."""
64106426
self.__check_scope()
6427+
self._check_browser()
64116428
if times < 1:
64126429
return
64136430
element = self.wait_for_element_present(selector)
@@ -6430,6 +6447,7 @@ def press_right_arrow(self, selector="body", times=1, by="css selector"):
64306447
By default, "html" will be used as the CSS Selector target.
64316448
You can specify how many times in-a-row the action happens."""
64326449
self.__check_scope()
6450+
self._check_browser()
64336451
if times < 1:
64346452
return
64356453
element = self.wait_for_element_present(selector)
@@ -8781,6 +8799,9 @@ def set_text(self, selector, text, by="css selector", timeout=None):
87818799
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
87828800
timeout = self.__get_new_timeout(timeout)
87838801
selector, by = self.__recalculate_selector(selector, by)
8802+
if self.__is_cdp_swap_needed():
8803+
self.cdp.set_value(selector, text)
8804+
return
87848805
self.wait_for_ready_state_complete()
87858806
element = page_actions.wait_for_element_present(
87868807
self.driver, selector, by, timeout
@@ -8801,10 +8822,14 @@ def set_text_content(
88018822
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
88028823
timeout = self.__get_new_timeout(timeout)
88038824
selector, by = self.__recalculate_selector(selector, by)
8804-
self.wait_for_ready_state_complete()
8805-
element = page_actions.wait_for_element_present(
8806-
self.driver, selector, by, timeout
8807-
)
8825+
element = None
8826+
if self.__is_cdp_swap_needed():
8827+
element = self.cdp.select(selector, timeout=timeout)
8828+
else:
8829+
self.wait_for_ready_state_complete()
8830+
element = page_actions.wait_for_element_present(
8831+
self.driver, selector, by, timeout
8832+
)
88088833
if element.tag_name.lower() in ["input", "textarea"]:
88098834
self.js_update_text(selector, text, by=by, timeout=timeout)
88108835
return
@@ -15945,7 +15970,7 @@ def __get_exception_info(self):
1594515970
and self._outcome.errors
1594615971
):
1594715972
try:
15948-
exc_message = self._outcome.errors[0][1][1]
15973+
exc_message = self._outcome.errors[-1][1][1]
1594915974
except Exception:
1595015975
exc_message = "(Unknown Exception)"
1595115976
else:
@@ -16093,8 +16118,16 @@ def __has_exception(self):
1609316118
else:
1609416119
return False
1609516120
elif hasattr(self, "_outcome") and hasattr(self._outcome, "errors"):
16096-
if self._outcome.errors:
16097-
has_exception = True
16121+
if python3_11_or_newer:
16122+
if (
16123+
self._outcome.errors
16124+
and self._outcome.errors[-1]
16125+
and self._outcome.errors[-1][1]
16126+
):
16127+
has_exception = True
16128+
else:
16129+
if self._outcome.errors:
16130+
has_exception = True
1609816131
else:
1609916132
has_exception = sys.exc_info()[1] is not None
1610016133
if self.__will_be_skipped and hasattr(self, "_using_sb_fixture"):

seleniumbase/masterqa/master_qa.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
from seleniumbase.fixtures import js_utils
1212

1313

14+
python3_11_or_newer = False
15+
if sys.version_info >= (3, 11):
16+
python3_11_or_newer = True
17+
18+
1419
class MasterQA(BaseCase):
1520
def setUp(self):
1621
self.check_count = 0
@@ -309,8 +314,17 @@ def __has_exception(self):
309314
if hasattr(sys, "last_traceback") and sys.last_traceback is not None:
310315
has_exception = True
311316
elif hasattr(self, "_outcome"):
312-
if hasattr(self._outcome, "errors") and self._outcome.errors:
313-
has_exception = True
317+
if hasattr(self._outcome, "errors"):
318+
if python3_11_or_newer:
319+
if (
320+
self._outcome.errors
321+
and self._outcome.errors[-1]
322+
and self._outcome.errors[-1][1]
323+
):
324+
has_exception = True
325+
else:
326+
if self._outcome.errors:
327+
has_exception = True
314328
else:
315329
has_exception = sys.exc_info()[1] is not None
316330
return has_exception

seleniumbase/undetected/cdp_driver/browser.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,28 @@ async def get(
430430
username_and_password = sb_config._cdp_proxy.split("@")[0]
431431
proxy_user = username_and_password.split(":")[0]
432432
proxy_pass = username_and_password.split(":")[1]
433-
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
434-
time.sleep(0.25)
433+
if (
434+
hasattr(self.main_tab, "_last_auth")
435+
and self.main_tab._last_auth == username_and_password
436+
):
437+
pass # Auth was already set
438+
else:
439+
self.main_tab._last_auth = username_and_password
440+
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
441+
time.sleep(0.25)
435442
if "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
436443
username_and_password = kwargs["auth"]
437444
proxy_user = username_and_password.split(":")[0]
438445
proxy_pass = username_and_password.split(":")[1]
439-
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
440-
time.sleep(0.25)
446+
if (
447+
hasattr(self.main_tab, "_last_auth")
448+
and self.main_tab._last_auth == username_and_password
449+
):
450+
pass # Auth was already set
451+
else:
452+
self.main_tab._last_auth = username_and_password
453+
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
454+
time.sleep(0.25)
441455
await connection.sleep(0.15)
442456
frame_id, loader_id, *_ = await connection.send(
443457
cdp.page.navigate(url)

0 commit comments

Comments
 (0)