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.
Files changed (162) hide show
  1. {seleniumbase-4.47.5/seleniumbase.egg-info → seleniumbase-4.47.7}/PKG-INFO +7 -4
  2. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/README.md +6 -3
  3. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__version__.py +1 -1
  4. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkdir.py +3 -0
  5. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/browser_launcher.py +1 -0
  6. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/sb_cdp.py +49 -14
  7. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/base_case.py +31 -15
  8. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/sb_manager.py +2 -0
  9. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/element.py +27 -2
  10. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/tab.py +6 -1
  11. {seleniumbase-4.47.5 → seleniumbase-4.47.7/seleniumbase.egg-info}/PKG-INFO +7 -4
  12. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/.gitignore +0 -0
  13. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/LICENSE +0 -0
  14. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/MANIFEST.in +0 -0
  15. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/install.sh +0 -0
  16. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/pyproject.toml +0 -0
  17. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/pytest.ini +0 -0
  18. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/requirements.txt +0 -0
  19. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/__init__.py +0 -0
  20. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/__main__.py +0 -0
  21. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/sbase/steps.py +0 -0
  22. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__init__.py +0 -0
  23. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/__main__.py +0 -0
  24. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/__init__.py +0 -0
  25. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/behave_helper.py +0 -0
  26. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/behave_sb.py +0 -0
  27. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/behave/steps.py +0 -0
  28. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/__init__.py +0 -0
  29. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/decorators.py +0 -0
  30. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/encryption.py +0 -0
  31. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/exceptions.py +0 -0
  32. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/obfuscate.py +0 -0
  33. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/common/unobfuscate.py +0 -0
  34. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/__init__.py +0 -0
  35. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/ad_block_list.py +0 -0
  36. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/proxy_list.py +0 -0
  37. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/config/settings.py +0 -0
  38. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/__init__.py +0 -0
  39. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/logo_helper.py +0 -0
  40. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/rich_helper.py +0 -0
  41. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/run.py +0 -0
  42. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  43. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  44. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_commander.py +0 -0
  45. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_install.py +0 -0
  46. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  47. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
  48. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  49. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_mkrec.py +0 -0
  50. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  51. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_print.py +0 -0
  52. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  53. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/__init__.py +0 -0
  54. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/application_manager.py +0 -0
  55. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/capabilities_parser.py +0 -0
  56. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/colored_traceback.py +0 -0
  57. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/create_db_tables.sql +0 -0
  58. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/detect_b_ver.py +0 -0
  59. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/download_helper.py +0 -0
  60. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/encoded_images.py +0 -0
  61. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/jqc_helper.py +0 -0
  62. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/log_helper.py +0 -0
  63. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/mysql.py +0 -0
  64. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/proxy_helper.py +0 -0
  65. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/recorder_helper.py +0 -0
  66. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/report_helper.py +0 -0
  67. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/s3_manager.py +0 -0
  68. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/sb_driver.py +0 -0
  69. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/session_helper.py +0 -0
  70. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/settings_parser.py +0 -0
  71. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/style_sheet.py +0 -0
  72. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/testcase_manager.py +0 -0
  73. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/tour_helper.py +0 -0
  74. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/core/visual_helper.py +0 -0
  75. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/__init__.py +0 -0
  76. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  77. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  78. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  79. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  80. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  81. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  82. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  83. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/__init__.py +0 -0
  84. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/ad_block.zip +0 -0
  85. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/disable_csp.zip +0 -0
  86. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/recorder.zip +0 -0
  87. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/extensions/sbase_ext.zip +0 -0
  88. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/__init__.py +0 -0
  89. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/constants.py +0 -0
  90. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  91. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/errors.py +0 -0
  92. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/js_utils.py +0 -0
  93. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/page_actions.py +0 -0
  94. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/page_utils.py +0 -0
  95. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/shared_utils.py +0 -0
  96. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/unittest_helper.py +0 -0
  97. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/words.py +0 -0
  98. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  99. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/__init__.py +0 -0
  100. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/active_css_js.py +0 -0
  101. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/live_js.py +0 -0
  102. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/js_code/recorder_js.py +0 -0
  103. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/masterqa/__init__.py +0 -0
  104. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/masterqa/master_qa.py +0 -0
  105. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/__init__.py +0 -0
  106. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/base_plugin.py +0 -0
  107. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/basic_test_info.py +0 -0
  108. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  109. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/driver_manager.py +0 -0
  110. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/page_source.py +0 -0
  111. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/pytest_plugin.py +0 -0
  112. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  113. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/screen_shots.py +0 -0
  114. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/plugins/selenium_plugin.py +0 -0
  115. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/resources/__init__.py +0 -0
  116. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/__init__.py +0 -0
  117. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/chinese.py +0 -0
  118. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/dutch.py +0 -0
  119. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/french.py +0 -0
  120. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/italian.py +0 -0
  121. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/japanese.py +0 -0
  122. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/korean.py +0 -0
  123. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/master_dict.py +0 -0
  124. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/portuguese.py +0 -0
  125. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/russian.py +0 -0
  126. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/spanish.py +0 -0
  127. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/translate/translator.py +0 -0
  128. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/__init__.py +0 -0
  129. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp.py +0 -0
  130. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  131. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  132. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  133. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  134. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  135. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  136. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/dprocess.py +0 -0
  137. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/options.py +0 -0
  138. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/patcher.py +0 -0
  139. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/reactor.py +0 -0
  140. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/undetected/webelement.py +0 -0
  141. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/__init__.py +0 -0
  142. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  143. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  144. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  145. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  146. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  147. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  148. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  149. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  150. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  151. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  152. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  153. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  154. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  155. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/SOURCES.txt +0 -0
  156. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/dependency_links.txt +0 -0
  157. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/entry_points.txt +0 -0
  158. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/requires.txt +0 -0
  159. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/seleniumbase.egg-info/top_level.txt +0 -0
  160. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/setup.cfg +0 -0
  161. {seleniumbase-4.47.5 → seleniumbase-4.47.7}/setup.py +0 -0
  162. {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.5
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
- <p align="left">📓 Here's the stealthy architecture overview:</p>
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 overview" />
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>). This means <b>you no longer need random <span><code translate="no">time.sleep()</code></span> statements</b> in your scripts.</p>
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
- <p align="left">📓 Here's the stealthy architecture overview:</p>
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 overview" />
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>). This means <b>you no longer need random <span><code translate="no">time.sleep()</code></span> statements</b> in your scripts.</p>
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.5"
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
- element.scroll_into_view()
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
- element.scroll_into_view()
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
- element.scroll_into_view()
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
- element.scroll_into_view()
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
- element.scroll_into_view()
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
- self.__scroll_to_element(element)
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
- self.__scroll_to_element(element)
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
- self.__scroll_to_element(element)
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(self, selector, by="css selector", timeout=0):
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(selector, x, y, center=center)
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
- self.__slow_scroll_to_element(element)
14076
+ if scroll:
14077
+ self.__slow_scroll_to_element(element)
14078
+ else:
14079
+ self.sleep(0.2)
14065
14080
  else:
14066
- self.__scroll_to_element(element, selector, by)
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")).value
471
- scroll_x = (await self.tab.evaluate("window.scrollX")).value
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.5
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
- <p align="left">📓 Here's the stealthy architecture overview:</p>
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 overview" />
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>). This means <b>you no longer need random <span><code translate="no">time.sleep()</code></span> statements</b> in your scripts.</p>
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