Skip to content

Commit c963804

Browse files
authored
Merge pull request #3536 from seleniumbase/cdp-mode-patch-34
CDP Mode - Patch 34
2 parents 9c1909e + 0627037 commit c963804

14 files changed

+362
-60
lines changed

examples/cdp_mode/ReadMe.md

+6
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ sb.cdp.uncheck_if_checked(selector)
465465
sb.cdp.unselect_if_selected(selector)
466466
sb.cdp.is_element_present(selector)
467467
sb.cdp.is_element_visible(selector)
468+
sb.cdp.is_text_visible(text, selector="body")
469+
sb.cdp.is_exact_text_visible(text, selector="body")
470+
sb.cdp.wait_for_text(text, selector="body", timeout=None)
471+
sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None)
468472
sb.cdp.wait_for_element_visible(selector, timeout=None)
469473
sb.cdp.assert_element(selector, timeout=None)
470474
sb.cdp.assert_element_visible(selector, timeout=None)
@@ -478,6 +482,7 @@ sb.cdp.assert_url(url)
478482
sb.cdp.assert_url_contains(substring)
479483
sb.cdp.assert_text(text, selector="html", timeout=None)
480484
sb.cdp.assert_exact_text(text, selector="html", timeout=None)
485+
sb.cdp.assert_text_not_visible(text, selector="body", timeout=None)
481486
sb.cdp.assert_true()
482487
sb.cdp.assert_false()
483488
sb.cdp.assert_equal(first, second)
@@ -506,6 +511,7 @@ element.highlight_overlay()
506511
element.mouse_click()
507512
element.mouse_drag(destination)
508513
element.mouse_move()
514+
element.press_keys(text)
509515
element.query_selector(selector)
510516
element.querySelector(selector)
511517
element.query_selector_all(selector)

examples/cdp_mode/raw_ahrefs.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True, incognito=True, locale_code="en") as sb:
4+
url = "https://ahrefs.com/website-authority-checker"
5+
input_field = 'input[placeholder="Enter domain"]'
6+
submit_button = 'span:contains("Check Authority")'
7+
sb.activate_cdp_mode(url) # The bot-check is later
8+
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
9+
sb.cdp.scroll_down(36)
10+
sb.click(submit_button)
11+
sb.uc_gui_click_captcha()
12+
sb.wait_for_text_not_visible("Checking", timeout=15)
13+
sb.click_if_visible('button[data-cky-tag="close-button"]')
14+
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
15+
sb.highlight('a:contains("Top 100 backlinks")')
16+
sb.set_messenger_theme(location="bottom_center")
17+
sb.post_message("SeleniumBase wasn't detected!")

examples/cdp_mode/raw_elal.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True, locale_code="en") as sb:
4+
url = "www.elal.com/flight-deals/en-us/flights-from-boston-to-tel-aviv"
5+
sb.activate_cdp_mode(url)
6+
sb.sleep(2)
7+
sb.cdp.click('button[data-att="search"]')
8+
sb.sleep(4)
9+
sb.cdp.click_if_visible("#onetrust-close-btn-container button")
10+
sb.sleep(0.5)
11+
view_other_dates = 'button[aria-label*="viewOtherDates.cta"]'
12+
if sb.cdp.is_element_visible(view_other_dates):
13+
sb.cdp.click(view_other_dates)
14+
sb.sleep(4.5)
15+
if sb.is_element_visible("flexible-search-calendar"):
16+
print("*** Flight Calendar for El Al (Boston to Tel Aviv): ***")
17+
print(sb.cdp.get_text("flexible-search-calendar"))
18+
prices = []
19+
elements = sb.cdp.find_elements("span.matric-cell__content__price")
20+
if elements:
21+
print("*** Prices List: ***")
22+
for element in elements:
23+
prices.append(element.text)
24+
for price in sorted(prices):
25+
print(price)
26+
print("*** Lowest Price: ***")
27+
lowest_price = sorted(prices)[0]
28+
print(lowest_price)
29+
sb.cdp.find_element_by_text(lowest_price).click()
30+
sb.sleep(1)
31+
search_cell = 'button[aria-label*="Search.cell.buttonTitle"]'
32+
sb.cdp.scroll_into_view(search_cell)
33+
sb.sleep(1)
34+
sb.cdp.click(search_cell)
35+
sb.sleep(5)
36+
else:
37+
elements = sb.cdp.find_elements("div.ui-bound__price__value")
38+
print("*** Lowest Prices: ***")
39+
first = True
40+
for element in elements:
41+
if "lowest price" in element.text:
42+
if first:
43+
print("Departure Flight:")
44+
print(element.text)
45+
first = False
46+
else:
47+
print("Return Flight:")
48+
print(element.text)
49+
break
50+
dates = sb.cdp.find_elements('div[class*="flight-date"]')
51+
if len(dates) == 2:
52+
print("*** Departure Date: ***")
53+
print(dates[0].text)
54+
print("*** Return Date: ***")
55+
print(dates[1].text)

