Skip to content
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ with SB(uc=True, test=True, locale="en") as sb:
sb.activate_cdp_mode(url)
sb.sleep(2.2)
sb.uc_gui_click_captcha()
# (The rest is for testing and demo purposes)
sb.assert_text("Username", '[for="user_login"]', timeout=3)
sb.assert_element('label[for="user_login"]')
sb.highlight('button:contains("Sign in")')
Expand Down
6 changes: 4 additions & 2 deletions examples/boilerplates/samples/sb_swag_test.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""Classic Page Object Model with the "sb" fixture."""
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)


class LoginPage:
def login_to_swag_labs(self, sb, username):
def login_to_swag_labs(self, sb: BaseCase, username):
sb.open("https://www.saucedemo.com")
sb.type("#user-name", username)
sb.type("#password", "secret_sauce")
sb.click('input[type="submit"]')


class MyTests:
def test_swag_labs_login(self, sb):
def test_swag_labs_login(self, sb: BaseCase):
LoginPage().login_to_swag_labs(sb, "standard_user")
sb.assert_element("div.inventory_list")
sb.assert_element('div:contains("Sauce Labs Backpack")')
Expand Down
2 changes: 1 addition & 1 deletion examples/boilerplates/samples/swag_labs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class LoginPage:
def login_to_swag_labs(self, sb, username):
def login_to_swag_labs(self, sb: BaseCase, username):
sb.open("https://www.saucedemo.com")
sb.type("#user-name", username)
sb.type("#password", "secret_sauce")
Expand Down
102 changes: 77 additions & 25 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,27 @@

That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks).

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

```python
from seleniumbase import SB

with SB(uc=True, test=True, locale="en") as sb:
url = "https://gitlab.com/users/sign_in"
sb.activate_cdp_mode(url)
sb.sleep(1)
sb.sleep(2.2)
sb.uc_gui_click_captcha()
sb.sleep(2)
```

<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">

(If the CAPTCHA wasn't bypassed automatically, then `sb.uc_gui_click_captcha()` gets the job done.)
(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).)

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.
ℹ️ 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.

--------

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))
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:

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

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

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))

--------

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

* `sb.cdp.click(selector)` (Uses the CDP API to click)
* `sb.cdp.click_if_visible(selector)`
* `sb.cdp.click_if_visible(selector)` (Click if visible)
* `sb.cdp.gui_click_element(selector)` (Uses `PyAutoGUI`)
* `sb.cdp.type(selector, text)`
* `sb.cdp.type(selector, text)` (Type text into a selector)
* `sb.cdp.press_keys(selector, text)` (Human-speed `type`)
* `sb.cdp.select_all(selector)`
* `sb.cdp.get_text(selector)`
* `sb.cdp.select_all(selector)` (Returns matching elements)
* `sb.cdp.get_text(selector)` (Returns the element's text)

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

Expand Down Expand Up @@ -161,18 +162,12 @@ with SB(uc=True, test=True, locale="en", ad_block=True) as sb:
sb.sleep(2)
sb.cdp.highlight_overlay("div.pokemon-ability-info")
sb.sleep(2)
sb.cdp.click('a[href="https://www.pokemon.com/us/play-pokemon/"]')
sb.sleep(0.6)
sb.cdp.click('h3:contains("Find an Event")')
location = "Concord, MA, USA"
sb.cdp.type('input[data-testid="location-search"]', location)
sb.sleep(1.5)
sb.cdp.click("div.autocomplete-dropdown-container div.suggestion-item")
sb.sleep(0.6)
sb.cdp.click('img[alt="search-icon"]')
sb.sleep(2)
events = sb.cdp.select_all('div[data-testid="event-name"]')
print("*** Pokemon events near %s: ***" % location)
sb.cdp.open("https://events.pokemon.com/EventLocator/")
sb.sleep(3)
sb.cdp.click('button span:contains("Premier Events")')
sb.sleep(1)
events = sb.cdp.select_all('div[class="event-info"]')
print("*** Upcoming Premier Events for Pokémon: ***")
for event in events:
print("* " + event.text)
sb.sleep(2)
Expand Down Expand Up @@ -371,6 +366,9 @@ sb.cdp.select(selector, timeout=None)
sb.cdp.select_all(selector, timeout=None)
sb.cdp.find_elements(selector, timeout=None)
sb.cdp.find_visible_elements(selector, timeout=None)
sb.cdp.click(selector, timeout=None)
sb.cdp.click_if_visible(selector)
sb.cdp.click_visible_elements(selector, limit=0)
sb.cdp.click_nth_element(selector, number)
sb.cdp.click_nth_visible_element(selector, number)
sb.cdp.click_link(link_text)
Expand All @@ -391,10 +389,7 @@ sb.cdp.bring_active_window_to_front()
sb.cdp.bring_to_front()
sb.cdp.get_active_element()
sb.cdp.get_active_element_css()
sb.cdp.click(selector, timeout=None)
sb.cdp.click_active_element()
sb.cdp.click_if_visible(selector)
sb.cdp.click_visible_elements(selector, limit=0)
sb.cdp.mouse_click(selector, timeout=None)
sb.cdp.nested_click(parent_selector, selector)
sb.cdp.get_nested_element(parent_selector, selector)
Expand Down Expand Up @@ -488,6 +483,7 @@ 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.wait_for_element(selector, timeout=None)
sb.cdp.wait_for_element_not_visible(selector, timeout=None)
sb.cdp.wait_for_element_absent(selector, timeout=None)
sb.cdp.wait_for_any_of_elements_visible(*args, **kwargs)
Expand Down Expand Up @@ -524,10 +520,66 @@ sb.cdp.print_to_pdf(name, folder=None)
sb.cdp.save_as_pdf(name, folder=None)
```

ℹ️ 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.

--------

### 🐙 <b translate="no">Pure CDP Mode</b> (<code translate="no">sb_cdp</code>)

<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:

```python
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome(url=None, **kwargs)
```

<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()`.

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):

```python
from seleniumbase import sb_cdp

url = "https://seleniumbase.io/apps/turnstile"
sb = sb_cdp.Chrome(url)
sb.gui_click_captcha()
sb.sleep(2)
sb.driver.stop()
```

Another example: ([SeleniumBase/examples/cdp_mode/raw_cdp_methods.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_methods.py))

```python
from seleniumbase import sb_cdp

url = "https://seleniumbase.io/demo_page"
sb = sb_cdp.Chrome(url)
sb.press_keys("input", "Text")
sb.highlight("button")
sb.type("textarea", "Here are some words")
sb.click("button")
sb.set_value("input#mySlider", "100")
sb.click_visible_elements("input.checkBoxClassB")
sb.select_option_by_text("#mySelect", "Set to 75%")
sb.gui_hover_and_click("#myDropdown", "#dropOption2")
sb.gui_click_element("#checkBox1")
sb.gui_drag_and_drop("img#logo", "div#drop2")
sb.nested_click("iframe#myFrame3", ".fBox")
sb.sleep(2)
sb.driver.stop()
```

ℹ️ Even if you don't call `sb.driver.stop()`, the browser still quits after the script goes out-of-scope.

--------

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

After finding an element in CDP Mode, you can access `WebElement` methods:

(Eg. After `element = sb.find_element(selector)`)

```python
element.clear_input()
element.click()
Expand Down
2 changes: 2 additions & 0 deletions examples/cdp_mode/raw_ahrefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
sb.cdp.scroll_down(36)
sb.click(submit_button)
sb.sleep(1)
sb.uc_gui_click_captcha()
sb.sleep(3)
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")')
Expand Down
34 changes: 34 additions & 0 deletions examples/cdp_mode/raw_copilot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from seleniumbase import SB

with SB(uc=True, test=True, guest=True) as sb:
url = "https://copilot.microsoft.com/"
sb.activate_cdp_mode(url)
textarea = "textarea#userInput"
sb.wait_for_element(textarea)
sb.sleep(1.5)
sb.click_if_visible('[aria-label="Dismiss"]')
sb.sleep(0.5)
sb.click('button[data-testid*="chat-mode-"]')
sb.sleep(1.1)
sb.click('button[title="Think Deeper"]')
sb.sleep(1.1)
query = "How to migrate from Playwright to SeleniumBase?"
sb.press_keys(textarea, query)
sb.sleep(1.1)
sb.click('button[data-testid="submit-button"]')
sb.sleep(2.5)
sb.uc_gui_click_captcha()
sb.sleep(2.5)
sb.uc_gui_click_captcha()
sb.sleep(2.5)
stop_button = '[data-testid="stop-button"]'
thumbs_up = 'button[data-testid*="-thumbs-up-"]'
sb.wait_for_element_absent(stop_button, timeout=30)
sb.wait_for_element(thumbs_up, timeout=30)
sb.sleep(0.5)
sb.click('button[data-testid*="scroll-to-bottom"]')
sb.sleep(1.5)
folder = "downloaded_files"
file_name = "copilot_results.html"
sb.save_page_source(file_name, folder)
print('"./%s/%s" was saved!' % (folder, file_name))
1 change: 1 addition & 0 deletions examples/cdp_mode/raw_gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
sb.activate_cdp_mode(url)
sb.sleep(2.2)
sb.uc_gui_click_captcha()
# (The rest is for testing and demo purposes)
sb.assert_text("Username", '[for="user_login"]', timeout=3)
sb.assert_element('label[for="user_login"]')
sb.highlight('button:contains("Sign in")')
Expand Down
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_multi_captcha.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def main(url):
sb = sb_cdp.Chrome(url, lang="en")
sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520)
sb.sleep(2.2)
sb.sleep(2)
sb.gui_click_captcha()
sb.sleep(2)
sb.driver.quit()
Expand Down
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_planetmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
url = "www.planetminecraft.com/account/sign_in/"
sb.activate_cdp_mode(url)
sb.sleep(2)
sb.cdp.gui_click_element("#turnstile-widget div")
sb.uc_gui_click_captcha()
sb.sleep(2)
9 changes: 9 additions & 0 deletions examples/cdp_mode/raw_turnstile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/apps/turnstile"
sb.activate_cdp_mode(url)
sb.uc_gui_click_captcha()
sb.assert_element("img#captcha-success", timeout=3)
sb.set_messenger_theme(location="top_left")
sb.post_message("SeleniumBase wasn't detected", duration=3)
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_united.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
part_3 = flight.text.split(" Destination")[-1].split(" Aircraft")[0]
parts = "%s - %s %s" % (part_1, part_2, part_3)
print("* " + parts)
for category in ["ECO-BASIC", "ECONOMY"]:
for category in ["ECONOMY", "ECONOMY-UNRESTRICTED"]:
prices = sb.find_elements('[aria-describedby="%s"]' % category)
full_prices = []
for item in prices:
Expand Down
6 changes: 1 addition & 5 deletions examples/hack_the_planet.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,7 @@ def test_all_your_base_are_belong_to_us(self):

self.open("https://git-scm.com/")
self.set_text_content("span#tagline", aybabtu)
self.set_text_content("#nav-about h3", ayb)
self.set_text_content("#nav-documentation h3", abtu)
self.highlight("span#tagline", loops=8, scroll=False)
self.highlight("#nav-about h3", loops=5, scroll=False)
self.highlight("#nav-documentation h3", loops=6, scroll=False)
self.highlight("span#tagline", loops=10, scroll=False)

self.open("https://pragprog.com/")
self.set_text_content("header p", aybabtu)
Expand Down
8 changes: 6 additions & 2 deletions examples/sb_fixture_tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)


# "sb" pytest fixture test in a method with no class
def test_sb_fixture_with_no_class(sb):
def test_sb_fixture_with_no_class(sb: BaseCase):
sb.open("seleniumbase.io/simple/login")
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
Expand All @@ -13,7 +17,7 @@ def test_sb_fixture_with_no_class(sb):

# "sb" pytest fixture test in a method inside a class
class Test_SB_Fixture:
def test_sb_fixture_inside_class(self, sb):
def test_sb_fixture_inside_class(self, sb: BaseCase):
sb.open("seleniumbase.io/simple/login")
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
Expand Down
6 changes: 4 additions & 2 deletions examples/test_override_sb_fixture.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Overriding the "sb" fixture to override the driver."""
import pytest
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)


