Skip to content

Commit 8c11b73

Browse files
committed
Update the documentation
1 parent 5308238 commit 8c11b73

File tree

3 files changed

+106
-44
lines changed

3 files changed

+106
-44
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/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()

help_docs/syntax_formats.md

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ class MyTests(BaseTestCase):
119119
The pytest framework comes with a unique system called fixtures, which replaces import statements at the top of Python files by importing libraries directly into test definitions. More than just being an import, a pytest fixture can also automatically call predefined <code translate="no">setUp()</code> and <code translate="no">tearDown()</code> methods at the beginning and end of test methods. To work, <code translate="no">sb</code> is added as an argument to each test method definition that needs SeleniumBase functionality. This means you no longer need import statements in your Python files to use SeleniumBase. <b>If using other pytest fixtures in your tests, you may need to use the SeleniumBase fixture (instead of <code translate="no">BaseCase</code> class inheritance) for compatibility reasons.</b> Here's an example of the <code translate="no">sb</code> fixture in a test that does not use Python classes:
120120

121121
```python
122-
def test_sb_fixture_with_no_class(sb):
122+
from seleniumbase import BaseCase
123+
BaseCase.main(__name__, __file__)
124+
125+
def test_sb_fixture_with_no_class(sb: BaseCase):
123126
sb.open("seleniumbase.io/help_docs/install/")
124127
sb.type('input[aria-label="Search"]', "GUI Commander")
125128
sb.click('mark:contains("Commander")')
@@ -134,8 +137,11 @@ def test_sb_fixture_with_no_class(sb):
134137
The <code translate="no">sb</code> pytest fixture can also be used inside of a class. There is a slight change to the syntax because that means test methods must also include <code translate="no">self</code> in their argument definitions when test methods are defined. (The <code translate="no">self</code> argument represents the class object, and is used in every test method that lives inside of a class.) Once again, no import statements are needed in your Python files for this to work. Here's an example of using the <code translate="no">sb</code> fixture in a test method that lives inside of a Python class:
135138

136139
```python
140+
from seleniumbase import BaseCase
141+
BaseCase.main(__name__, __file__)
142+
137143
class Test_SB_Fixture:
138-
def test_sb_fixture_inside_class(self, sb):
144+
def test_sb_fixture_inside_class(self, sb: BaseCase):
139145
sb.open("seleniumbase.io/help_docs/install/")
140146
sb.type('input[aria-label="Search"]', "GUI Commander")
141147
sb.click('mark:contains("Commander")')
@@ -154,7 +160,7 @@ from seleniumbase import BaseCase
154160
BaseCase.main(__name__, __file__)
155161

156162
class LoginPage:
157-
def login_to_swag_labs(self, sb, username):
163+
def login_to_swag_labs(self, sb: BaseCase, username):
158164
sb.open("https://www.saucedemo.com")
159165
sb.type("#user-name", username)
160166
sb.type("#password", "secret_sauce")
@@ -175,18 +181,23 @@ class MyTests(BaseCase):
175181
This is similar to the classic Page Object Model with <code translate="no">BaseCase</code> inheritance, except that this time we pass the <code translate="no">sb</code> pytest fixture from the test into the <code translate="no">sb</code> arg of the page object class method, (instead of passing <code translate="no">self</code>). Now that you're using <code translate="no">sb</code> as a pytest fixture, you no longer need to import <code translate="no">BaseCase</code> anywhere in your code. See the example below:
176182

177183
```python
184+
from seleniumbase import BaseCase
185+
BaseCase.main(__name__, __file__)
186+
178187
class LoginPage:
179-
def login_to_swag_labs(self, sb, username):
188+
def login_to_swag_labs(self, sb: BaseCase, username):
180189
sb.open("https://www.saucedemo.com")
181190
sb.type("#user-name", username)
182191
sb.type("#password", "secret_sauce")
183192
sb.click('input[type="submit"]')
184193

185194
class MyTests:
186-
def test_swag_labs_login(self, sb):
195+
def test_swag_labs_login(self, sb: BaseCase):
187196
LoginPage().login_to_swag_labs(sb, "standard_user")
188197
sb.assert_element("div.inventory_list")
189198
sb.assert_element('div:contains("Sauce Labs Backpack")')
199+
sb.js_click("a#logout_sidebar_link")
200+
sb.assert_element("div#login_button_container")
190201
```
191202

192203
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/samples/sb_swag_test.py">examples/boilerplates/samples/sb_swag_test.py</a> for the full test.)
@@ -197,8 +208,11 @@ class MyTests:
197208
The pytest <code translate="no">request</code> fixture can be used to retrieve other pytest fixtures from within tests, such as the <code translate="no">sb</code> fixture. This allows you to have more control over when fixtures get initialized because the fixture no longer needs to be loaded at the very beginning of test methods. This is done by calling <code translate="no">request.getfixturevalue('sb')</code> from the test. Here's an example of using the pytest <code translate="no">request</code> fixture to load the <code translate="no">sb</code> fixture in a test method that does not use Python classes:
198209