examples/raw_ahrefs.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
sb.reconnect(0.1)
1010
sb.uc_click(submit_button, reconnect_time=3.25)
1111
sb.uc_gui_click_captcha()
12-
sb.wait_for_text_not_visible("Checking", timeout=11.5)
12+
sb.wait_for_text_not_visible("Checking", timeout=15)
1313
sb.click_if_visible('button[data-cky-tag="close-button"]')
1414
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
1515
sb.highlight('a:contains("Top 100 backlinks")')

requirements.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ attrs>=25.1.0
77
certifi>=2025.1.31
88
exceptiongroup>=1.2.2
99
websockets~=13.1;python_version<"3.9"
10-
websockets>=14.2;python_version>="3.9"
10+
websockets>=15.0;python_version>="3.9"
1111
filelock~=3.16.1;python_version<"3.9"
1212
filelock>=3.17.0;python_version>="3.9"
1313
fasteners>=0.19
@@ -38,8 +38,8 @@ sniffio==1.3.1
3838
h11==0.14.0
3939
outcome==1.3.0.post0
4040
trio==0.27.0;python_version<"3.9"
41-
trio==0.28.0;python_version>="3.9"
42-
trio-websocket==0.11.1
41+
trio==0.29.0;python_version>="3.9"
42+
trio-websocket==0.12.1
4343
wsproto==1.2.0
4444
websocket-client==1.8.0
4545
selenium==4.27.1;python_version<"3.9"
@@ -74,7 +74,7 @@ coverage>=7.6.12;python_version>="3.9"
7474
pytest-cov>=5.0.0;python_version<"3.9"
7575
pytest-cov>=6.0.0;python_version>="3.9"
7676
flake8==5.0.4;python_version<"3.9"
77-
flake8==7.1.1;python_version>="3.9"
77+
flake8==7.1.2;python_version>="3.9"
7878
mccabe==0.7.0
7979
pyflakes==2.5.0;python_version<"3.9"
8080
pyflakes==3.2.0;python_version>="3.9"

seleniumbase/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.34.15"
2+
__version__ = "4.34.16"

seleniumbase/core/browser_launcher.py

