Skip to content
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

CDP Mode: Patch 39 #3581

Merged
merged 4 commits into from
Mar 5, 2025
Merged
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
7 changes: 7 additions & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
@@ -420,6 +420,13 @@ sb.cdp.minimize()
sb.cdp.medimize()
sb.cdp.set_window_rect()
sb.cdp.reset_window_size()
sb.cdp.switch_to_window(window)
sb.cdp.switch_to_newest_window()
sb.cdp.switch_to_tab(tab)
sb.cdp.switch_to_newest_tab()
sb.cdp.close_active_tab()
sb.cdp.get_active_tab()
sb.cdp.get_tabs()
sb.cdp.get_window()
sb.cdp.get_text(selector)
sb.cdp.get_title()
3 changes: 2 additions & 1 deletion examples/cdp_mode/raw_async.py
Original file line number Diff line number Diff line change
@@ -51,13 +51,14 @@ async def main():
location = "Amsterdam"
where_to = 'div[data-automation*="experiences"] input'
button = 'button[data-automation*="experiences-search"]'
sb.wait_for_text("Where to?")
sb.gui_click_element(where_to)
sb.press_keys(where_to, location)
sb.sleep(1)
sb.gui_click_element(button)
sb.sleep(3)
print(sb.get_title())
print("************")
cards = sb.select_all('h2[data-automation*="product-list-card"]')
cards = sb.select_all('span[data-automation*="product-list-card"]')
for card in cards:
print("* %s" % card.text)
3 changes: 2 additions & 1 deletion examples/cdp_mode/raw_cdp.py
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ def main():
location = "Amsterdam"
where_to = 'div[data-automation*="experiences"] input'
button = 'button[data-automation*="experiences-search"]'
sb.wait_for_text("Where to?")
sb.gui_click_element(where_to)
sb.press_keys(where_to, location)
sb.sleep(1)
@@ -26,7 +27,7 @@ def main():
for i in range(8):
sb.scroll_down(50)
sb.sleep(0.2)
cards = sb.select_all('h2[data-automation*="product-list-card"]')
cards = sb.select_all('span[data-automation*="product-list-card"]')
for card in cards:
print("* %s" % card.text)

