Skip to content

CDP Mode - Patch 34 #3536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 19, 2025
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
6 changes: 6 additions & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ sb.cdp.uncheck_if_checked(selector)
sb.cdp.unselect_if_selected(selector)
sb.cdp.is_element_present(selector)
sb.cdp.is_element_visible(selector)
sb.cdp.is_text_visible(text, selector="body")
sb.cdp.is_exact_text_visible(text, selector="body")
sb.cdp.wait_for_text(text, selector="body", timeout=None)
sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None)
sb.cdp.wait_for_element_visible(selector, timeout=None)
sb.cdp.assert_element(selector, timeout=None)
sb.cdp.assert_element_visible(selector, timeout=None)
Expand All @@ -478,6 +482,7 @@ sb.cdp.assert_url(url)
sb.cdp.assert_url_contains(substring)
sb.cdp.assert_text(text, selector="html", timeout=None)
sb.cdp.assert_exact_text(text, selector="html", timeout=None)
sb.cdp.assert_text_not_visible(text, selector="body", timeout=None)
sb.cdp.assert_true()
sb.cdp.assert_false()
sb.cdp.assert_equal(first, second)
Expand Down Expand Up @@ -506,6 +511,7 @@ element.highlight_overlay()
element.mouse_click()
element.mouse_drag(destination)
element.mouse_move()
element.press_keys(text)
element.query_selector(selector)
element.querySelector(selector)
element.query_selector_all(selector)
Expand Down
17 changes: 17 additions & 0 deletions examples/cdp_mode/raw_ahrefs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from seleniumbase import SB

with SB(uc=True, test=True, incognito=True, locale_code="en") as sb:
url = "https://ahrefs.com/website-authority-checker"
input_field = 'input[placeholder="Enter domain"]'
submit_button = 'span:contains("Check Authority")'
sb.activate_cdp_mode(url) # The bot-check is later
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
sb.cdp.scroll_down(36)
sb.click(submit_button)
sb.uc_gui_click_captcha()
sb.wait_for_text_not_visible("Checking", timeout=15)
sb.click_if_visible('button[data-cky-tag="close-button"]')
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
sb.highlight('a:contains("Top 100 backlinks")')
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
55 changes: 55 additions & 0 deletions examples/cdp_mode/raw_elal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from seleniumbase import SB

