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
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ iniconfig==2.3.0;python_version>="3.10"
pluggy==1.5.0;python_version<"3.9"
pluggy==1.6.0;python_version>="3.9"
pytest==8.3.5;python_version<"3.9"
pytest==8.4.2;python_version>="3.9"
pytest==8.4.2;python_version>="3.9" and python_version<"3.11"
pytest==9.0.1;python_version>="3.11"
pytest-html==4.0.2
pytest-metadata==3.1.1
pytest-ordering==0.6
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.44.11"
__version__ = "4.44.12"
12 changes: 12 additions & 0 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,21 @@ def extend_driver(
# Extend the driver with new methods
driver.default_find_element = driver.find_element
driver.default_find_elements = driver.find_elements
driver.default_add_cookie = driver.add_cookie
driver.default_get_cookie = driver.get_cookie
driver.default_delete_cookie = driver.delete_cookie
driver.default_back = driver.back
driver.default_forward = driver.forward
driver.default_refresh = driver.refresh
DM = sb_driver.DriverMethods(driver)
driver.find_element = DM.find_element
driver.find_elements = DM.find_elements
driver.add_cookie = DM.add_cookie
driver.get_cookie = DM.get_cookie
driver.delete_cookie = DM.delete_cookie
driver.back = DM.back
driver.forward = DM.forward
driver.refresh = DM.refresh
driver.locator = DM.locator
page = types.SimpleNamespace()
page.open = DM.open_url
Expand Down
4 changes: 2 additions & 2 deletions seleniumbase/core/log_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
and test._outcome.errors
):
try:
exc_message = test._outcome.errors[0][1][1]
traceback_address = test._outcome.errors[0][1][2]
exc_message = test._outcome.errors[-1][1][1]
traceback_address = test._outcome.errors[-1][1][2]
traceback_list = traceback.format_list(
traceback.extract_tb(traceback_address)[1:]
)
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/core/report_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def process_failures(test, test_count, duration):
and test._outcome.errors
):
try:
exc_message = test._outcome.errors[0][1][1]
exc_message = test._outcome.errors[-1][1][1]
except Exception:
exc_message = "(Unknown Exception)"
else:
Expand Down
5 changes: 4 additions & 1 deletion seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,10 @@ def click(self, selector, timeout=None):
tag_name in ["a", "button", "canvas", "div", "input", "li", "span"]
and "contains(" not in selector
):
element.mouse_click() # Simulated click (NOT PyAutoGUI)
try:
element.mouse_click() # Simulated click (NOT PyAutoGUI)
except Exception:
element.click() # Standard CDP click
else:
element.click() # Standard CDP click
self.__slow_mode_pause_if_set()
Expand Down
34 changes: 34 additions & 0 deletions seleniumbase/core/sb_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
class DriverMethods(WebDriver):
def __init__(self, driver):
self.driver = driver
if hasattr(driver, "session_id"):
self.session_id = driver.session_id
if hasattr(driver, "command_executor"):
self.command_executor = driver.command_executor

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

def add_cookie(self, *args, **kwargs):
page_actions._reconnect_if_disconnected(self.driver)
self.driver.default_add_cookie(*args, **kwargs)

def get_cookie(self, *args, **kwargs):
page_actions._reconnect_if_disconnected(self.driver)
self.driver.default_get_cookie(*args, **kwargs)

def delete_cookie(self, *args, **kwargs):
page_actions._reconnect_if_disconnected(self.driver)
self.driver.default_delete_cookie(*args, **kwargs)

def back(self):
if self.__is_cdp_swap_needed():
self.driver.cdp.go_back()
return
self.driver.default_back()

def forward(self):
if self.__is_cdp_swap_needed():
self.driver.cdp.go_forward()
return
self.driver.default_forward()

def refresh(self, *args, **kwargs):
if self.__is_cdp_swap_needed():
self.driver.cdp.refresh(*args, **kwargs)
return
self.driver.default_refresh()

def locator(self, selector, by=None):
if not by:
by = "css selector"
Expand Down
55 changes: 44 additions & 11 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -3969,8 +3969,23 @@ def set_content_to_parent_frame(self):

