seleniumbase 4.47.5__tar.gz → 4.47.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {seleniumbase-4.47.5/seleniumbase.egg-info → seleniumbase-4.47.7}/PKG-INFO +7 -4
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/README.md +6 -3
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__version__.py +1 -1
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkdir.py +3 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/browser_launcher.py +1 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/sb_cdp.py +49 -14
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/base_case.py +31 -15
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/sb_manager.py +2 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/element.py +27 -2
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/tab.py +6 -1
- {seleniumbase-4.47.5 → seleniumbase-4.47.7/seleniumbase.egg-info}/PKG-INFO +7 -4
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/.gitignore +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/LICENSE +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/MANIFEST.in +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/install.sh +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/pyproject.toml +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/pytest.ini +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/requirements.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/__main__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/steps.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__main__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/behave_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/behave_sb.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/steps.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/decorators.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/encryption.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/exceptions.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/obfuscate.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/unobfuscate.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/ad_block_list.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/proxy_list.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/settings.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/logo_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/rich_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/run.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_commander.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_install.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkrec.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_objectify.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_print.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_recorder.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/application_manager.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/capabilities_parser.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/colored_traceback.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/create_db_tables.sql +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/detect_b_ver.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/download_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/encoded_images.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/jqc_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/log_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/mysql.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/proxy_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/recorder_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/report_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/s3_manager.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/sb_driver.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/session_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/settings_parser.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/style_sheet.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/testcase_manager.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/tour_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/visual_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/ad_block.zip +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/disable_csp.zip +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/recorder.zip +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/sbase_ext.zip +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/constants.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/css_to_xpath.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/errors.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/js_utils.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/page_actions.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/page_utils.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/shared_utils.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/unittest_helper.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/words.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/xpath_to_css.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/active_css_js.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/live_js.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/recorder_js.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/masterqa/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/masterqa/master_qa.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/base_plugin.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/basic_test_info.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/driver_manager.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/page_source.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/pytest_plugin.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/screen_shots.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/selenium_plugin.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/resources/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/chinese.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/dutch.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/french.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/italian.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/japanese.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/korean.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/master_dict.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/portuguese.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/russian.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/spanish.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/translator.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/config.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/dprocess.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/options.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/patcher.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/reactor.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/webelement.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/font_color +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/SOURCES.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/dependency_links.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/entry_points.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/requires.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/top_level.txt +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/setup.cfg +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/setup.py +0 -0
- {seleniumbase-4.47.5 → seleniumbase-4.47.7}/virtualenv_install.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seleniumbase
|
|
3
|
-
Version: 4.47.
|
|
3
|
+
Version: 4.47.7
|
|
4
4
|
Summary: A complete web automation framework for end-to-end testing.
|
|
5
5
|
Home-page: https://github.com/seleniumbase/SeleniumBase
|
|
6
6
|
Author: Michael Mintz
|
|
@@ -262,9 +262,9 @@ Dynamic: summary
|
|
|
262
262
|
|
|
263
263
|
--------
|
|
264
264
|
|
|
265
|
-
<
|
|
265
|
+
<h3 align="left">⚙️ Stealthy architecture flowchart:</h3>
|
|
266
266
|
|
|
267
|
-
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture
|
|
267
|
+
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture flowchart" />
|
|
268
268
|
|
|
269
269
|
(For maximum stealth, use <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md">CDP Mode</a>, which includes <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md">Stealthy Playwright Mode</a>)
|
|
270
270
|
|
|
@@ -280,6 +280,7 @@ sb = sb_cdp.Chrome(url)
|
|
|
280
280
|
elements = sb.find_elements("span.titleline > a")
|
|
281
281
|
for element in elements:
|
|
282
282
|
print("* " + element.text)
|
|
283
|
+
sb.driver.stop()
|
|
283
284
|
```
|
|
284
285
|
|
|
285
286
|
--------
|
|
@@ -292,6 +293,7 @@ from seleniumbase import SB
|
|
|
292
293
|
with SB(uc=True, test=True) as sb:
|
|
293
294
|
url = "https://google.com/ncr"
|
|
294
295
|
sb.activate_cdp_mode(url)
|
|
296
|
+
sb.click_if_visible('button:contains("Accept all")')
|
|
295
297
|
sb.type('[name="q"]', "SeleniumBase GitHub page")
|
|
296
298
|
sb.click('[value="Google Search"]')
|
|
297
299
|
sb.sleep(4) # The "AI Overview" sometimes loads
|
|
@@ -691,6 +693,7 @@ class MyTestClass(BaseCase):
|
|
|
691
693
|
|
|
692
694
|
```python
|
|
693
695
|
self.open(url) # Navigate the browser window to the URL.
|
|
696
|
+
self.activate_cdp_mode() # Activate CDP Mode from UC Mode.
|
|
694
697
|
self.type(selector, text) # Update the field with the text.
|
|
695
698
|
self.click(selector) # Click the element with the selector.
|
|
696
699
|
self.click_link(link_text) # Click the link containing text.
|
|
@@ -779,7 +782,7 @@ pytest [FILE_NAME.py]::[CLASS_NAME]::[METHOD_NAME]
|
|
|
779
782
|
pynose [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME]
|
|
780
783
|
```
|
|
781
784
|
|
|
782
|
-
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>)
|
|
785
|
+
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>).</p>
|
|
783
786
|
<img src="https://img.shields.io/badge/Flaky%20Tests%3F-%20NO%21-11BBDD.svg" alt="NO MORE FLAKY TESTS!" />
|
|
784
787
|
|
|
785
788
|
✅ SeleniumBase supports all major browsers and operating systems:
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
|
|
63
63
|
--------
|
|
64
64
|
|
|
65
|
-
<
|
|
65
|
+
<h3 align="left">⚙️ Stealthy architecture flowchart:</h3>
|
|
66
66
|
|
|
67
|
-
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture
|
|
67
|
+
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture flowchart" />
|
|
68
68
|
|
|
69
69
|
(For maximum stealth, use <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md">CDP Mode</a>, which includes <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md">Stealthy Playwright Mode</a>)
|
|
70
70
|
|
|
@@ -80,6 +80,7 @@ sb = sb_cdp.Chrome(url)
|
|
|
80
80
|
elements = sb.find_elements("span.titleline > a")
|
|
81
81
|
for element in elements:
|
|
82
82
|
print("* " + element.text)
|
|
83
|
+
sb.driver.stop()
|
|
83
84
|
```
|
|
84
85
|
|
|
85
86
|
--------
|
|
@@ -92,6 +93,7 @@ from seleniumbase import SB
|
|
|
92
93
|
with SB(uc=True, test=True) as sb:
|
|
93
94
|
url = "https://google.com/ncr"
|
|
94
95
|
sb.activate_cdp_mode(url)
|
|
96
|
+
sb.click_if_visible('button:contains("Accept all")')
|
|
95
97
|
sb.type('[name="q"]', "SeleniumBase GitHub page")
|
|
96
98
|
sb.click('[value="Google Search"]')
|
|
97
99
|
sb.sleep(4) # The "AI Overview" sometimes loads
|
|
@@ -491,6 +493,7 @@ class MyTestClass(BaseCase):
|
|
|
491
493
|
|
|
492
494
|
```python
|
|
493
495
|
self.open(url) # Navigate the browser window to the URL.
|
|
496
|
+
self.activate_cdp_mode() # Activate CDP Mode from UC Mode.
|
|
494
497
|
self.type(selector, text) # Update the field with the text.
|
|
495
498
|
self.click(selector) # Click the element with the selector.
|
|
496
499
|
self.click_link(link_text) # Click the link containing text.
|
|
@@ -579,7 +582,7 @@ pytest [FILE_NAME.py]::[CLASS_NAME]::[METHOD_NAME]
|
|
|
579
582
|
pynose [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME]
|
|
580
583
|
```
|
|
581
584
|
|
|
582
|
-
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>)
|
|
585
|
+
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>).</p>
|
|
583
586
|
<img src="https://img.shields.io/badge/Flaky%20Tests%3F-%20NO%21-11BBDD.svg" alt="NO MORE FLAKY TESTS!" />
|
|
584
587
|
|
|
585
588
|
✅ SeleniumBase supports all major browsers and operating systems:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# seleniumbase package
|
|
2
|
-
__version__ = "4.47.
|
|
2
|
+
__version__ = "4.47.7"
|
|
@@ -712,6 +712,9 @@ def main():
|
|
|
712
712
|
data.append(" if not self.undetectable:")
|
|
713
713
|
data.append(" self.get_new_driver(undetectable=True)")
|
|
714
714
|
data.append(' self.open("https://google.com/ncr")')
|
|
715
|
+
data.append(
|
|
716
|
+
" sb.click_if_visible('button:contains(\"Accept all\")')"
|
|
717
|
+
)
|
|
715
718
|
data.append(' self.assert_title_contains("Google")')
|
|
716
719
|
data.append(" self.save_screenshot_to_logs()")
|
|
717
720
|
data.append(' self.type(HomePage.search_box, "github.com")')
|
|
@@ -942,6 +942,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
|
|
|
942
942
|
cdp.assert_not_equal = CDPM.assert_not_equal
|
|
943
943
|
cdp.assert_in = CDPM.assert_in
|
|
944
944
|
cdp.assert_not_in = CDPM.assert_not_in
|
|
945
|
+
cdp.js_scroll_into_view = CDPM.js_scroll_into_view
|
|
945
946
|
cdp.scroll_into_view = CDPM.scroll_into_view
|
|
946
947
|
cdp.scroll_to_y = CDPM.scroll_to_y
|
|
947
948
|
cdp.scroll_by_y = CDPM.scroll_by_y
|
|
@@ -66,6 +66,7 @@ class CDPMethods():
|
|
|
66
66
|
lambda *args, **kwargs: self.__gui_click(element, *args, **kwargs)
|
|
67
67
|
)
|
|
68
68
|
element.highlight_overlay = lambda: self.__highlight_overlay(element)
|
|
69
|
+
element.is_in_viewport = lambda: self.__is_in_viewport(element)
|
|
69
70
|
element.mouse_click = lambda: self.__mouse_click(element)
|
|
70
71
|
element.click_with_offset = (
|
|
71
72
|
lambda *args, **kwargs: self.__mouse_click_with_offset_async(
|
|
@@ -429,7 +430,7 @@ class CDPMethods():
|
|
|
429
430
|
visible_elements.append(element)
|
|
430
431
|
return visible_elements
|
|
431
432
|
|
|
432
|
-
def click_nth_element(self, selector, number):
|
|
433
|
+
def click_nth_element(self, selector, number, scroll=True):
|
|
433
434
|
elements = self.select_all(selector)
|
|
434
435
|
if len(elements) < number:
|
|
435
436
|
raise Exception(
|
|
@@ -440,10 +441,11 @@ class CDPMethods():
|
|
|
440
441
|
if number < 0:
|
|
441
442
|
number = 0
|
|
442
443
|
element = elements[number]
|
|
443
|
-
|
|
444
|
+
if scroll:
|
|
445
|
+
element.scroll_into_view()
|
|
444
446
|
element.click()
|
|
445
447
|
|
|
446
|
-
def click_nth_visible_element(self, selector, number):
|
|
448
|
+
def click_nth_visible_element(self, selector, number, scroll=True):
|
|
447
449
|
"""Finds all matching page elements and clicks the nth visible one.
|
|
448
450
|
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
|
|
449
451
|
(Clicks the 5th visible checkbox on the page.)"""
|
|
@@ -457,7 +459,8 @@ class CDPMethods():
|
|
|
457
459
|
if number < 0:
|
|
458
460
|
number = 0
|
|
459
461
|
element = elements[number]
|
|
460
|
-
|
|
462
|
+
if scroll:
|
|
463
|
+
element.scroll_into_view()
|
|
461
464
|
element.click()
|
|
462
465
|
|
|
463
466
|
def click_link(self, link_text):
|
|
@@ -541,6 +544,11 @@ class CDPMethods():
|
|
|
541
544
|
self.loop.run_until_complete(element.highlight_overlay_async())
|
|
542
545
|
)
|
|
543
546
|
|
|
547
|
+
def __is_in_viewport(self, element):
|
|
548
|
+
return (
|
|
549
|
+
self.loop.run_until_complete(element.is_in_viewport_async())
|
|
550
|
+
)
|
|
551
|
+
|
|
544
552
|
def __mouse_click(self, element):
|
|
545
553
|
result = (
|
|
546
554
|
self.loop.run_until_complete(element.mouse_click_async())
|
|
@@ -793,13 +801,14 @@ class CDPMethods():
|
|
|
793
801
|
js_code = js_code.replace("return getBestSelector", "getBestSelector")
|
|
794
802
|
return self.loop.run_until_complete(self.page.evaluate(js_code))
|
|
795
803
|
|
|
796
|
-
def click(self, selector, timeout=None):
|
|
804
|
+
def click(self, selector, timeout=None, scroll=True):
|
|
797
805
|
if not timeout:
|
|
798
806
|
timeout = settings.SMALL_TIMEOUT
|
|
799
807
|
self.__slow_mode_pause_if_set()
|
|
800
808
|
element = self.find_element(selector, timeout=timeout)
|
|
801
|
-
element.scroll_into_view()
|
|
802
809
|
tag_name = element.tag_name
|
|
810
|
+
current_url = self.get_current_url()
|
|
811
|
+
|
|
803
812
|
if tag_name:
|
|
804
813
|
tag_name = tag_name.lower().strip()
|
|
805
814
|
if (
|
|
@@ -807,12 +816,24 @@ class CDPMethods():
|
|
|
807
816
|
"a", "button", "canvas", "div", "input", "li", "span", "label"
|
|
808
817
|
]
|
|
809
818
|
and "contains(" not in selector
|
|
819
|
+
and "://google" not in current_url
|
|
820
|
+
and "://www.google" not in current_url
|
|
810
821
|
):
|
|
822
|
+
if scroll:
|
|
823
|
+
element.scroll_into_view()
|
|
811
824
|
try:
|
|
812
825
|
element.mouse_click() # Simulated click (NOT PyAutoGUI)
|
|
813
826
|
except Exception:
|
|
814
827
|
element.click() # Standard CDP click
|
|
815
828
|
else:
|
|
829
|
+
if scroll:
|
|
830
|
+
if "contains(" in selector:
|
|
831
|
+
element.scroll_into_view()
|
|
832
|
+
else:
|
|
833
|
+
try:
|
|
834
|
+
self.js_scroll_into_view(selector)
|
|
835
|
+
except Exception:
|
|
836
|
+
element.scroll_into_view()
|
|
816
837
|
element.click() # Standard CDP click
|
|
817
838
|
self.__slow_mode_pause_if_set()
|
|
818
839
|
self.loop.run_until_complete(self.page.wait(0.2))
|
|
@@ -824,10 +845,10 @@ class CDPMethods():
|
|
|
824
845
|
self.__slow_mode_pause_if_set()
|
|
825
846
|
self.loop.run_until_complete(self.page.wait(0.2))
|
|
826
847
|
|
|
827
|
-
def click_if_visible(self, selector, timeout=0):
|
|
848
|
+
def click_if_visible(self, selector, timeout=0, scroll=True):
|
|
828
849
|
if self.is_element_visible(selector):
|
|
829
850
|
with suppress(Exception):
|
|
830
|
-
self.click(selector, timeout=1)
|
|
851
|
+
self.click(selector, timeout=1, scroll=scroll)
|
|
831
852
|
elif timeout == 0:
|
|
832
853
|
return
|
|
833
854
|
else:
|
|
@@ -836,7 +857,7 @@ class CDPMethods():
|
|
|
836
857
|
if self.is_element_visible(selector):
|
|
837
858
|
self.click(selector, timeout=1)
|
|
838
859
|
|
|
839
|
-
def click_visible_elements(self, selector, limit=0):
|
|
860
|
+
def click_visible_elements(self, selector, limit=0, scroll=True):
|
|
840
861
|
"""Finds all matching page elements and clicks visible ones in order.
|
|
841
862
|
If a click reloads or opens a new page, the clicking will stop.
|
|
842
863
|
If no matching elements appear, an Exception will be raised.
|
|
@@ -859,7 +880,8 @@ class CDPMethods():
|
|
|
859
880
|
except Exception:
|
|
860
881
|
continue
|
|
861
882
|
if (width != 0 or height != 0):
|
|
862
|
-
|
|
883
|
+
if scroll:
|
|
884
|
+
element.scroll_into_view()
|
|
863
885
|
element.click()
|
|
864
886
|
click_count += 1
|
|
865
887
|
time.sleep(0.044)
|
|
@@ -868,13 +890,14 @@ class CDPMethods():
|
|
|
868
890
|
except Exception:
|
|
869
891
|
break
|
|
870
892
|
|
|
871
|
-
def mouse_click(self, selector, timeout=None):
|
|
893
|
+
def mouse_click(self, selector, timeout=None, scroll=True):
|
|
872
894
|
"""(Attempt simulating a mouse click)"""
|
|
873
895
|
if not timeout:
|
|
874
896
|
timeout = settings.SMALL_TIMEOUT
|
|
875
897
|
self.__slow_mode_pause_if_set()
|
|
876
898
|
element = self.find_element(selector, timeout=timeout)
|
|
877
|
-
|
|
899
|
+
if scroll:
|
|
900
|
+
element.scroll_into_view()
|
|
878
901
|
element.mouse_click()
|
|
879
902
|
self.__slow_mode_pause_if_set()
|
|
880
903
|
self.loop.run_until_complete(self.page.wait(0.2))
|
|
@@ -1970,9 +1993,10 @@ class CDPMethods():
|
|
|
1970
1993
|
py = element_rect["y"]
|
|
1971
1994
|
self.gui_click_x_y(px + x, py + y, timeframe=timeframe)
|
|
1972
1995
|
|
|
1973
|
-
def click_with_offset(self, selector, x, y, center=False):
|
|
1996
|
+
def click_with_offset(self, selector, x, y, center=False, scroll=True):
|
|
1974
1997
|
element = self.find_element(selector)
|
|
1975
|
-
|
|
1998
|
+
if scroll:
|
|
1999
|
+
element.scroll_into_view()
|
|
1976
2000
|
if "--debug" in sys.argv:
|
|
1977
2001
|
displayed_selector = "`%s`" % selector
|
|
1978
2002
|
if '"' not in selector:
|
|
@@ -3103,6 +3127,17 @@ class CDPMethods():
|
|
|
3103
3127
|
if first in second:
|
|
3104
3128
|
raise AssertionError("%s is in %s" % (first, second))
|
|
3105
3129
|
|
|
3130
|
+
def js_scroll_into_view(self, selector):
|
|
3131
|
+
css_selector = self.__convert_to_css_if_xpath(selector)
|
|
3132
|
+
css_selector = re.escape(css_selector) # Add "\\" to special chars
|
|
3133
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
|
3134
|
+
js_code = (
|
|
3135
|
+
"document.querySelector('%s')?.scrollIntoView();"
|
|
3136
|
+
% css_selector
|
|
3137
|
+
)
|
|
3138
|
+
with suppress(Exception):
|
|
3139
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
|
3140
|
+
|
|
3106
3141
|
def scroll_into_view(self, selector):
|
|
3107
3142
|
self.find_element(selector).scroll_into_view()
|
|
3108
3143
|
self.loop.run_until_complete(self.page.wait(0.1))
|
|
@@ -411,7 +411,7 @@ class BaseCase(unittest.TestCase):
|
|
|
411
411
|
original_by = by
|
|
412
412
|
selector, by = self.__recalculate_selector(selector, by)
|
|
413
413
|
if self.__is_cdp_swap_needed():
|
|
414
|
-
self.cdp.click(selector, timeout=timeout)
|
|
414
|
+
self.cdp.click(selector, timeout=timeout, scroll=scroll)
|
|
415
415
|
return
|
|
416
416
|
if delay and (type(delay) in [int, float]) and delay > 0:
|
|
417
417
|
time.sleep(delay)
|
|
@@ -2254,7 +2254,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2254
2254
|
)
|
|
2255
2255
|
|
|
2256
2256
|
def click_visible_elements(
|
|
2257
|
-
self, selector, by="css selector", limit=0, timeout=None
|
|
2257
|
+
self, selector, by="css selector", limit=0, timeout=None, scroll=True
|
|
2258
2258
|
):
|
|
2259
2259
|
"""Finds all matching page elements and clicks visible ones in order.
|
|
2260
2260
|
If a click reloads or opens a new page, the clicking will stop.
|
|
@@ -2270,7 +2270,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2270
2270
|
timeout = self.__get_new_timeout(timeout)
|
|
2271
2271
|
selector, by = self.__recalculate_selector(selector, by)
|
|
2272
2272
|
if self.__is_cdp_swap_needed():
|
|
2273
|
-
self.cdp.click_visible_elements(selector, limit)
|
|
2273
|
+
self.cdp.click_visible_elements(selector, limit, scroll=scroll)
|
|
2274
2274
|
return
|
|
2275
2275
|
self.wait_for_ready_state_complete()
|
|
2276
2276
|
if self.__needs_minimum_wait():
|
|
@@ -2297,7 +2297,8 @@ class BaseCase(unittest.TestCase):
|
|
|
2297
2297
|
return
|
|
2298
2298
|
try:
|
|
2299
2299
|
if element.is_displayed():
|
|
2300
|
-
|
|
2300
|
+
if scroll:
|
|
2301
|
+
self.__scroll_to_element(element)
|
|
2301
2302
|
if self.browser == "safari":
|
|
2302
2303
|
self.execute_script("arguments[0].click();", element)
|
|
2303
2304
|
else:
|
|
@@ -2311,7 +2312,8 @@ class BaseCase(unittest.TestCase):
|
|
|
2311
2312
|
time.sleep(0.12)
|
|
2312
2313
|
try:
|
|
2313
2314
|
if element.is_displayed():
|
|
2314
|
-
|
|
2315
|
+
if scroll:
|
|
2316
|
+
self.__scroll_to_element(element)
|
|
2315
2317
|
if self.browser == "safari":
|
|
2316
2318
|
self.execute_script(
|
|
2317
2319
|
"arguments[0].click();", element
|
|
@@ -2348,7 +2350,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2348
2350
|
self.__switch_to_newest_window_if_not_blank()
|
|
2349
2351
|
|
|
2350
2352
|
def click_nth_visible_element(
|
|
2351
|
-
self, selector, number, by="css selector", timeout=None
|
|
2353
|
+
self, selector, number, by="css selector", timeout=None, scroll=True
|
|
2352
2354
|
):
|
|
2353
2355
|
"""Finds all matching page elements and clicks the nth visible one.
|
|
2354
2356
|
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
|
|
@@ -2360,7 +2362,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2360
2362
|
timeout = self.__get_new_timeout(timeout)
|
|
2361
2363
|
selector, by = self.__recalculate_selector(selector, by)
|
|
2362
2364
|
if self.__is_cdp_swap_needed():
|
|
2363
|
-
self.cdp.click_nth_visible_element(selector, number)
|
|
2365
|
+
self.cdp.click_nth_visible_element(selector, number, scroll=scroll)
|
|
2364
2366
|
return
|
|
2365
2367
|
self.wait_for_ready_state_complete()
|
|
2366
2368
|
self.wait_for_element_present(selector, by=by, timeout=timeout)
|
|
@@ -2379,7 +2381,8 @@ class BaseCase(unittest.TestCase):
|
|
|
2379
2381
|
pre_action_url = self.driver.current_url
|
|
2380
2382
|
pre_window_count = len(self.driver.window_handles)
|
|
2381
2383
|
try:
|
|
2382
|
-
|
|
2384
|
+
if scroll:
|
|
2385
|
+
self.__scroll_to_element(element)
|
|
2383
2386
|
self.__element_click(element)
|
|
2384
2387
|
except (Stale_Exception, ENI_Exception, ECI_Exception):
|
|
2385
2388
|
time.sleep(0.12)
|
|
@@ -2409,18 +2412,20 @@ class BaseCase(unittest.TestCase):
|
|
|
2409
2412
|
):
|
|
2410
2413
|
self.__switch_to_newest_window_if_not_blank()
|
|
2411
2414
|
|
|
2412
|
-
def click_if_visible(
|
|
2415
|
+
def click_if_visible(
|
|
2416
|
+
self, selector, by="css selector", timeout=0, scroll=True
|
|
2417
|
+
):
|
|
2413
2418
|
"""If the page selector exists and is visible, clicks on the element.
|
|
2414
2419
|
This method only clicks on the first matching element found.
|
|
2415
2420
|
Use click_visible_elements() to click all matching elements.
|
|
2416
2421
|
If a "timeout" is provided, waits that long for the element
|
|
2417
2422
|
to appear before giving up and returning without a click()."""
|
|
2418
2423
|
if self.__is_cdp_swap_needed():
|
|
2419
|
-
self.cdp.click_if_visible(selector, timeout=timeout)
|
|
2424
|
+
self.cdp.click_if_visible(selector, timeout=timeout, scroll=scroll)
|
|
2420
2425
|
return
|
|
2421
2426
|
self.wait_for_ready_state_complete()
|
|
2422
2427
|
if self.is_element_visible(selector, by=by):
|
|
2423
|
-
self.click(selector, by=by)
|
|
2428
|
+
self.click(selector, by=by, scroll=scroll)
|
|
2424
2429
|
elif timeout > 0:
|
|
2425
2430
|
with suppress(Exception):
|
|
2426
2431
|
self.wait_for_element_visible(
|
|
@@ -2428,7 +2433,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2428
2433
|
)
|
|
2429
2434
|
self.sleep(0.2)
|
|
2430
2435
|
if self.is_element_visible(selector, by=by):
|
|
2431
|
-
self.click(selector, by=by)
|
|
2436
|
+
self.click(selector, by=by, scroll=scroll)
|
|
2432
2437
|
|
|
2433
2438
|
def click_active_element(self):
|
|
2434
2439
|
if self.__is_cdp_swap_needed():
|
|
@@ -2484,6 +2489,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2484
2489
|
mark=None,
|
|
2485
2490
|
timeout=None,
|
|
2486
2491
|
center=None,
|
|
2492
|
+
scroll=True,
|
|
2487
2493
|
):
|
|
2488
2494
|
"""Click an element at an {X,Y}-offset location.
|
|
2489
2495
|
{0,0} is the top-left corner of the element.
|
|
@@ -2500,6 +2506,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2500
2506
|
mark=mark,
|
|
2501
2507
|
timeout=timeout,
|
|
2502
2508
|
center=center,
|
|
2509
|
+
scroll=scroll,
|
|
2503
2510
|
)
|
|
2504
2511
|
|
|
2505
2512
|
def double_click_with_offset(
|
|
@@ -2511,6 +2518,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2511
2518
|
mark=None,
|
|
2512
2519
|
timeout=None,
|
|
2513
2520
|
center=None,
|
|
2521
|
+
scroll=True,
|
|
2514
2522
|
):
|
|
2515
2523
|
"""Double click an element at an {X,Y}-offset location.
|
|
2516
2524
|
{0,0} is the top-left corner of the element.
|
|
@@ -2527,6 +2535,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2527
2535
|
mark=mark,
|
|
2528
2536
|
timeout=timeout,
|
|
2529
2537
|
center=center,
|
|
2538
|
+
scroll=scroll,
|
|
2530
2539
|
)
|
|
2531
2540
|
|
|
2532
2541
|
def is_checked(self, selector, by="css selector", timeout=None):
|
|
@@ -14043,9 +14052,12 @@ class BaseCase(unittest.TestCase):
|
|
|
14043
14052
|
mark=None,
|
|
14044
14053
|
timeout=None,
|
|
14045
14054
|
center=None,
|
|
14055
|
+
scroll=True,
|
|
14046
14056
|
):
|
|
14047
14057
|
if self.__is_cdp_swap_needed():
|
|
14048
|
-
self.cdp.click_with_offset(
|
|
14058
|
+
self.cdp.click_with_offset(
|
|
14059
|
+
selector, x, y, center=center, scroll=scroll
|
|
14060
|
+
)
|
|
14049
14061
|
return
|
|
14050
14062
|
self.wait_for_ready_state_complete()
|
|
14051
14063
|
if self.__needs_minimum_wait():
|
|
@@ -14061,9 +14073,13 @@ class BaseCase(unittest.TestCase):
|
|
|
14061
14073
|
if self.demo_mode:
|
|
14062
14074
|
self.__highlight(selector, by=by, loops=1)
|
|
14063
14075
|
elif self.slow_mode:
|
|
14064
|
-
|
|
14076
|
+
if scroll:
|
|
14077
|
+
self.__slow_scroll_to_element(element)
|
|
14078
|
+
else:
|
|
14079
|
+
self.sleep(0.2)
|
|
14065
14080
|
else:
|
|
14066
|
-
|
|
14081
|
+
if scroll:
|
|
14082
|
+
self.__scroll_to_element(element, selector, by)
|
|
14067
14083
|
self.wait_for_ready_state_complete()
|
|
14068
14084
|
if self.__needs_minimum_wait():
|
|
14069
14085
|
time.sleep(0.03)
|
|
@@ -13,6 +13,7 @@ from seleniumbase import SB
|
|
|
13
13
|
with SB(uc=True, test=True) as sb:
|
|
14
14
|
url = "https://google.com/ncr"
|
|
15
15
|
sb.activate_cdp_mode(url)
|
|
16
|
+
sb.click_if_visible('button:contains("Accept all")')
|
|
16
17
|
sb.type('[name="q"]', "SeleniumBase GitHub page")
|
|
17
18
|
sb.click('[value="Google Search"]')
|
|
18
19
|
sb.sleep(2)
|
|
@@ -148,6 +149,7 @@ def SB(
|
|
|
148
149
|
with SB(uc=True, test=True) as sb:
|
|
149
150
|
url = "https://google.com/ncr"
|
|
150
151
|
sb.activate_cdp_mode(url)
|
|
152
|
+
sb.click_if_visible('button:contains("Accept all")')
|
|
151
153
|
sb.type('[name="q"]', "SeleniumBase GitHub page")
|
|
152
154
|
sb.click('[value="Google Search"]')
|
|
153
155
|
sb.sleep(2)
|
|
@@ -467,8 +467,8 @@ class Element:
|
|
|
467
467
|
raise Exception("Could not find position for %s " % self)
|
|
468
468
|
pos = Position(quads[0])
|
|
469
469
|
if abs:
|
|
470
|
-
scroll_y = (await self.tab.evaluate("window.scrollY"))
|
|
471
|
-
scroll_x = (await self.tab.evaluate("window.scrollX"))
|
|
470
|
+
scroll_y = (await self.tab.evaluate("window.scrollY"))
|
|
471
|
+
scroll_x = (await self.tab.evaluate("window.scrollX"))
|
|
472
472
|
abs_x = pos.left + scroll_x + (pos.width / 2)
|
|
473
473
|
abs_y = pos.top + scroll_y + (pos.height / 2)
|
|
474
474
|
pos.abs_x = abs_x
|
|
@@ -717,6 +717,31 @@ class Element:
|
|
|
717
717
|
)
|
|
718
718
|
)
|
|
719
719
|
|
|
720
|
+
async def is_in_viewport_async(self):
|
|
721
|
+
try:
|
|
722
|
+
layout = await self.tab.send(
|
|
723
|
+
cdp.page.get_layout_metrics()
|
|
724
|
+
)
|
|
725
|
+
viewport = layout[4]
|
|
726
|
+
v_left = viewport.page_x
|
|
727
|
+
v_top = viewport.page_y
|
|
728
|
+
v_right = v_left + viewport.client_width
|
|
729
|
+
v_bottom = v_top + viewport.client_height
|
|
730
|
+
box = await self.tab.send(
|
|
731
|
+
cdp.dom.get_box_model(backend_node_id=self.backend_node_id)
|
|
732
|
+
)
|
|
733
|
+
quad = box.content
|
|
734
|
+
e_left = min(quad[0], quad[2], quad[4], quad[6])
|
|
735
|
+
e_top = min(quad[1], quad[3], quad[5], quad[7])
|
|
736
|
+
e_right = max(quad[0], quad[2], quad[4], quad[6])
|
|
737
|
+
e_bottom = max(quad[1], quad[3], quad[5], quad[7])
|
|
738
|
+
return (
|
|
739
|
+
e_left >= v_left and e_right <= v_right
|
|
740
|
+
and e_top >= v_top and e_bottom <= v_bottom
|
|
741
|
+
)
|
|
742
|
+
except Exception:
|
|
743
|
+
return False
|
|
744
|
+
|
|
720
745
|
async def scroll_into_view_async(self):
|
|
721
746
|
"""Scrolls element into view."""
|
|
722
747
|
try:
|
|
@@ -226,7 +226,6 @@ class Tab(Connection):
|
|
|
226
226
|
except (Exception, TypeError):
|
|
227
227
|
pass
|
|
228
228
|
while not item:
|
|
229
|
-
await self
|
|
230
229
|
item = await self.find_element_by_text(
|
|
231
230
|
text, best_match, return_enclosing_element
|
|
232
231
|
)
|
|
@@ -1629,11 +1628,17 @@ class Tab(Connection):
|
|
|
1629
1628
|
await element.click_async()
|
|
1630
1629
|
|
|
1631
1630
|
async def click_with_offset(self, selector, x, y, center=False, timeout=5):
|
|
1631
|
+
"""Click an element at an {X,Y}-offset location.
|
|
1632
|
+
{0,0} is the top-left corner of the element.
|
|
1633
|
+
This method is used to click on CAPTCHAs."""
|
|
1632
1634
|
element = await self.find(selector, timeout=timeout)
|
|
1633
1635
|
await element.scroll_into_view_async()
|
|
1634
1636
|
await element.mouse_click_with_offset_async(x=x, y=y, center=center)
|
|
1635
1637
|
|
|
1636
1638
|
async def solve_captcha(self):
|
|
1639
|
+
"""This method does a few things to click a CAPTCHA:
|
|
1640
|
+
1. Checks to see if a CAPTCHA is on the current page.
|
|
1641
|
+
2. If found, calls `click_with_offset(*)` to click it."""
|
|
1637
1642
|
await self.sleep(0.11)
|
|
1638
1643
|
source = await self.get_html()
|
|
1639
1644
|
if await self.__on_a_cf_turnstile_page(source):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seleniumbase
|
|
3
|
-
Version: 4.47.
|
|
3
|
+
Version: 4.47.7
|
|
4
4
|
Summary: A complete web automation framework for end-to-end testing.
|
|
5
5
|
Home-page: https://github.com/seleniumbase/SeleniumBase
|
|
6
6
|
Author: Michael Mintz
|
|
@@ -262,9 +262,9 @@ Dynamic: summary
|
|
|
262
262
|
|
|
263
263
|
--------
|
|
264
264
|
|
|
265
|
-
<
|
|
265
|
+
<h3 align="left">⚙️ Stealthy architecture flowchart:</h3>
|
|
266
266
|
|
|
267
|
-
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture
|
|
267
|
+
<img src="https://seleniumbase.github.io/other/sb_stealth.png" width="596" alt="Stealthy architecture flowchart" />
|
|
268
268
|
|
|
269
269
|
(For maximum stealth, use <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md">CDP Mode</a>, which includes <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md">Stealthy Playwright Mode</a>)
|
|
270
270
|
|
|
@@ -280,6 +280,7 @@ sb = sb_cdp.Chrome(url)
|
|
|
280
280
|
elements = sb.find_elements("span.titleline > a")
|
|
281
281
|
for element in elements:
|
|
282
282
|
print("* " + element.text)
|
|
283
|
+
sb.driver.stop()
|
|
283
284
|
```
|
|
284
285
|
|
|
285
286
|
--------
|
|
@@ -292,6 +293,7 @@ from seleniumbase import SB
|
|
|
292
293
|
with SB(uc=True, test=True) as sb:
|
|
293
294
|
url = "https://google.com/ncr"
|
|
294
295
|
sb.activate_cdp_mode(url)
|
|
296
|
+
sb.click_if_visible('button:contains("Accept all")')
|
|
295
297
|
sb.type('[name="q"]', "SeleniumBase GitHub page")
|
|
296
298
|
sb.click('[value="Google Search"]')
|
|
297
299
|
sb.sleep(4) # The "AI Overview" sometimes loads
|
|
@@ -691,6 +693,7 @@ class MyTestClass(BaseCase):
|
|
|
691
693
|
|
|
692
694
|
```python
|
|
693
695
|
self.open(url) # Navigate the browser window to the URL.
|
|
696
|
+
self.activate_cdp_mode() # Activate CDP Mode from UC Mode.
|
|
694
697
|
self.type(selector, text) # Update the field with the text.
|
|
695
698
|
self.click(selector) # Click the element with the selector.
|
|
696
699
|
self.click_link(link_text) # Click the link containing text.
|
|
@@ -779,7 +782,7 @@ pytest [FILE_NAME.py]::[CLASS_NAME]::[METHOD_NAME]
|
|
|
779
782
|
pynose [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME]
|
|
780
783
|
```
|
|
781
784
|
|
|
782
|
-
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>)
|
|
785
|
+
<p>✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (<i>up to a timeout limit</i>).</p>
|
|
783
786
|
<img src="https://img.shields.io/badge/Flaky%20Tests%3F-%20NO%21-11BBDD.svg" alt="NO MORE FLAKY TESTS!" />
|
|
784
787
|
|
|
785
788
|
✅ SeleniumBase supports all major browsers and operating systems:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|