with SB(uc=True, test=True, locale_code="en") as sb:
url = "www.elal.com/flight-deals/en-us/flights-from-boston-to-tel-aviv"
sb.activate_cdp_mode(url)
sb.sleep(2)
sb.cdp.click('button[data-att="search"]')
sb.sleep(4)
sb.cdp.click_if_visible("#onetrust-close-btn-container button")
sb.sleep(0.5)
view_other_dates = 'button[aria-label*="viewOtherDates.cta"]'
if sb.cdp.is_element_visible(view_other_dates):
sb.cdp.click(view_other_dates)
sb.sleep(4.5)
if sb.is_element_visible("flexible-search-calendar"):
print("*** Flight Calendar for El Al (Boston to Tel Aviv): ***")
print(sb.cdp.get_text("flexible-search-calendar"))
prices = []
elements = sb.cdp.find_elements("span.matric-cell__content__price")
if elements:
print("*** Prices List: ***")
for element in elements:
prices.append(element.text)
for price in sorted(prices):
print(price)
print("*** Lowest Price: ***")
lowest_price = sorted(prices)[0]
print(lowest_price)
sb.cdp.find_element_by_text(lowest_price).click()
sb.sleep(1)
search_cell = 'button[aria-label*="Search.cell.buttonTitle"]'
sb.cdp.scroll_into_view(search_cell)
sb.sleep(1)
sb.cdp.click(search_cell)
sb.sleep(5)
else:
elements = sb.cdp.find_elements("div.ui-bound__price__value")
print("*** Lowest Prices: ***")
first = True
for element in elements:
if "lowest price" in element.text:
if first:
print("Departure Flight:")
print(element.text)
first = False
else:
print("Return Flight:")
print(element.text)
break
dates = sb.cdp.find_elements('div[class*="flight-date"]')
if len(dates) == 2:
print("*** Departure Date: ***")
print(dates[0].text)
print("*** Return Date: ***")
print(dates[1].text)
2 changes: 1 addition & 1 deletion examples/raw_ahrefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
sb.reconnect(0.1)
sb.uc_click(submit_button, reconnect_time=3.25)
sb.uc_gui_click_captcha()
sb.wait_for_text_not_visible("Checking", timeout=11.5)
sb.wait_for_text_not_visible("Checking", timeout=15)
sb.click_if_visible('button[data-cky-tag="close-button"]')
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
sb.highlight('a:contains("Top 100 backlinks")')
Expand Down
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ attrs>=25.1.0
certifi>=2025.1.31
exceptiongroup>=1.2.2
websockets~=13.1;python_version<"3.9"
websockets>=14.2;python_version>="3.9"
websockets>=15.0;python_version>="3.9"
filelock~=3.16.1;python_version<"3.9"
filelock>=3.17.0;python_version>="3.9"
fasteners>=0.19
Expand Down Expand Up @@ -38,8 +38,8 @@ sniffio==1.3.1
h11==0.14.0
outcome==1.3.0.post0
trio==0.27.0;python_version<"3.9"
trio==0.28.0;python_version>="3.9"
trio-websocket==0.11.1
trio==0.29.0;python_version>="3.9"
trio-websocket==0.12.1
wsproto==1.2.0
websocket-client==1.8.0
selenium==4.27.1;python_version<"3.9"
Expand Down Expand Up @@ -74,7 +74,7 @@ coverage>=7.6.12;python_version>="3.9"
pytest-cov>=5.0.0;python_version<"3.9"
pytest-cov>=6.0.0;python_version>="3.9"
flake8==5.0.4;python_version<"3.9"
flake8==7.1.1;python_version>="3.9"
flake8==7.1.2;python_version>="3.9"
mccabe==0.7.0
pyflakes==2.5.0;python_version<"3.9"
pyflakes==3.2.0;python_version>="3.9"
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.34.15"
__version__ = "4.34.16"
7 changes: 7 additions & 0 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,10 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.is_selected = CDPM.is_selected
cdp.is_element_present = CDPM.is_element_present
cdp.is_element_visible = CDPM.is_element_visible
cdp.is_text_visible = CDPM.is_text_visible
cdp.is_exact_text_visible = CDPM.is_exact_text_visible
cdp.wait_for_text = CDPM.wait_for_text
cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
cdp.wait_for_element_visible = CDPM.wait_for_element_visible
cdp.assert_element = CDPM.assert_element
cdp.assert_element_visible = CDPM.assert_element_visible
Expand All @@ -731,6 +735,7 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.assert_url_contains = CDPM.assert_url_contains
cdp.assert_text = CDPM.assert_text
cdp.assert_exact_text = CDPM.assert_exact_text
cdp.assert_text_not_visible = CDPM.assert_text_not_visible
cdp.assert_true = CDPM.assert_true
cdp.assert_false = CDPM.assert_false
cdp.assert_equal = CDPM.assert_equal
Expand Down Expand Up @@ -2280,6 +2285,7 @@ def _set_chrome_options(
or proxy_string
):
chrome_options.add_argument("--ignore-certificate-errors")
chrome_options.add_argument("--ignore-ssl-errors=yes")
if not enable_ws:
chrome_options.add_argument("--disable-web-security")
if (
Expand Down Expand Up @@ -4231,6 +4237,7 @@ def get_local_driver(
edge_options.add_argument("--log-level=3")
edge_options.add_argument("--no-first-run")
edge_options.add_argument("--ignore-certificate-errors")
edge_options.add_argument("--ignore-ssl-errors=yes")
if devtools and not headless:
edge_options.add_argument("--auto-open-devtools-for-tabs")
edge_options.add_argument("--allow-file-access-from-files")
Expand Down
140 changes: 105 additions & 35 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __add_sync_methods(self, element):
lambda destination: self.__mouse_drag(element, destination)
)
element.mouse_move = lambda: self.__mouse_move(element)
element.press_keys = lambda text: self.__press_keys(element, text)
element.query_selector = (
lambda selector: self.__query_selector(element, selector)
)
Expand Down Expand Up @@ -211,7 +212,8 @@ def find_element_by_text(self, text, tag_name=None, timeout=None):
element = self.__add_sync_methods(element.parent)
return self.__add_sync_methods(element)
elif (
element.parent.parent
element.parent
and element.parent.parent
and tag_name in element.parent.parent.tag_name.lower()
and text.strip() in element.parent.parent.text
):
Expand Down Expand Up @@ -272,7 +274,8 @@ def find_elements_by_text(self, text, tag_name=None):
if element not in updated_elements:
updated_elements.append(element)
elif (
element.parent.parent
element.parent
and element.parent.parent
and tag_name in element.parent.parent.tag_name.lower()
and text.strip() in element.parent.parent.text
):
Expand Down Expand Up @@ -445,6 +448,23 @@ def __mouse_move(self, element):
self.loop.run_until_complete(element.mouse_move_async())
)

