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
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
7 changes: 7 additions & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion examples/cdp_mode/raw_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion examples/cdp_mode/raw_cdp_with_sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
14 changes: 5 additions & 9 deletions examples/presenter/uc_presentation_4.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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!")
Expand Down
2 changes: 1 addition & 1 deletion help_docs/syntax_formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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
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.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
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
55 changes: 46 additions & 9 deletions seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
13 changes: 6 additions & 7 deletions seleniumbase/undetected/cdp_driver/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 = ".*"):
"""
Expand All @@ -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:
Expand Down Expand Up @@ -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):
"""
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/undetected/cdp_driver/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions seleniumbase/undetected/cdp_driver/tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
],
Expand Down