Skip to content

Commit 11a834a

Browse files
authored
Merge pull request #3996 from seleniumbase/cdp-mode-patch-59
CDP Mode: Patch 59
2 parents 186e472 + 58c8f65 commit 11a834a

24 files changed

+287
-108
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ with SB(uc=True, test=True, locale="en") as sb:
9999
sb.activate_cdp_mode(url)
100100
sb.sleep(2.2)
101101
sb.uc_gui_click_captcha()
102+
# (The rest is for testing and demo purposes)
102103
sb.assert_text("Username", '[for="user_login"]', timeout=3)
103104
sb.assert_element('label[for="user_login"]')
104105
sb.highlight('button:contains("Sign in")')

examples/boilerplates/samples/sb_swag_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"""Classic Page Object Model with the "sb" fixture."""
2+
from seleniumbase import BaseCase
3+
BaseCase.main(__name__, __file__)
24

35

46
class LoginPage:
5-
def login_to_swag_labs(self, sb, username):
7+
def login_to_swag_labs(self, sb: BaseCase, username):
68
sb.open("https://www.saucedemo.com")
79
sb.type("#user-name", username)
810
sb.type("#password", "secret_sauce")
911
sb.click('input[type="submit"]')
1012

1113

1214
class MyTests:
13-
def test_swag_labs_login(self, sb):
15+
def test_swag_labs_login(self, sb: BaseCase):
1416
LoginPage().login_to_swag_labs(sb, "standard_user")
1517
sb.assert_element("div.inventory_list")
1618
sb.assert_element('div:contains("Sauce Labs Backpack")')

examples/boilerplates/samples/swag_labs_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
class LoginPage:
7-
def login_to_swag_labs(self, sb, username):
7+
def login_to_swag_labs(self, sb: BaseCase, username):
88
sb.open("https://www.saucedemo.com")
99
sb.type("#user-name", username)
1010
sb.type("#password", "secret_sauce")

examples/cdp_mode/ReadMe.md

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,27 @@
4545
4646
That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks).
4747

48-
Simple example: ([SeleniumBase/examples/cdp_mode/raw_gitlab.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py))
48+
Simple example from [SeleniumBase/examples/cdp_mode/raw_gitlab.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py):
4949

5050
```python
5151
from seleniumbase import SB
5252

5353
with SB(uc=True, test=True, locale="en") as sb:
5454
url = "https://gitlab.com/users/sign_in"
5555
sb.activate_cdp_mode(url)
56-
sb.sleep(1)
56+
sb.sleep(2.2)
5757
sb.uc_gui_click_captcha()
58-
sb.sleep(2)
5958
```
6059

6160
<img src="https://seleniumbase.github.io/other/cf_sec.jpg" title="SeleniumBase" width="332"> <img src="https://seleniumbase.github.io/other/gitlab_bypass.png" title="SeleniumBase" width="288">
6261