+7
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,10 @@ def uc_open_with_cdp_mode(driver, url=None):
718718
cdp.is_selected = CDPM.is_selected
719719
cdp.is_element_present = CDPM.is_element_present
720720
cdp.is_element_visible = CDPM.is_element_visible
721+
cdp.is_text_visible = CDPM.is_text_visible
722+
cdp.is_exact_text_visible = CDPM.is_exact_text_visible
723+
cdp.wait_for_text = CDPM.wait_for_text
724+
cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
721725
cdp.wait_for_element_visible = CDPM.wait_for_element_visible
722726
cdp.assert_element = CDPM.assert_element
723727
cdp.assert_element_visible = CDPM.assert_element_visible
@@ -731,6 +735,7 @@ def uc_open_with_cdp_mode(driver, url=None):
731735
cdp.assert_url_contains = CDPM.assert_url_contains
732736
cdp.assert_text = CDPM.assert_text
733737
cdp.assert_exact_text = CDPM.assert_exact_text
738+
cdp.assert_text_not_visible = CDPM.assert_text_not_visible
734739
cdp.assert_true = CDPM.assert_true
735740
cdp.assert_false = CDPM.assert_false
736741
cdp.assert_equal = CDPM.assert_equal
@@ -2280,6 +2285,7 @@ def _set_chrome_options(
22802285
or proxy_string
22812286
):
22822287
chrome_options.add_argument("--ignore-certificate-errors")
2288+
chrome_options.add_argument("--ignore-ssl-errors=yes")
22832289
if not enable_ws:
22842290
chrome_options.add_argument("--disable-web-security")
22852291
if (
@@ -4231,6 +4237,7 @@ def get_local_driver(
42314237
edge_options.add_argument("--log-level=3")
42324238
edge_options.add_argument("--no-first-run")
42334239
edge_options.add_argument("--ignore-certificate-errors")
4240+
edge_options.add_argument("--ignore-ssl-errors=yes")
42344241
if devtools and not headless:
42354242
edge_options.add_argument("--auto-open-devtools-for-tabs")
42364243
edge_options.add_argument("--allow-file-access-from-files")

seleniumbase/core/sb_cdp.py

+105-35
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def __add_sync_methods(self, element):
6262
lambda destination: self.__mouse_drag(element, destination)
6363
)
6464
element.mouse_move = lambda: self.__mouse_move(element)
65+
element.press_keys = lambda text: self.__press_keys(element, text)
6566
element.query_selector = (
6667
lambda selector: self.__query_selector(element, selector)
6768
)
@@ -211,7 +212,8 @@ def find_element_by_text(self, text, tag_name=None, timeout=None):
211212
element = self.__add_sync_methods(element.parent)
212213
return self.__add_sync_methods(element)
213214
elif (
214-
element.parent.parent
215+
element.parent
216+
and element.parent.parent
215217
and tag_name in element.parent.parent.tag_name.lower()
216218
and text.strip() in element.parent.parent.text
217219
):
@@ -272,7 +274,8 @@ def find_elements_by_text(self, text, tag_name=None):
272274
if element not in updated_elements:
273275
updated_elements.append(element)
274276
elif (
275-
element.parent.parent
277+
element.parent
278+
and element.parent.parent
276279
and tag_name in element.parent.parent.tag_name.lower()
277280
and text.strip() in element.parent.parent.text
278281
):
@@ -445,6 +448,23 @@ def __mouse_move(self, element):
445448
self.loop.run_until_complete(element.mouse_move_async())
446449
)
447450