def open_new_window(self, switch_to=True):
"""Opens a new browser tab/window and switches to it by default."""
url = None
if self.__looks_like_a_page_url(str(switch_to)):
# Different API for CDP Mode: First arg is a `url`.
# (Also, don't break backwards compat for reg mode)
url = switch_to
switch_to = True
if self.__is_cdp_swap_needed():
self.cdp.open_new_tab(switch_to=switch_to)
self.cdp.open_new_tab(url=url, switch_to=switch_to)
return
elif (
hasattr(self.driver, "_is_using_uc")
and self.driver._is_using_uc
and hasattr(self.driver, "_is_using_cdp")
and self.driver._is_using_cdp
):
self.disconnect()
self.cdp.open_new_tab(url=url, switch_to=switch_to)
return
self.wait_for_ready_state_complete()
if switch_to:
Expand Down Expand Up @@ -5040,9 +5055,8 @@ def activate_cdp_mode(self, url=None, **kwargs):
if hasattr(self.cdp, "find_element_by_text"):
self.find_element_by_text = self.cdp.find_element_by_text
if (
hasattr(sb_config, "_cdp_proxy")
and sb_config._cdp_proxy
and "@" in sb_config._cdp_proxy
hasattr(self.driver, "_is_using_auth")
and self.driver._is_using_auth
):
with suppress(Exception):
self.cdp.loop.run_until_complete(self.cdp.page.wait(0.25))
Expand Down Expand Up @@ -6364,6 +6378,7 @@ def press_up_arrow(self, selector="body", times=1, by="css selector"):
By default, "html" will be used as the CSS Selector target.
You can specify how many times in-a-row the action happens."""
self.__check_scope()
self._check_browser()
if times < 1:
return
element = self.wait_for_element_present(selector)
Expand All @@ -6386,6 +6401,7 @@ def press_down_arrow(self, selector="body", times=1, by="css selector"):
By default, "html" will be used as the CSS Selector target.
You can specify how many times in-a-row the action happens."""
self.__check_scope()
self._check_browser()
if times < 1:
return
element = self.wait_for_element_present(selector)
Expand All @@ -6408,6 +6424,7 @@ def press_left_arrow(self, selector="body", times=1, by="css selector"):
By default, "html" will be used as the CSS Selector target.
You can specify how many times in-a-row the action happens."""
self.__check_scope()
self._check_browser()
if times < 1:
return
element = self.wait_for_element_present(selector)
Expand All @@ -6430,6 +6447,7 @@ def press_right_arrow(self, selector="body", times=1, by="css selector"):
By default, "html" will be used as the CSS Selector target.
You can specify how many times in-a-row the action happens."""
self.__check_scope()
self._check_browser()
if times < 1:
return
element = self.wait_for_element_present(selector)
Expand Down Expand Up @@ -8781,6 +8799,9 @@ def set_text(self, selector, text, by="css selector", timeout=None):
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
selector, by = self.__recalculate_selector(selector, by)
if self.__is_cdp_swap_needed():
self.cdp.set_value(selector, text)
return
self.wait_for_ready_state_complete()
element = page_actions.wait_for_element_present(
self.driver, selector, by, timeout
Expand All @@ -8801,10 +8822,14 @@ def set_text_content(
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
selector, by = self.__recalculate_selector(selector, by)
self.wait_for_ready_state_complete()
element = page_actions.wait_for_element_present(
self.driver, selector, by, timeout
)
element = None
if self.__is_cdp_swap_needed():
element = self.cdp.select(selector, timeout=timeout)
else:
self.wait_for_ready_state_complete()
element = page_actions.wait_for_element_present(
self.driver, selector, by, timeout
)
if element.tag_name.lower() in ["input", "textarea"]:
self.js_update_text(selector, text, by=by, timeout=timeout)
return
Expand Down Expand Up @@ -15945,7 +15970,7 @@ def __get_exception_info(self):
and self._outcome.errors
):
try:
exc_message = self._outcome.errors[0][1][1]
exc_message = self._outcome.errors[-1][1][1]
except Exception:
exc_message = "(Unknown Exception)"
else:
Expand Down Expand Up @@ -16093,8 +16118,16 @@ def __has_exception(self):
else:
return False
elif hasattr(self, "_outcome") and hasattr(self._outcome, "errors"):
if self._outcome.errors:
has_exception = True
if python3_11_or_newer:
if (
self._outcome.errors
and self._outcome.errors[-1]
and self._outcome.errors[-1][1]
):
has_exception = True
else:
if self._outcome.errors:
has_exception = True
else:
has_exception = sys.exc_info()[1] is not None
if self.__will_be_skipped and hasattr(self, "_using_sb_fixture"):
Expand Down
18 changes: 16 additions & 2 deletions seleniumbase/masterqa/master_qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
from seleniumbase.fixtures import js_utils


python3_11_or_newer = False
if sys.version_info >= (3, 11):
python3_11_or_newer = True


class MasterQA(BaseCase):
def setUp(self):
self.check_count = 0
Expand Down Expand Up @@ -309,8 +314,17 @@ def __has_exception(self):
if hasattr(sys, "last_traceback") and sys.last_traceback is not None:
has_exception = True
elif hasattr(self, "_outcome"):
if hasattr(self._outcome, "errors") and self._outcome.errors:
has_exception = True
if hasattr(self._outcome, "errors"):
if python3_11_or_newer:
if (
self._outcome.errors
and self._outcome.errors[-1]
and self._outcome.errors[-1][1]
):
has_exception = True
else:
if self._outcome.errors:
has_exception = True
else:
has_exception = sys.exc_info()[1] is not None
return has_exception
Expand Down
22 changes: 18 additions & 4 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,28 @@ async def get(
username_and_password = sb_config._cdp_proxy.split("@")[0]
proxy_user = username_and_password.split(":")[0]
proxy_pass = username_and_password.split(":")[1]
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
time.sleep(0.25)
if (
hasattr(self.main_tab, "_last_auth")
and self.main_tab._last_auth == username_and_password
):
pass # Auth was already set
else:
self.main_tab._last_auth = username_and_password
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
time.sleep(0.25)
if "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
username_and_password = kwargs["auth"]
proxy_user = username_and_password.split(":")[0]
proxy_pass = username_and_password.split(":")[1]
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
time.sleep(0.25)
if (
hasattr(self.main_tab, "_last_auth")
and self.main_tab._last_auth == username_and_password
):
pass # Auth was already set
else:
self.main_tab._last_auth = username_and_password
await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
time.sleep(0.25)
await connection.sleep(0.15)
frame_id, loader_id, *_ = await connection.send(
cdp.page.navigate(url)
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@
'pluggy==1.5.0;python_version<"3.9"',
'pluggy==1.6.0;python_version>="3.9"',
'pytest==8.3.5;python_version<"3.9"',
'pytest==8.4.2;python_version>="3.9"',
'pytest==8.4.2;python_version>="3.9" and python_version<"3.11"',
'pytest==9.0.1;python_version>="3.11"',
"pytest-html==4.0.2", # Newer ones had issues
'pytest-metadata==3.1.1',
"pytest-ordering==0.6",
Expand Down