def __press_keys(self, element, text):
element.scroll_into_view()
submit = False
if text.endswith("\n") or text.endswith("\r"):
submit = True
text = text[:-1]
for key in text:
element.send_keys(key)
time.sleep(0.044)
if submit:
element.send_keys("\r\n")
time.sleep(0.044)
self.__slow_mode_pause_if_set()
return (
self.loop.run_until_complete(self.page.wait())
)

def __query_selector(self, element, selector):
selector = self.__convert_to_css_if_xpath(selector)
element2 = self.loop.run_until_complete(
Expand Down Expand Up @@ -1681,21 +1701,79 @@ def is_element_visible(self, selector):
return True
return False

def wait_for_element_visible(self, selector, timeout=None):
def is_text_visible(self, text, selector="body"):
selector = self.__convert_to_css_if_xpath(selector)
text = text.strip()
element = None
try:
element = self.find_element(selector, timeout=0.1)
except Exception:
return False
with suppress(Exception):
if text in element.text_all:
return True
return False

def is_exact_text_visible(self, text, selector="body"):
selector = self.__convert_to_css_if_xpath(selector)
text = text.strip()
element = None
try:
element = self.find_element(selector, timeout=0.1)
except Exception:
return False
with suppress(Exception):
if text == element.text_all.strip():
return True
return False

def wait_for_text(self, text, selector="body", timeout=None):
if not timeout:
timeout = settings.SMALL_TIMEOUT
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
text = text.strip()
element = None
try:
self.select(selector, timeout=timeout)
element = self.find_element(selector, timeout=timeout)
except Exception:
raise Exception("Element {%s} was not found!" % selector)
for i in range(30):
if self.is_element_visible(selector):
return self.select(selector)
raise Exception("Element {%s} not found!" % selector)
for i in range(int(timeout * 10)):
with suppress(Exception):
element = self.find_element(selector, timeout=0.1)
if text in element.text_all:
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception("Element {%s} was not visible!" % selector)
raise Exception(
"Text {%s} not found in {%s}! Actual text: {%s}"
% (text, selector, element.text_all)
)

def assert_element(self, selector, timeout=None):
"""Same as assert_element_visible()"""
def wait_for_text_not_visible(self, text, selector="body", timeout=None):
if not timeout:
timeout = settings.SMALL_TIMEOUT
text = text.strip()
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for i in range(int(timeout * 10)):
if not self.is_text_visible(text, selector):
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
plural = "s"
if timeout == 1:
plural = ""
raise Exception(
"Text {%s} in {%s} was still visible after %s second%s!"
% (text, selector, timeout, plural)
)

def wait_for_element_visible(self, selector, timeout=None):
if not timeout:
timeout = settings.SMALL_TIMEOUT
try:
Expand All @@ -1704,10 +1782,15 @@ def assert_element(self, selector, timeout=None):
raise Exception("Element {%s} was not found!" % selector)
for i in range(30):
if self.is_element_visible(selector):
return True
return self.select(selector)
time.sleep(0.1)
raise Exception("Element {%s} was not visible!" % selector)

def assert_element(self, selector, timeout=None):
"""Same as assert_element_visible()"""
self.assert_element_visible(selector, timeout=timeout)
return True

def assert_element_visible(self, selector, timeout=None):
"""Same as assert_element()"""
if not timeout:
Expand Down Expand Up @@ -1852,29 +1935,9 @@ def assert_url_contains(self, substring):
raise Exception(error % (expected, actual))

def assert_text(self, text, selector="body", timeout=None):
if not timeout:
timeout = settings.SMALL_TIMEOUT
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
text = text.strip()
element = None
try:
element = self.find_element(selector, timeout=timeout)
except Exception:
raise Exception("Element {%s} not found!" % selector)
for i in range(int(timeout * 10)):
with suppress(Exception):
element = self.find_element(selector, timeout=0.1)
if text in element.text_all:
return True
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception(
"Text {%s} not found in {%s}! Actual text: {%s}"
% (text, selector, element.text_all)
)
"""Same as wait_for_text()"""
self.wait_for_text(text, selector=selector, timeout=timeout)
return True

def assert_exact_text(self, text, selector="body", timeout=None):
if not timeout:
Expand Down Expand Up @@ -1904,6 +1967,13 @@ def assert_exact_text(self, text, selector="body", timeout=None):
% (text, element.text_all, selector)
)

def assert_text_not_visible(self, text, selector="body", timeout=None):
"""Raises an exception if the text is still visible after timeout."""
self.wait_for_text_not_visible(
text, selector=selector, timeout=timeout
)
return True

def assert_true(self, expression):
if not expression:
raise AssertionError("%s is not true" % expression)
Expand Down
Loading