Skip to content

Commit

Permalink
Update examples
Browse files Browse the repository at this point in the history
  • Loading branch information
mdmintz committed Dec 15, 2024
1 parent 0d6c255 commit 6cb8844
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 27 deletions.
2 changes: 1 addition & 1 deletion examples/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

* <b>SeleniumBase</b> tests are run with <b>pytest</b>.
* Chrome is the default browser if not specified.
* Tests are structured using [23 unique syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md).
* Tests are structured using [25 unique syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md).
* Logs from test failures are saved to ``./latest_logs/``.
* Tests can be run with [multiple command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).
* Examples can be found in: **[SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples)**.
Expand Down
53 changes: 45 additions & 8 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## [<img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) CDP Mode 🐙

🐙 <b translate="no">SeleniumBase</b> <b translate="no">CDP Mode</b> (Chrome Devtools Protocol Mode) is a special mode inside of <b><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md" translate="no"><span translate="no">SeleniumBase UC Mode</span></a></b> that lets bots appear human while controlling the browser with the <b translate="no">CDP-Driver</b>. Although regular <span translate="no">UC Mode</span> can't perform <span translate="no">WebDriver</span> actions while the <code>driver</code> is disconnected from the browser, the <span translate="no">CDP-Driver</span> can still perform actions while maintaining its cover.
🐙 <b translate="no">SeleniumBase</b> <b translate="no">CDP Mode</b> (Chrome Devtools Protocol Mode) is a special mode inside of <b><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md" translate="no"><span translate="no">SeleniumBase UC Mode</span></a></b> that lets bots appear human while controlling the browser with the <b translate="no">CDP-Driver</b>. Although regular <b translate="no">UC Mode</b> can't perform <span translate="no">WebDriver</span> actions while the <code>driver</code> is disconnected from the browser, the <b translate="no">CDP-Driver</b> can.

--------

Expand All @@ -13,7 +13,7 @@

👤 <b translate="no">UC Mode</b> avoids bot-detection by first disconnecting WebDriver from the browser at strategic times, calling special <code>PyAutoGUI</code> methods to bypass CAPTCHAs (as needed), and finally reconnecting the <code>driver</code> afterwards so that WebDriver actions can be performed again. Although this approach works for bypassing simple CAPTCHAs, more flexibility is needed for bypassing bot-detection on websites with advanced protection. (That's where <b translate="no">CDP Mode</b> comes in.)

🐙 <b translate="no">CDP Mode</b> is based on <a href="https://github.com/HyperionGray/python-chrome-devtools-protocol" translate="no">python-cdp</a>, <a href="https://github.com/HyperionGray/trio-chrome-devtools-protocol" translate="no">trio-cdp</a>, and <a href="https://github.com/ultrafunkamsterdam/nodriver" translate="no">nodriver</a>. <code>trio-cdp</code> is an early implementation of <code>python-cdp</code>, and <code>nodriver</code> is a modern implementation of <code>python-cdp</code>. (Refactored Python-CDP code is imported from <a href="https://github.com/mdmintz/MyCDP" translate="no">MyCDP</a>.)
🐙 <b translate="no">CDP Mode</b> is based on <a href="https://github.com/HyperionGray/python-chrome-devtools-protocol" translate="no">python-cdp</a>, <a href="https://github.com/HyperionGray/trio-chrome-devtools-protocol" translate="no">trio-cdp</a>, and <a href="https://github.com/ultrafunkamsterdam/nodriver" translate="no">nodriver</a>. <code>trio-cdp</code> is an early implementation of <code>python-cdp</code>, and <code>nodriver</code> is a modern implementation of <code>python-cdp</code>. (Refactored <code>Python-CDP</code> code is imported from <a href="https://github.com/mdmintz/MyCDP" translate="no">MyCDP</a>.)

🐙 <b translate="no">CDP Mode</b> includes multiple updates to the above, such as:

Expand All @@ -35,17 +35,52 @@
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_planetmc.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_planetmc.py))

```python
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "www.planetminecraft.com/account/sign_in/"
sb.activate_cdp_mode(url)
sb.sleep(2)
sb.cdp.gui_click_element("#turnstile-widget div")
sb.sleep(2)
```

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