3 changes: 2 additions & 1 deletion examples/cdp_mode/raw_cdp_with_sb.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
location = "Amsterdam"
where_to = 'div[data-automation*="experiences"] input'
button = 'button[data-automation*="experiences-search"]'
sb.wait_for_text("Where to?")
sb.cdp.gui_click_element(where_to)
sb.press_keys(where_to, location)
sb.sleep(1)
@@ -24,6 +25,6 @@
for i in range(8):
sb.cdp.scroll_down(50)
sb.sleep(0.2)
cards = sb.select_all('h2[data-automation*="product-list-card"]')
cards = sb.select_all('span[data-automation*="product-list-card"]')
for card in cards:
print("* %s" % card.text)
38 changes: 38 additions & 0 deletions examples/cdp_mode/raw_cookies_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""A script that loads cookies to bypass login."""
import asyncio
import time
from seleniumbase import cdp_driver


# Log in to Swag Labs and save cookies
async def get_login_cookies():
url = "https://www.saucedemo.com"
driver = await cdp_driver.start_async(incognito=True)
page = await driver.get(url)
element = await page.select("#user-name")
await element.send_keys_async("standard_user")
element = await page.select("#password")
await element.send_keys_async("secret_sauce")
element = await page.select('input[type="submit"]')
await element.click_async()
cookies = await driver.cookies.get_all()
await page.close()
return cookies


# Load previously saved cookies to bypass login
async def login_with_cookies(cookies):
url_1 = "https://www.saucedemo.com"
url_2 = "https://www.saucedemo.com/inventory.html"
driver = await cdp_driver.start_async()
page = await driver.get(url_1)
await driver.cookies.set_all(cookies)
await driver.get(url_2)
await page.select("div.inventory_list")
time.sleep(2)


if __name__ == "__main__":
loop = asyncio.new_event_loop()
cookies = loop.run_until_complete(get_login_cookies())
loop.run_until_complete(login_with_cookies(cookies))
12 changes: 4 additions & 8 deletions examples/cdp_mode/raw_priceline.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from seleniumbase import SB

with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
window_handle = sb.driver.current_window_handle
url = "https://www.priceline.com"
sb.activate_cdp_mode(url)
sb.sleep(2.5)
@@ -17,17 +16,14 @@
sb.sleep(1.5)
sb.cdp.click('button[aria-label="Dismiss calendar"]')
sb.sleep(4.5)
sb.connect()
if len(sb.driver.window_handles) > 1:
sb.switch_to_window(window_handle)
sb.driver.close()
sb.sleep(0.2)
sb.switch_to_newest_window()
if len(sb.cdp.get_tabs()) > 1:
sb.cdp.close_active_tab()
sb.cdp.switch_to_newest_tab()
sb.sleep(0.6)
sb.sleep(0.8)
for y in range(1, 9):
sb.scroll_to_y(y * 400)
sb.sleep(0.75)
sb.sleep(0.5)
hotel_names = sb.find_elements('a[data-autobot-element-id*="HOTEL_NAME"]')
hotel_prices = sb.find_elements('span[font-size="4,,,5"]')
print("Priceline Hotels in %s:" % location)
14 changes: 5 additions & 9 deletions examples/presenter/uc_presentation_4.py
Original file line number Diff line number Diff line change
@@ -770,7 +770,6 @@ def test_presentation_4(self):
self.begin_presentation(filename="uc_presentation.html")

with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
window_handle = sb.driver.current_window_handle
url = "https://www.priceline.com"
sb.activate_cdp_mode(url)
sb.sleep(2.5)
@@ -786,22 +785,19 @@ def test_presentation_4(self):
sb.sleep(1.5)
sb.cdp.click('button[aria-label="Dismiss calendar"]')
sb.sleep(4.5)
sb.connect()
if len(sb.driver.window_handles) > 1:
sb.switch_to_window(window_handle)
sb.driver.close()
sb.sleep(0.2)
sb.switch_to_newest_window()
if len(sb.cdp.get_tabs()) > 1:
sb.cdp.close_active_tab()
sb.cdp.switch_to_newest_tab()
sb.sleep(0.6)
sb.sleep(0.8)
for y in range(1, 9):
sb.scroll_to_y(y * 400)
sb.sleep(0.75)
sb.sleep(0.5)
hotel_names = sb.find_elements(
'a[data-autobot-element-id*="HOTEL_NAME"]'
)
hotel_prices = sb.find_elements('span[font-size="4,,,5"]')
print("\n\nPriceline Hotels in %s:" % location)
print("Priceline Hotels in %s:" % location)
print(sb.get_text('[data-testid="POPOVER-DATE-PICKER"]'))
if len(hotel_names) == 0:
print("No availability over the selected dates!")
2 changes: 1 addition & 1 deletion help_docs/syntax_formats.md
Original file line number Diff line number Diff line change
@@ -1072,7 +1072,7 @@ def main():
for i in range(8):
sb.scroll_down(50)
sb.sleep(0.2)
cards = sb.select_all('h2[data-automation*="product-list-card"]')
cards = sb.select_all('span[data-automation*="product-list-card"]')
for card in cards:
print("* %s" % card.text)

2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ pathspec==0.12.1
Babel==2.17.0
paginate==0.5.7
mkdocs==1.6.1
mkdocs-material==9.6.5
mkdocs-material==9.6.7
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ sortedcontainers==2.4.0
execnet==2.1.1
iniconfig==2.0.0
pluggy==1.5.0
pytest==8.3.4
pytest==8.3.5
pytest-html==4.0.2
pytest-metadata==3.1.1
pytest-ordering==0.6
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.35.2"
__version__ = "4.35.3"
8 changes: 8 additions & 0 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
@@ -682,6 +682,13 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.gui_hover_element = CDPM.gui_hover_element
cdp.gui_hover_and_click = CDPM.gui_hover_and_click
cdp.internalize_links = CDPM.internalize_links
cdp.switch_to_window = CDPM.switch_to_window
cdp.switch_to_newest_window = CDPM.switch_to_newest_window
cdp.switch_to_tab = CDPM.switch_to_tab
cdp.switch_to_newest_tab = CDPM.switch_to_newest_tab
cdp.close_active_tab = CDPM.close_active_tab
cdp.get_active_tab = CDPM.get_active_tab
cdp.get_tabs = CDPM.get_tabs
cdp.get_window = CDPM.get_window
cdp.get_element_attributes = CDPM.get_element_attributes
cdp.get_element_attribute = CDPM.get_element_attribute
@@ -2033,6 +2040,7 @@ def _set_chrome_options(
prefs["download.default_directory"] = downloads_path
prefs["download.directory_upgrade"] = True
prefs["download.prompt_for_download"] = False
prefs["download_bubble.partial_view_enabled"] = False
prefs["credentials_enable_service"] = False
prefs["local_discovery.notifications_enabled"] = False
prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups
55 changes: 46 additions & 9 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
@@ -1014,10 +1014,51 @@ def reset_window_size(self):
self.set_window_rect(x, y, width, height)
self.__add_light_pause()

def switch_to_window(self, window):
self.switch_to_tab(window)

def switch_to_newest_window(self):
self.switch_to_tab(-1)

def switch_to_tab(self, tab):
driver = self.driver
if hasattr(driver, "cdp_base"):
driver = driver.cdp_base
if isinstance(tab, int):
self.page = driver.tabs[tab]
elif isinstance(tab, cdp_util.Tab):
self.page = tab
else:
raise Exception("`tab` must be an int or a Tab type!")
self.bring_active_window_to_front()

def switch_to_newest_tab(self):
self.switch_to_tab(-1)

def close_active_tab(self):
"""Close the active tab.
The active tab is the one currenly controlled by CDP.
The active tab MIGHT NOT be the currently visible tab!
(If a page opens a new tab, the new tab WON'T be active)
To switch the active tab, call: sb.switch_to_tab(tab)"""
return self.loop.run_until_complete(self.page.close())

def get_active_tab(self):
"""Return the active tab.
The active tab is the one currenly controlled by CDP.
The active tab MIGHT NOT be the currently visible tab!
(If a page opens a new tab, the new tab WON'T be active)
To switch the active tab, call: sb.switch_to_tab(tab)"""
return self.page

def get_tabs(self):
driver = self.driver
if hasattr(driver, "cdp_base"):
driver = driver.cdp_base
return driver.tabs

def get_window(self):
return self.loop.run_until_complete(
self.page.get_window()
)
return self.loop.run_until_complete(self.page.get_window())

def get_text(self, selector):
return self.find_element(selector).text_all
@@ -1211,14 +1252,10 @@ def get_gui_element_center(self, selector, timeout=None):
return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)

def get_document(self):
return self.loop.run_until_complete(
self.page.get_document()
)
return self.loop.run_until_complete(self.page.get_document())

def get_flattened_document(self):
return self.loop.run_until_complete(
self.page.get_flattened_document()
)
return self.loop.run_until_complete(self.page.get_flattened_document())

def get_element_attributes(self, selector):
selector = self.__convert_to_css_if_xpath(selector)
3 changes: 3 additions & 0 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
@@ -3918,6 +3918,9 @@ def switch_to_window(self, window, timeout=None):
timeout = settings.SMALL_TIMEOUT
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if self.__is_cdp_swap_needed() and not isinstance(window, str):
self.cdp.switch_to_tab(window)
return
page_actions.switch_to_window(self.driver, window, timeout)

def switch_to_default_window(self):
13 changes: 6 additions & 7 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
@@ -660,7 +660,7 @@ async def get_all(
break
else:
connection = self._browser.connection
cookies = await connection.send(cdp.storage.get_cookies())
cookies = await connection.send(cdp.network.get_cookies())
if requests_cookie_format:
import requests.cookies

@@ -690,8 +690,7 @@ async def set_all(self, cookies: List[cdp.network.CookieParam]):
break
else:
connection = self._browser.connection
cookies = await connection.send(cdp.storage.get_cookies())
await connection.send(cdp.storage.set_cookies(cookies))
await connection.send(cdp.network.set_cookies(cookies))

async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"):
"""
@@ -718,7 +717,7 @@ async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"):
break
else:
connection = self._browser.connection
cookies = await connection.send(cdp.storage.get_cookies())
cookies = await connection.send(cdp.network.get_cookies())
# if not connection:
# return
# if not connection.websocket:
@@ -776,7 +775,7 @@ async def load(self, file: PathLike = ".session.dat", pattern: str = ".*"):
cookie.value,
)
break
await connection.send(cdp.storage.set_cookies(included_cookies))
await connection.send(cdp.network.set_cookies(included_cookies))

async def clear(self):
"""
@@ -791,9 +790,9 @@ async def clear(self):
break
else:
connection = self._browser.connection
cookies = await connection.send(cdp.storage.get_cookies())
cookies = await connection.send(cdp.network.get_cookies())
if cookies:
await connection.send(cdp.storage.clear_cookies())
await connection.send(cdp.network.clear_cookies())


class HTTPApi:
2 changes: 1 addition & 1 deletion seleniumbase/undetected/cdp_driver/connection.py
Original file line number Diff line number Diff line change
@@ -382,7 +382,7 @@ async def update_target(self):
async def send(
self,
cdp_obj: Generator[dict[str, Any], dict[str, Any], Any],
_is_update=False,
_is_update=True,
) -> Any:
"""
Send a protocol command.
1 change: 1 addition & 0 deletions seleniumbase/undetected/cdp_driver/tab.py
Original file line number Diff line number Diff line change
@@ -852,6 +852,7 @@ async def close(self):
await self.send(
cdp.target.close_target(target_id=self.target.target_id)
)
await asyncio.sleep(0.1)

async def get_window(self) -> Tuple[
cdp.browser.WindowID, cdp.browser.Bounds
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -198,7 +198,7 @@
'execnet==2.1.1',
'iniconfig==2.0.0',
'pluggy==1.5.0',
'pytest==8.3.4',
'pytest==8.3.5',
"pytest-html==4.0.2", # Newer ones had issues
'pytest-metadata==3.1.1',
"pytest-ordering==0.6",
@@ -259,7 +259,7 @@
"pdfminer": [
'pdfminer.six==20240706',
'cryptography==39.0.2;python_version<"3.9"',
'cryptography==44.0.1;python_version>="3.9"',
'cryptography==44.0.2;python_version>="3.9"',
'cffi==1.17.1',
"pycparser==2.22",
],