@pytest.fixture()
Expand Down Expand Up @@ -66,7 +68,7 @@ def tearDown(self):
sb._needs_tearDown = False


def test_override_fixture_no_class(sb):
def test_override_fixture_no_class(sb: BaseCase):
sb.open("https://seleniumbase.io/demo_page")
sb.type("#myTextInput", "This is Automated")
sb.set_value("input#mySlider", "100")
Expand All @@ -77,7 +79,7 @@ def test_override_fixture_no_class(sb):


class TestOverride:
def test_override_fixture_inside_class(self, sb):
def test_override_fixture_inside_class(self, sb: BaseCase):
sb.open("https://seleniumbase.io/demo_page")
sb.type("#myTextInput", "This is Automated")
sb.set_value("input#mySlider", "100")
Expand Down
8 changes: 6 additions & 2 deletions examples/test_request_sb_fixture.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)


# Use the pytest "request" fixture to get the "sb" fixture (no class)
def test_request_sb_fixture(request):
sb = request.getfixturevalue("sb")
sb: BaseCase = request.getfixturevalue("sb")
sb.open("https://seleniumbase.io/demo_page")
sb.assert_text("SeleniumBase", "#myForm h2")
sb.assert_element("input#myTextInput")
Expand All @@ -12,7 +16,7 @@ def test_request_sb_fixture(request):
# Use the pytest "request" fixture to get the "sb" fixture (in class)
class Test_Request_Fixture:
def test_request_sb_fixture_in_class(self, request):
sb = request.getfixturevalue("sb")
sb: BaseCase = request.getfixturevalue("sb")
sb.open("https://seleniumbase.io/demo_page")
sb.assert_element("input#myTextInput")
sb.type("#myTextarea", "Automated")
Expand Down
Loading