63-
(If the CAPTCHA wasn't bypassed automatically, then `sb.uc_gui_click_captcha()` gets the job done.)
62+
(If the CAPTCHA wasn't bypassed automatically when going to the URL, then `sb.uc_gui_click_captcha()` gets the job done with a mouse click from [PyAutoGUI](https://github.com/asweigart/pyautogui).)
6463

65-
Note that `PyAutoGUI` is an optional dependency. If calling a method that uses it when not already installed, then `SeleniumBase` installs `PyAutoGUI` at run-time.
64+
ℹ️ Note that `PyAutoGUI` is an optional dependency. If calling a method that uses it when not already installed, then `SeleniumBase` installs `PyAutoGUI` at run-time.
6665

6766
--------
6867

69-
For some Cloudflare CAPTCHAs that appear within websites, you may need to use `sb.cdp.gui_click_element(selector)` instead (if the Turnstile wasn't bypassed automatically). Example: ([SeleniumBase/examples/cdp_mode/raw_planetmc.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_planetmc.py))
68+
You can also use `sb.cdp.gui_click_element(selector)` to click on elements using `PyAutoGUI`. (This is useful when clicking inside `#shadow-root`.) Example:
7069

7170
```python
7271
from seleniumbase import SB
@@ -86,17 +85,19 @@ Eg. `sb.cdp.gui_click_element("#turnstile-widget div")`
8685

8786
<img src="https://seleniumbase.github.io/other/above_shadow.png" title="SeleniumBase" width="480">
8887

88+
In most cases, `sb.uc_gui_click_captcha()` is good enough for CF Turnstiles without needing `sb.cdp.gui_click_element(selector)`. (See [SeleniumBase/examples/cdp_mode/raw_planetmc.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_planetmc.py))
89+
8990
--------
9091

9192
### 🐙 Here are a few common `sb.cdp` methods:
9293

9394
* `sb.cdp.click(selector)` (Uses the CDP API to click)
94-
* `sb.cdp.click_if_visible(selector)`
95+
* `sb.cdp.click_if_visible(selector)` (Click if visible)
9596
* `sb.cdp.gui_click_element(selector)` (Uses `PyAutoGUI`)
96-
* `sb.cdp.type(selector, text)`
97+
* `sb.cdp.type(selector, text)` (Type text into a selector)
9798
* `sb.cdp.press_keys(selector, text)` (Human-speed `type`)
98-
* `sb.cdp.select_all(selector)`
99-
* `sb.cdp.get_text(selector)`
99+
* `sb.cdp.select_all(selector)` (Returns matching elements)
100+
* `sb.cdp.get_text(selector)` (Returns the element's text)
100101

101102
Methods that start with `sb.cdp.gui` use `PyAutoGUI` for interaction.
102103

@@ -161,18 +162,12 @@ with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
161162
sb.sleep(2)
162163
sb.cdp.highlight_overlay("div.pokemon-ability-info")
163164
sb.sleep(2)
164-
sb.cdp.click('a[href="https://www.pokemon.com/us/play-pokemon/"]')
165-
sb.sleep(0.6)
166-
sb.cdp.click('h3:contains("Find an Event")')
167-
location = "Concord, MA, USA"
168-
sb.cdp.type('input[data-testid="location-search"]', location)
169-
sb.sleep(1.5)
170-
sb.cdp.click("div.autocomplete-dropdown-container div.suggestion-item")
171-
sb.sleep(0.6)
172-
sb.cdp.click('img[alt="search-icon"]')
173-
sb.sleep(2)
174-
events = sb.cdp.select_all('div[data-testid="event-name"]')
175-
print("*** Pokemon events near %s: ***" % location)
165+
sb.cdp.open("https://events.pokemon.com/EventLocator/")
166+
sb.sleep(3)
167+
sb.cdp.click('button span:contains("Premier Events")')
168+
sb.sleep(1)
169+
events = sb.cdp.select_all('div[class="event-info"]')
170+
print("*** Upcoming Premier Events for Pokémon: ***")
176171
for event in events:
177172
print("* " + event.text)
178173
sb.sleep(2)
@@ -371,6 +366,9 @@ sb.cdp.select(selector, timeout=None)
371366
sb.cdp.select_all(selector, timeout=None)
372367
sb.cdp.find_elements(selector, timeout=None)
373368
sb.cdp.find_visible_elements(selector, timeout=None)
369+
sb.cdp.click(selector, timeout=None)
370+
sb.cdp.click_if_visible(selector)
371+
sb.cdp.click_visible_elements(selector, limit=0)
374372
sb.cdp.click_nth_element(selector, number)
375373
sb.cdp.click_nth_visible_element(selector, number)
376374
sb.cdp.click_link(link_text)
@@ -391,10 +389,7 @@ sb.cdp.bring_active_window_to_front()
391389
sb.cdp.bring_to_front()
392390
sb.cdp.get_active_element()
393391
sb.cdp.get_active_element_css()
394-
sb.cdp.click(selector, timeout=None)
395392
sb.cdp.click_active_element()
396-
sb.cdp.click_if_visible(selector)
397-
sb.cdp.click_visible_elements(selector, limit=0)
398393
sb.cdp.mouse_click(selector, timeout=None)
399394
sb.cdp.nested_click(parent_selector, selector)
400395
sb.cdp.get_nested_element(parent_selector, selector)
@@ -488,6 +483,7 @@ sb.cdp.is_exact_text_visible(text, selector="body")
488483
sb.cdp.wait_for_text(text, selector="body", timeout=None)
489484
sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None)
490485
sb.cdp.wait_for_element_visible(selector, timeout=None)
486+
sb.cdp.wait_for_element(selector, timeout=None)
491487
sb.cdp.wait_for_element_not_visible(selector, timeout=None)
492488
sb.cdp.wait_for_element_absent(selector, timeout=None)
493489
sb.cdp.wait_for_any_of_elements_visible(*args, **kwargs)
@@ -524,10 +520,66 @@ sb.cdp.print_to_pdf(name, folder=None)
524520
sb.cdp.save_as_pdf(name, folder=None)
525521
```
526522

523+
ℹ️ When available, calling `sb.METHOD()` redirects to `sb.cdp.METHOD()` because regular SB methods automatically call their CDP Mode counterparts to maintain stealth when CDP Mode is active.
524+
525+
--------
526+
527+
### 🐙 <b translate="no">Pure CDP Mode</b> (<code translate="no">sb_cdp</code>)
528+
529+
<b translate="no">Pure CDP Mode</b> doesn't use WebDriver for anything. The browser is launched using CDP, and all browser actions are performed using CDP (or <code>PyAutoGUI</code>). Initialization:
530+
531+
```python
532+
from seleniumbase import sb_cdp
533+
534+
sb = sb_cdp.Chrome(url=None, **kwargs)
535+
```
536+
537+
<b translate="no">Pure CDP Mode</b> includes all methods from regular CDP Mode, except that they're called directly from <code>sb</code> instead of <code>sb.cdp</code>. Eg: <code>sb.gui_click_captcha()</code>. To quit a CDP-launched browser, use `sb.driver.stop()`.
538+
539+
Basic example from [SeleniumBase/examples/cdp_mode/raw_cdp_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_turnstile.py):
540+
541+
```python
542+
from seleniumbase import sb_cdp
543+
544+
url = "https://seleniumbase.io/apps/turnstile"
545+
sb = sb_cdp.Chrome(url)
546+
sb.gui_click_captcha()
547+
sb.sleep(2)
548+
sb.driver.stop()
549+
```
550+
551+
Another example: ([SeleniumBase/examples/cdp_mode/raw_cdp_methods.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_methods.py))
552+
553+
```python
554+
from seleniumbase import sb_cdp
555+
556+
url = "https://seleniumbase.io/demo_page"
557+
sb = sb_cdp.Chrome(url)
558+
sb.press_keys("input", "Text")
559+
sb.highlight("button")
560+
sb.type("textarea", "Here are some words")
561+
sb.click("button")
562+
sb.set_value("input#mySlider", "100")
563+
sb.click_visible_elements("input.checkBoxClassB")
564+
sb.select_option_by_text("#mySelect", "Set to 75%")
565+
sb.gui_hover_and_click("#myDropdown", "#dropOption2")
566+
sb.gui_click_element("#checkBox1")
567+
sb.gui_drag_and_drop("img#logo", "div#drop2")
568+
sb.nested_click("iframe#myFrame3", ".fBox")
569+
sb.sleep(2)
570+
sb.driver.stop()
571+
```
572+
573+
ℹ️ Even if you don't call `sb.driver.stop()`, the browser still quits after the script goes out-of-scope.
574+
527575
--------
528576

529577
### 🐙 <b translate="no">CDP Mode</b> WebElement API / Methods
530578

579+
After finding an element in CDP Mode, you can access `WebElement` methods:
580+
581+
(Eg. After `element = sb.find_element(selector)`)
582+
531583
```python
532584
element.clear_input()
533585
element.click()

examples/cdp_mode/raw_ahrefs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
99
sb.cdp.scroll_down(36)
1010
sb.click(submit_button)
11+
sb.sleep(1)
1112
sb.uc_gui_click_captcha()
13+
sb.sleep(3)
1214
sb.wait_for_text_not_visible("Checking", timeout=15)
1315
sb.click_if_visible('button[data-cky-tag="close-button"]')
1416
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')

examples/cdp_mode/raw_copilot.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True, guest=True) as sb:
4+
url = "https://copilot.microsoft.com/"
5+
sb.activate_cdp_mode(url)
6+
textarea = "textarea#userInput"
7+
sb.wait_for_element(textarea)
8+
sb.sleep(1.5)
9+
sb.click_if_visible('[aria-label="Dismiss"]')
10+
sb.sleep(0.5)
11+
sb.click('button[data-testid*="chat-mode-"]')
12+
sb.sleep(1.1)
13+
sb.click('button[title="Think Deeper"]')
14+
sb.sleep(1.1)
15+
query = "How to migrate from Playwright to SeleniumBase?"
16+
sb.press_keys(textarea, query)
17+
sb.sleep(1.1)
18+
sb.click('button[data-testid="submit-button"]')
19+
sb.sleep(2.5)
20+
sb.uc_gui_click_captcha()
21+
sb.sleep(2.5)
22+
sb.uc_gui_click_captcha()
23+
sb.sleep(2.5)
24+
stop_button = '[data-testid="stop-button"]'
25+
thumbs_up = 'button[data-testid*="-thumbs-up-"]'
26+
sb.wait_for_element_absent(stop_button, timeout=30)
27+
sb.wait_for_element(thumbs_up, timeout=30)
28+
sb.sleep(0.5)
29+
sb.click('button[data-testid*="scroll-to-bottom"]')
30+
sb.sleep(1.5)
31+
folder = "downloaded_files"
32+
file_name = "copilot_results.html"
33+
sb.save_page_source(file_name, folder)
34+
print('"./%s/%s" was saved!' % (folder, file_name))

examples/cdp_mode/raw_gitlab.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
sb.activate_cdp_mode(url)
66
sb.sleep(2.2)
77
sb.uc_gui_click_captcha()
8+
# (The rest is for testing and demo purposes)
89
sb.assert_text("Username", '[for="user_login"]', timeout=3)
910
sb.assert_element('label[for="user_login"]')
1011
sb.highlight('button:contains("Sign in")')

examples/cdp_mode/raw_multi_captcha.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
def main(url):
99
sb = sb_cdp.Chrome(url, lang="en")
1010
sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520)
11-
sb.sleep(2.2)
11+
sb.sleep(2)
1212
sb.gui_click_captcha()
1313
sb.sleep(2)
1414
sb.driver.quit()

examples/cdp_mode/raw_planetmc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
url = "www.planetminecraft.com/account/sign_in/"
55
sb.activate_cdp_mode(url)
66
sb.sleep(2)
7-
sb.cdp.gui_click_element("#turnstile-widget div")
7+
sb.uc_gui_click_captcha()
88
sb.sleep(2)

examples/cdp_mode/raw_turnstile.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True) as sb:
4+
url = "https://seleniumbase.io/apps/turnstile"
5+
sb.activate_cdp_mode(url)
6+
sb.uc_gui_click_captcha()
7+
sb.assert_element("img#captcha-success", timeout=3)
8+
sb.set_messenger_theme(location="top_left")
9+
sb.post_message("SeleniumBase wasn't detected", duration=3)

0 commit comments

Comments
 (0)