451+
def __press_keys(self, element, text):
452+
element.scroll_into_view()
453+
submit = False
454+
if text.endswith("\n") or text.endswith("\r"):
455+
submit = True
456+
text = text[:-1]
457+
for key in text:
458+
element.send_keys(key)
459+
time.sleep(0.044)
460+
if submit:
461+
element.send_keys("\r\n")
462+
time.sleep(0.044)
463+
self.__slow_mode_pause_if_set()
464+
return (
465+
self.loop.run_until_complete(self.page.wait())
466+
)
467+
448468
def __query_selector(self, element, selector):
449469
selector = self.__convert_to_css_if_xpath(selector)
450470
element2 = self.loop.run_until_complete(
@@ -1681,21 +1701,79 @@ def is_element_visible(self, selector):
16811701
return True
16821702
return False
16831703

1684-
def wait_for_element_visible(self, selector, timeout=None):
1704+
def is_text_visible(self, text, selector="body"):
1705+
selector = self.__convert_to_css_if_xpath(selector)
1706+
text = text.strip()
1707+
element = None
1708+
try:
1709+
element = self.find_element(selector, timeout=0.1)
1710+
except Exception:
1711+
return False
1712+
with suppress(Exception):
1713+
if text in element.text_all:
1714+
return True
1715+
return False
1716+
1717+
def is_exact_text_visible(self, text, selector="body"):
1718+
selector = self.__convert_to_css_if_xpath(selector)
1719+
text = text.strip()
1720+
element = None
1721+
try:
1722+
element = self.find_element(selector, timeout=0.1)
1723+
except Exception:
1724+
return False
1725+
with suppress(Exception):
1726+
if text == element.text_all.strip():
1727+
return True
1728+
return False
1729+
1730+
def wait_for_text(self, text, selector="body", timeout=None):
16851731
if not timeout:
16861732
timeout = settings.SMALL_TIMEOUT
1733+
start_ms = time.time() * 1000.0
1734+
stop_ms = start_ms + (timeout * 1000.0)
1735+
text = text.strip()
1736+
element = None
16871737
try:
1688-
self.select(selector, timeout=timeout)
1738+
element = self.find_element(selector, timeout=timeout)
16891739
except Exception:
1690-
raise Exception("Element {%s} was not found!" % selector)
1691-
for i in range(30):
1692-
if self.is_element_visible(selector):
1693-
return self.select(selector)
1740+
raise Exception("Element {%s} not found!" % selector)
1741+
for i in range(int(timeout * 10)):
1742+
with suppress(Exception):
1743+
element = self.find_element(selector, timeout=0.1)
1744+
if text in element.text_all:
1745+
return True
1746+
now_ms = time.time() * 1000.0
1747+
if now_ms >= stop_ms:
1748+
break
16941749
time.sleep(0.1)
1695-
raise Exception("Element {%s} was not visible!" % selector)
1750+
raise Exception(
1751+
"Text {%s} not found in {%s}! Actual text: {%s}"
1752+
% (text, selector, element.text_all)
1753+
)
16961754

1697-
def assert_element(self, selector, timeout=None):
1698-
"""Same as assert_element_visible()"""
1755+
def wait_for_text_not_visible(self, text, selector="body", timeout=None):
1756+
if not timeout:
1757+
timeout = settings.SMALL_TIMEOUT
1758+
text = text.strip()
1759+
start_ms = time.time() * 1000.0
1760+
stop_ms = start_ms + (timeout * 1000.0)
1761+
for i in range(int(timeout * 10)):
1762+
if not self.is_text_visible(text, selector):
1763+
return True
1764+
now_ms = time.time() * 1000.0
1765+
if now_ms >= stop_ms:
1766+
break
1767+
time.sleep(0.1)
1768+
plural = "s"
1769+
if timeout == 1:
1770+
plural = ""
1771+
raise Exception(
1772+
"Text {%s} in {%s} was still visible after %s second%s!"
1773+
% (text, selector, timeout, plural)
1774+
)
1775+
1776+
def wait_for_element_visible(self, selector, timeout=None):
16991777
if not timeout:
17001778
timeout = settings.SMALL_TIMEOUT
17011779
try:
@@ -1704,10 +1782,15 @@ def assert_element(self, selector, timeout=None):
17041782
raise Exception("Element {%s} was not found!" % selector)
17051783
for i in range(30):
17061784
if self.is_element_visible(selector):
1707-
return True
1785+
return self.select(selector)
17081786
time.sleep(0.1)
17091787
raise Exception("Element {%s} was not visible!" % selector)
17101788

1789+
def assert_element(self, selector, timeout=None):
1790+
"""Same as assert_element_visible()"""
1791+
self.assert_element_visible(selector, timeout=timeout)
1792+
return True
1793+
17111794
def assert_element_visible(self, selector, timeout=None):
17121795
"""Same as assert_element()"""
17131796
if not timeout:
@@ -1852,29 +1935,9 @@ def assert_url_contains(self, substring):
18521935
raise Exception(error % (expected, actual))
18531936

18541937
def assert_text(self, text, selector="body", timeout=None):
1855-
if not timeout:
1856-
timeout = settings.SMALL_TIMEOUT
1857-
start_ms = time.time() * 1000.0
1858-
stop_ms = start_ms + (timeout * 1000.0)
1859-
text = text.strip()
1860-
element = None
1861-
try:
1862-
element = self.find_element(selector, timeout=timeout)
1863-
except Exception:
1864-
raise Exception("Element {%s} not found!" % selector)
1865-
for i in range(int(timeout * 10)):
1866-
with suppress(Exception):
1867-
element = self.find_element(selector, timeout=0.1)
1868-
if text in element.text_all:
1869-
return True
1870-
now_ms = time.time() * 1000.0
1871-
if now_ms >= stop_ms:
1872-
break
1873-
time.sleep(0.1)
1874-
raise Exception(
1875-
"Text {%s} not found in {%s}! Actual text: {%s}"
1876-
% (text, selector, element.text_all)
1877-
)
1938+
"""Same as wait_for_text()"""
1939+
self.wait_for_text(text, selector=selector, timeout=timeout)
1940+
return True
18781941

18791942
def assert_exact_text(self, text, selector="body", timeout=None):
18801943
if not timeout:
@@ -1904,6 +1967,13 @@ def assert_exact_text(self, text, selector="body", timeout=None):
19041967
% (text, element.text_all, selector)
19051968
)
19061969

1970+
def assert_text_not_visible(self, text, selector="body", timeout=None):
1971+
"""Raises an exception if the text is still visible after timeout."""
1972+
self.wait_for_text_not_visible(
1973+
text, selector=selector, timeout=timeout
1974+
)
1975+
return True
1976+
19071977
def assert_true(self, expression):
19081978
if not expression:
19091979
raise AssertionError("%s is not true" % expression)

0 commit comments

Comments
 (0)