(If the CAPTCHA wasn't initially bypassed, then the click gets the job done.)

Note that `PyAutoGUI` is an optional dependency. If calling a method that uses it when not already installed, then `SeleniumBase` will install it at run-time, which pauses the script briefly.

For standard Cloudflare pages, use `sb.uc_gui_click_captcha()` if Turnstiles aren't initially bypassed. Example: ([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_code="en") as sb:
url = "https://gitlab.com/users/sign_in"
sb.activate_cdp_mode(url)
sb.uc_gui_click_captcha()
sb.sleep(2)
```

<img src="https://seleniumbase.github.io/other/cf_sec.jpg" title="SeleniumBase" width="404"> <img src="https://seleniumbase.github.io/other/gitlab_bypass.png" title="SeleniumBase" width="350">

--------

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

* `sb.cdp.click(selector)`
* `sb.cdp.click(selector)` (Uses the CDP API to click)
* `sb.cdp.click_if_visible(selector)`
* `sb.cdp.gui_click_element(selector)`
* `sb.cdp.gui_click_element(selector)` (Uses `PyAutoGUI`)
* `sb.cdp.type(selector, text)`
* `sb.cdp.press_keys(selector, text)`
* `sb.cdp.press_keys(selector, text)` (Human-speed `type`)
* `sb.cdp.select_all(selector)`
* `sb.cdp.get_text(selector)`

When `type()` is too fast, use the slower `press_keys()` to avoid detection. You can also use `sb.sleep(seconds)` to slow things down. Methods that start with `sb.cdp.gui` use `PyAutoGUI` for interaction.
Methods that start with `sb.cdp.gui` use `PyAutoGUI` for interaction.

To use WebDriver methods again, call:

Expand All @@ -57,15 +92,17 @@ To disconnect again, call:

* **`sb.disconnect()`**

While disconnected, if you accidentally call a WebDriver method, then SeleniumBase will attempt to use the CDP Mode version of that method (if available). For example, if you accidentally call `sb.click(selector)` instead of `sb.cdp.click(selector)`, then your WebDriver call will automatically be redirected to the CDP Mode version. Not all WebDriver methods have a matching CDP Mode method. In that scenario, calling a WebDriver method while disconnected could raise an error, or make WebDriver automatically reconnect first.
While disconnected, if you accidentally call a WebDriver method, then <b translate="no">SeleniumBase</b> will attempt to use the <b translate="no">CDP Mode</b> version of that method (if available). For example, if you accidentally call `sb.click(selector)` instead of `sb.cdp.click(selector)`, then your WebDriver call will automatically be redirected to the <b translate="no">CDP Mode</b> version. Not all WebDriver methods have a matching <b translate="no">CDP Mode</b> method. In that scenario, calling a WebDriver method while disconnected could raise an error, or make WebDriver automatically reconnect first.

To find out if WebDriver is connected or disconnected, call:

* **`sb.is_connected()`**

<b>Note:</b> When <b translate="no">CDP Mode</b> is initialized from <b translate="no">UC Mode</b>, the WebDriver is disconnected from the browser. (The stealthy <b translate="no">CDP-Driver</b> takes over.)

--------

### 🐙 <b translate="no">CDP Mode</b> Examples ([SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode))
### 🐙 <b translate="no">CDP Mode</b> examples ([SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode))

<p><div /></p>

Expand Down
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_planetmc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from seleniumbase import SB

with SB(uc=True, test=True, locale_code="en") as sb:
with SB(uc=True, test=True) as sb:
url = "www.planetminecraft.com/account/sign_in/"
sb.activate_cdp_mode(url)
sb.sleep(2)
Expand Down
9 changes: 3 additions & 6 deletions examples/cdp_mode/raw_tiktok.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from seleniumbase import SB

with SB(
uc=True, test=True, locale_code="en", incognito=True, ad_block=True
) as sb:
with SB(uc=True, test=True, incognito=True, ad_block=True) as sb:
url = "https://www.tiktok.com/@startrek?lang=en"
sb.activate_cdp_mode(url)
sb.sleep(2.5)
sb.cdp.click('button:contains("Refresh")')
sb.cdp.click_if_visible('button:contains("Refresh")')
sb.sleep(1.5)
print(sb.cdp.get_text('h2[data-e2e="user-bio"]'))
for i in range(55):
for i in range(50):
sb.cdp.scroll_down(12)
sb.sleep(0.05)
sb.sleep(1)
2 changes: 1 addition & 1 deletion examples/presenter/uc_presentation_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ def test_presentation_3(self):
"<hr /><h6><br /></h6>"
"<p><mk-1>"
"There are different ways of stucturing SeleniumBase scripts."
' (Internally called: "The 23 Syntax Formats")'
' (Internally called: "The 25 Syntax Formats")'
"</mk-1><br /><br /><mk-2>"
'Most examples use Syntax Format 1: "BaseCase direct class'
' inheritance", which uses the "pytest" test runner.'
Expand Down
8 changes: 8 additions & 0 deletions examples/raw_google.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from seleniumbase import SB

with SB(test=True, ad_block=True, locale_code="en") as sb:
sb.open("https://google.com/ncr")
sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
sb.click('[href*="github.com/seleniumbase/SeleniumBase"]')
sb.save_screenshot_to_logs() # (See ./latest_logs folder)
print(sb.get_page_title())
2 changes: 1 addition & 1 deletion examples/test_download_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_download_files_from_pypi(self):
self.assert_text("Download files", "a#files-tab")
pkg_header = self.get_text("h1.package-header__name").strip()
pkg_name = pkg_header.replace(" ", "-")
whl_file = pkg_name + "-py2.py3-none-any.whl"
whl_file = pkg_name + "-py3-none-any.whl"
tar_gz_file = pkg_name + ".tar.gz"

# Click the links to download the files into: "./downloaded_files/"
Expand Down
20 changes: 20 additions & 0 deletions examples/test_get_swag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)


class MyTestClass(BaseCase):
def test_swag_labs(self):
self.open("https://www.saucedemo.com")
self.type("#user-name", "standard_user")
self.type("#password", "secret_sauce\n")
self.assert_element("div.inventory_list")
self.click('button[name*="backpack"]')
self.click("#shopping_cart_container a")
self.assert_text("Backpack", "div.cart_item")
self.click("button#checkout")
self.type("input#first-name", "SeleniumBase")
self.type("input#last-name", "Automation")
self.type("input#postal-code", "77123")
self.click("input#continue")
self.click("button#finish")
self.assert_text("Thank you for your order!")
8 changes: 4 additions & 4 deletions examples/translations/italian_test_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ def test_esempio_1(self):
self.apri("https://it.wikipedia.org/wiki/")
self.verificare_testo("Wikipedia")
self.verificare_elemento('a[title="Lingua italiana"]')
self.digitare("#searchInput", "Pizza")
self.fare_clic("#searchButton")
self.digitare('input[name="search"]', "Pizza")
self.fare_clic("#searchform button")
self.verificare_testo("Pizza", "#firstHeading")
self.verificare_elemento('figure img[src*="pizza"]')
self.digitare("#searchInput", "Colosseo")
self.fare_clic("#searchButton")
self.digitare('input[name="search"]', "Colosseo")
self.fare_clic("#searchform button")
self.verificare_testo("Colosseo", "#firstHeading")
self.verificare_elemento('figure img[src*="Colosseo"]')
self.indietro()
Expand Down
108 changes: 103 additions & 5 deletions help_docs/syntax_formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<a id="syntax_formats"></a>

<h2><img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="40"> The 23 Syntax Formats / Design Patterns</h2>
<h2><img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="40"> The 25 Syntax Formats / Design Patterns</h2>

<h3>🔠 SeleniumBase supports multiple ways of structuring tests:</h3>

Expand Down Expand Up @@ -32,6 +32,8 @@
<li><a href="#sb_sf_21"><strong>21. SeleniumBase SB (Python context manager)</strong></a></li>
<li><a href="#sb_sf_22"><strong>22. The driver manager (via context manager)</strong></a></li>
<li><a href="#sb_sf_23"><strong>23. The driver manager (via direct import)</strong></a></li>
<li><a href="#sb_sf_24"><strong>24. CDP driver (async/await API. No Selenium)</strong></a></li>
<li><a href="#sb_sf_25"><strong>25. CDP driver (SB-CDP sync API. No Selenium)</strong></a></li>
</ul>
</blockquote>

Expand Down Expand Up @@ -550,12 +552,12 @@ class MiaClasseDiTest(CasoDiProva):
self.apri("https://it.wikipedia.org/wiki/")
self.verificare_testo("Wikipedia")
self.verificare_elemento('a[title="Lingua italiana"]')
self.digitare("#searchInput", "Pizza")
self.fare_clic("#searchButton")
self.digitare('input[name="search"]', "Pizza")
self.fare_clic("#searchform button")
self.verificare_testo("Pizza", "#firstHeading")
self.verificare_elemento('figure img[src*="pizza"]')
self.digitare("#searchInput", "Colosseo")
self.fare_clic("#searchButton")
self.digitare('input[name="search"]', "Colosseo")
self.fare_clic("#searchform button")
self.verificare_testo("Colosseo", "#firstHeading")
self.verificare_elemento('figure img[src*="Colosseo"]')
self.indietro()
Expand Down Expand Up @@ -876,6 +878,21 @@ with SB(test=True, rtf=True, demo=True) as sb:

(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_test_scripts.py">examples/raw_test_scripts.py</a> for the test.)

Here's another example, which uses [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) from the SeleniumBase SB format:

```python
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
url = "www.planetminecraft.com/account/sign_in/"
sb.activate_cdp_mode(url)
sb.sleep(2)
sb.cdp.gui_click_element("#turnstile-widget div")
sb.sleep(2)
```

(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_planetmc.py">examples/cdp_mode/raw_planetmc.py</a> for the test.)

<a id="sb_sf_22"></a>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 22. The driver manager (via context manager)</h2>

Expand Down Expand Up @@ -994,6 +1011,87 @@ The ``Driver()`` manager format can be used as a drop-in replacement for virtual

When using the ``Driver()`` format, you may need to activate a Virtual Display on your own if you want to run headed tests in a headless Linux environment. (See https://github.com/mdmintz/sbVirtualDisplay for details.) One such example of this is using an authenticated proxy, which is configured via a Chrome extension that is generated at runtime. (Note that regular headless mode in Chrome doesn't support extensions.)

<a id="sb_sf_24"></a>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 24. CDP driver (async/await API. No Selenium)</h2>

This format provides a pure CDP way of using SeleniumBase (without Selenium or a test runner). The async/await API is used. Here's an example:

```python
import asyncio
import time
from seleniumbase.undetected import cdp_driver


async def main():
driver = await cdp_driver.cdp_util.start_async()
page = await driver.get("about:blank")
await page.set_locale("en")
await page.get("https://www.priceline.com/")
time.sleep(3)
print(await page.evaluate("document.title"))
element = await page.select('[data-testid*="endLocation"]')
await element.click_async()
time.sleep(1)
await element.send_keys_async("Boston")
time.sleep(2)

if __name__ == "__main__":
loop = asyncio.new_event_loop()
loop.run_until_complete(main())
```

(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_async.py">examples/cdp_mode/raw_async.py</a> for the test.)

<a id="sb_sf_25"></a>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> 25. CDP driver (SB-CDP sync API. No Selenium)</h2>

This format provides a pure CDP way of using SeleniumBase (without Selenium or a test runner). The expanded SB-CDP sync API is used. Here's an example:

```python
import asyncio
from seleniumbase.core import sb_cdp
from seleniumbase.undetected import cdp_driver


def main():
url0 = "about:blank" # Set Locale code from here first
url1 = "https://www.priceline.com/" # (The "real" URL)
loop = asyncio.new_event_loop()
driver = cdp_driver.cdp_util.start_sync()
page = loop.run_until_complete(driver.get(url0))
sb = sb_cdp.CDPMethods(loop, page, driver)
sb.set_locale("en") # This test expects English locale
sb.open(url1)
sb.sleep(2.5)
sb.internalize_links() # Don't open links in a new tab
sb.click("#link_header_nav_experiences")
sb.sleep(3.5)
sb.remove_elements("msm-cookie-banner")
sb.sleep(1.5)
location = "Amsterdam"
where_to = 'div[data-automation*="experiences"] input'
button = 'button[data-automation*="experiences-search"]'
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("************")
for i in range(8):
sb.scroll_down(50)
sb.sleep(0.2)
cards = sb.select_all('h2[data-automation*="product-list-card"]')
for card in cards:
print("* %s" % card.text)


if __name__ == "__main__":
main()
```

(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp.py">examples/cdp_mode/raw_cdp.py</a> for the test.)

--------

<h3 align="left"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/img/sb_logo_10.png" title="SeleniumBase" width="280" /></a></h3>

0 comments on commit 6cb8844

Please sign in to comment.