199210
```python
211+
from seleniumbase import BaseCase
212+
BaseCase.main(__name__, __file__)
213+
200214
def test_request_sb_fixture(request):
201-
sb = request.getfixturevalue('sb')
215+
sb: BaseCase = request.getfixturevalue("sb")
202216
sb.open("https://seleniumbase.io/demo_page")
203217
sb.assert_text("SeleniumBase", "#myForm h2")
204218
sb.assert_element("input#myTextInput")
@@ -215,9 +229,12 @@ def test_request_sb_fixture(request):
215229
The pytest <code translate="no">request</code> fixture can also be used to get the <code translate="no">sb</code> fixture from inside a Python class. Here's an example of that:
216230

217231
```python
232+
from seleniumbase import BaseCase
233+
BaseCase.main(__name__, __file__)
234+
218235
class Test_Request_Fixture:
219236
def test_request_sb_fixture_in_class(self, request):
220-
sb = request.getfixturevalue('sb')
237+
sb: BaseCase = request.getfixturevalue("sb")
221238
sb.open("https://seleniumbase.io/demo_page")
222239
sb.assert_element("input#myTextInput")
223240
sb.type("#myTextarea", "Automated")
@@ -272,7 +289,6 @@ from seleniumbase import BaseCase
272289
from seleniumwire import webdriver # Requires "pip install selenium-wire"
273290
BaseCase.main(__name__, __file__)
274291

275-
276292
class WireTestCase(BaseCase):
277293
def get_new_driver(self, *args, **kwargs):
278294
options = webdriver.ChromeOptions()
@@ -298,6 +314,8 @@ When you want to use SeleniumBase methods via the ``sb`` pytest fixture, but you
298314
```python
299315
"""Overriding the "sb" fixture to override the driver."""
300316
import pytest
317+
from seleniumbase import BaseCase
318+
BaseCase.main(__name__, __file__)
301319

302320
@pytest.fixture()
303321
def sb(request):
@@ -356,12 +374,12 @@ def sb(request):
356374
sb.tearDown()
357375
sb._needs_tearDown = False
358376

359-
def test_override_fixture_no_class(sb):
377+
def test_override_fixture_no_class(sb: BaseCase):
360378
sb.open("https://seleniumbase.io/demo_page")
361379
sb.type("#myTextInput", "This is Automated")
362380

363381
class TestOverride:
364-
def test_override_fixture_inside_class(self, sb):
382+
def test_override_fixture_inside_class(self, sb: BaseCase):
365383
sb.open("https://seleniumbase.io/demo_page")
366384
sb.type("#myTextInput", "This is Automated")
367385
```
@@ -446,7 +464,6 @@ This format is similar to the English version with <code translate="no">BaseCase
446464
from seleniumbase.translate.chinese import 硒测试用例
447465
硒测试用例.main(__name__, __file__)
448466

449-
450467
class 我的测试类(硒测试用例):
451468
def test_例子1(self):
452469
self.开启("https://zh.wikipedia.org/wiki/")
@@ -483,7 +500,6 @@ This format is similar to the English version with <code translate="no">BaseCase
483500
from seleniumbase.translate.dutch import Testgeval
484501
Testgeval.main(__name__, __file__)
485502

486-
487503
class MijnTestklasse(Testgeval):
488504
def test_voorbeeld_1(self):
489505
self.openen("https://nl.wikipedia.org/wiki/Hoofdpagina")
@@ -514,7 +530,6 @@ This format is similar to the English version with <code translate="no">BaseCase
514530
from seleniumbase.translate.french import CasDeBase
515531
CasDeBase.main(__name__, __file__)
516532

517-
518533
class MaClasseDeTest(CasDeBase):
519534
def test_exemple_1(self):
520535
self.ouvrir("https://fr.wikipedia.org/wiki/")
@@ -546,7 +561,6 @@ This format is similar to the English version with <code translate="no">BaseCase
546561
from seleniumbase.translate.italian import CasoDiProva
547562
CasoDiProva.main(__name__, __file__)
548563

549-
550564
class MiaClasseDiTest(CasoDiProva):
551565
def test_esempio_1(self):
552566
self.apri("https://it.wikipedia.org/wiki/")
@@ -577,7 +591,6 @@ This format is similar to the English version with <code translate="no">BaseCase
577591
from seleniumbase.translate.japanese import セレニウムテストケース
578592
セレニウムテストケース.main(__name__, __file__)
579593

580-
581594
class 私のテストクラス(セレニウムテストケース):
582595
def test_例1(self):
583596
self.を開く("https://ja.wikipedia.org/wiki/")
@@ -609,7 +622,6 @@ This format is similar to the English version with <code translate="no">BaseCase
609622
from seleniumbase.translate.korean import 셀레늄_테스트_케이스
610623
셀레늄_테스트_케이스.main(__name__, __file__)
611624

612-
613625
class 테스트_클래스(셀레늄_테스트_케이스):
614626
def test_실시예_1(self):
615627
self.열기("https://ko.wikipedia.org/wiki/")
@@ -639,7 +651,6 @@ This format is similar to the English version with <code translate="no">BaseCase
639651
from seleniumbase.translate.portuguese import CasoDeTeste
640652
CasoDeTeste.main(__name__, __file__)
641653

642-
643654
class MinhaClasseDeTeste(CasoDeTeste):
644655
def test_exemplo_1(self):
645656
self.abrir("https://pt.wikipedia.org/wiki/")
@@ -673,7 +684,6 @@ This format is similar to the English version with <code translate="no">BaseCase
673684
from seleniumbase.translate.russian import ТестНаСелен
674685
ТестНаСелен.main(__name__, __file__)
675686

676-
677687
class МойТестовыйКласс(ТестНаСелен):
678688
def test_пример_1(self):
679689
self.открыть("https://ru.wikipedia.org/wiki/")
@@ -704,7 +714,6 @@ This format is similar to the English version with <code translate="no">BaseCase
704714
from seleniumbase.translate.spanish import CasoDePrueba
705715
CasoDePrueba.main(__name__, __file__)
706716

707-
708717
class MiClaseDePrueba(CasoDePrueba):
709718
def test_ejemplo_1(self):
710719
self.abrir("https://es.wikipedia.org/wiki/")

0 commit comments

Comments
 (0)