seleniumbase 4.41.6__tar.gz → 4.41.8__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 (157) hide show
  1. {seleniumbase-4.41.6/seleniumbase.egg-info → seleniumbase-4.41.8}/PKG-INFO +39 -14
  2. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/README.md +36 -11
  3. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/sbase/steps.py +9 -0
  4. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/__version__.py +1 -1
  5. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/behave/behave_helper.py +2 -0
  6. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/decorators.py +3 -1
  7. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/browser_launcher.py +2 -0
  8. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/recorder_helper.py +3 -0
  9. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/sb_cdp.py +10 -0
  10. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/base_case.py +50 -1
  11. {seleniumbase-4.41.6 → seleniumbase-4.41.8/seleniumbase.egg-info}/PKG-INFO +39 -14
  12. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase.egg-info/requires.txt +2 -2
  13. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/setup.py +2 -2
  14. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/.gitignore +0 -0
  15. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/LICENSE +0 -0
  16. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/MANIFEST.in +0 -0
  17. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/install.sh +0 -0
  18. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/pyproject.toml +0 -0
  19. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/pytest.ini +0 -0
  20. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/requirements.txt +0 -0
  21. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/sbase/__init__.py +0 -0
  22. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/sbase/__main__.py +0 -0
  23. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/__init__.py +0 -0
  24. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/__main__.py +0 -0
  25. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/behave/__init__.py +0 -0
  26. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/behave/behave_sb.py +0 -0
  27. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/behave/steps.py +0 -0
  28. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/__init__.py +0 -0
  29. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/encryption.py +0 -0
  30. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/exceptions.py +0 -0
  31. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/obfuscate.py +0 -0
  32. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/common/unobfuscate.py +0 -0
  33. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/config/__init__.py +0 -0
  34. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/config/ad_block_list.py +0 -0
  35. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/config/proxy_list.py +0 -0
  36. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/config/settings.py +0 -0
  37. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/__init__.py +0 -0
  38. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/logo_helper.py +0 -0
  39. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/rich_helper.py +0 -0
  40. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/run.py +0 -0
  41. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  42. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  43. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_commander.py +0 -0
  44. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_install.py +0 -0
  45. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  46. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_mkdir.py +0 -0
  47. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
  48. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  49. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_mkrec.py +0 -0
  50. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  51. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_print.py +0 -0
  52. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  53. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/__init__.py +0 -0
  54. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/application_manager.py +0 -0
  55. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/capabilities_parser.py +0 -0
  56. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/colored_traceback.py +0 -0
  57. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/create_db_tables.sql +0 -0
  58. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/detect_b_ver.py +0 -0
  59. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/download_helper.py +0 -0
  60. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/encoded_images.py +0 -0
  61. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/jqc_helper.py +0 -0
  62. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/log_helper.py +0 -0
  63. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/mysql.py +0 -0
  64. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/proxy_helper.py +0 -0
  65. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/report_helper.py +0 -0
  66. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/s3_manager.py +0 -0
  67. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/sb_driver.py +0 -0
  68. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/session_helper.py +0 -0
  69. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/settings_parser.py +0 -0
  70. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/style_sheet.py +0 -0
  71. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/testcase_manager.py +0 -0
  72. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/tour_helper.py +0 -0
  73. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/core/visual_helper.py +0 -0
  74. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/drivers/__init__.py +0 -0
  75. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  76. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  77. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/extensions/__init__.py +0 -0
  78. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/extensions/ad_block.zip +0 -0
  79. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/extensions/disable_csp.zip +0 -0
  80. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/extensions/recorder.zip +0 -0
  81. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/extensions/sbase_ext.zip +0 -0
  82. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/__init__.py +0 -0
  83. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/constants.py +0 -0
  84. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  85. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/errors.py +0 -0
  86. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/js_utils.py +0 -0
  87. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/page_actions.py +0 -0
  88. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/page_utils.py +0 -0
  89. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/shared_utils.py +0 -0
  90. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/unittest_helper.py +0 -0
  91. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/words.py +0 -0
  92. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  93. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/js_code/__init__.py +0 -0
  94. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/js_code/active_css_js.py +0 -0
  95. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/js_code/live_js.py +0 -0
  96. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/js_code/recorder_js.py +0 -0
  97. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/masterqa/__init__.py +0 -0
  98. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/masterqa/master_qa.py +0 -0
  99. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/__init__.py +0 -0
  100. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/base_plugin.py +0 -0
  101. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/basic_test_info.py +0 -0
  102. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  103. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/driver_manager.py +0 -0
  104. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/page_source.py +0 -0
  105. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/pytest_plugin.py +0 -0
  106. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  107. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/sb_manager.py +0 -0
  108. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/screen_shots.py +0 -0
  109. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/plugins/selenium_plugin.py +0 -0
  110. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/resources/__init__.py +0 -0
  111. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/__init__.py +0 -0
  112. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/chinese.py +0 -0
  113. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/dutch.py +0 -0
  114. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/french.py +0 -0
  115. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/italian.py +0 -0
  116. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/japanese.py +0 -0
  117. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/korean.py +0 -0
  118. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/master_dict.py +0 -0
  119. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/portuguese.py +0 -0
  120. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/russian.py +0 -0
  121. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/spanish.py +0 -0
  122. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/translate/translator.py +0 -0
  123. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/__init__.py +0 -0
  124. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp.py +0 -0
  125. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  126. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  127. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  128. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  129. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  130. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  131. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  132. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/cdp_driver/tab.py +0 -0
  133. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/dprocess.py +0 -0
  134. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/options.py +0 -0
  135. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/patcher.py +0 -0
  136. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/reactor.py +0 -0
  137. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/undetected/webelement.py +0 -0
  138. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/__init__.py +0 -0
  139. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  140. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  141. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  142. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  143. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  144. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  145. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  146. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  147. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  148. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  149. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  150. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  151. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  152. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase.egg-info/SOURCES.txt +0 -0
  153. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase.egg-info/dependency_links.txt +0 -0
  154. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase.egg-info/entry_points.txt +0 -0
  155. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/seleniumbase.egg-info/top_level.txt +0 -0
  156. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/setup.cfg +0 -0
  157. {seleniumbase-4.41.6 → seleniumbase-4.41.8}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.41.6
3
+ Version: 4.41.8
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
@@ -164,7 +164,7 @@ Provides-Extra: pdfminer
164
164
  Requires-Dist: pdfminer.six==20250324; python_version < "3.9" and extra == "pdfminer"
165
165
  Requires-Dist: pdfminer.six==20250506; python_version >= "3.9" and extra == "pdfminer"
166
166
  Requires-Dist: cryptography==39.0.2; python_version < "3.9" and extra == "pdfminer"
167
- Requires-Dist: cryptography==45.0.7; python_version >= "3.9" and extra == "pdfminer"
167
+ Requires-Dist: cryptography==46.0.1; python_version >= "3.9" and extra == "pdfminer"
168
168
  Requires-Dist: cffi==1.17.1; python_version < "3.9" and extra == "pdfminer"
169
169
  Requires-Dist: cffi==2.0.0; python_version >= "3.9" and extra == "pdfminer"
170
170
  Requires-Dist: pycparser==2.22; python_version < "3.9" and extra == "pdfminer"
@@ -177,7 +177,7 @@ Requires-Dist: pip-system-certs==4.0; platform_system == "Windows" and extra ==
177
177
  Provides-Extra: proxy
178
178
  Requires-Dist: proxy.py==2.4.3; extra == "proxy"
179
179
  Provides-Extra: psutil
180
- Requires-Dist: psutil==7.0.0; extra == "psutil"
180
+ Requires-Dist: psutil==7.1.0; extra == "psutil"
181
181
  Provides-Extra: pyautogui
182
182
  Requires-Dist: PyAutoGUI==0.9.54; extra == "pyautogui"
183
183
  Provides-Extra: selenium-stealth
@@ -275,26 +275,31 @@ Dynamic: summary
275
275
 
276
276
  --------
277
277
 
278
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">raw_google.py</a>, which performs a Google search:</p>
278
+ <p align="left">📗 For performing a Google Search without hitting the "unusual traffic" page, you can use SeleniumBase UC Mode.<br />Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">SeleniumBase/examples/raw_google.py</a>, which exports results into different formats (PDF, HTML, PNG):</p>
279
279
 
280
280
  ```python
281
281
  from seleniumbase import SB
282
282
 
283
283
  with SB(test=True, uc=True) as sb:
284
284
  sb.open("https://google.com/ncr")
285
- sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
286
- sb.click('[href*="github.com/seleniumbase/"]')
287
- sb.save_screenshot_to_logs() # ./latest_logs/
285
+ sb.type('[title="Search"]', "SeleniumBase GitHub page")
286
+ sb.click("div:not([jsname]) > * > input")
288
287
  print(sb.get_page_title())
288
+ sb.sleep(2) # Wait for the "AI Overview" result
289
+ if sb.is_text_visible("Generating"):
290
+ sb.wait_for_text("AI Overview")
291
+ sb.save_as_pdf_to_logs() # Saved to ./latest_logs/
292
+ sb.save_page_source_to_logs()
293
+ sb.save_screenshot_to_logs()
289
294
  ```
290
295
 
291
296
  > `python raw_google.py`
292
297
 
293
- <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/gif/google_search.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
298
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/img/google_sb_result.png" alt="SeleniumBase on Google" title="SeleniumBase on Google" width="480" /></a>
294
299
 
295
300
  --------
296
301
 
297
- <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
302
+ <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page with UC Mode + CDP Mode: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
298
303
 
299
304
  ```python
300
305
  from seleniumbase import SB
@@ -302,16 +307,36 @@ from seleniumbase import SB
302
307
  with SB(uc=True, test=True, locale="en") as sb:
303
308
  url = "https://gitlab.com/users/sign_in"
304
309
  sb.activate_cdp_mode(url)
305
- sb.sleep(1)
310
+ sb.sleep(2.2)
306
311
  sb.uc_gui_click_captcha()
307
- sb.sleep(2)
312
+ sb.assert_text("Username", '[for="user_login"]', timeout=3)
313
+ sb.assert_element('label[for="user_login"]')
314
+ sb.highlight('button:contains("Sign in")')
315
+ sb.highlight('h1:contains("GitLab.com")')
316
+ sb.post_message("SeleniumBase wasn't detected", duration=4)
308
317
  ```
309
318
 
310
319
  <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">
311
320
 
321
+ <p align="left">📙 You can also use SeleniumBase's pure CDP Mode, which doesn't use chromedriver, Selenium, or a Python context manager at all: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_gitlab.py">SeleniumBase/examples/cdp_mode/raw_cdp_gitlab.py</a></p>
322
+
323
+ ```python
324
+ from seleniumbase import sb_cdp
325
+
326
+ url = "https://gitlab.com/users/sign_in"
327
+ sb = sb_cdp.Chrome(url)
328
+ sb.sleep(2.5)
329
+ sb.gui_click_captcha()
330
+ sb.highlight('h1:contains("GitLab.com")')
331
+ sb.highlight('button:contains("Sign in")')
332
+ sb.driver.stop()
333
+ ```
334
+
335
+ > (Due to a change in Chrome 137 where the --load-extension switch was removed, one limitation with this format is that you can't load extensions directly. The other formats weren't affected by this change.)
336
+
312
337
  --------
313
338
 
314
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">test_get_swag.py</a>, which tests an e-commerce site:</p>
339
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">SeleniumBase/examples/test_get_swag.py</a>, which tests an e-commerce site:</p>
315
340
 
316
341
  ```python
317
342
  from seleniumbase import BaseCase
@@ -343,7 +368,7 @@ class MyTestClass(BaseCase):
343
368
 
344
369
  --------
345
370
 
346
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
371
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">SeleniumBase/examples/test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
347
372
 
348
373
  ```zsh
349
374
  pytest test_coffee_cart.py --demo
@@ -357,7 +382,7 @@ pytest test_coffee_cart.py --demo
357
382
 
358
383
  <a id="multiple_examples"></a>
359
384
 
360
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">test_demo_site.py</a>, which covers several actions:</p>
385
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">SeleniumBase/examples/test_demo_site.py</a>, which covers several actions:</p>
361
386
 
362
387
  ```zsh
363
388
  pytest test_demo_site.py
@@ -65,26 +65,31 @@
65
65
 
66
66
  --------
67
67
 
68
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">raw_google.py</a>, which performs a Google search:</p>
68
+ <p align="left">📗 For performing a Google Search without hitting the "unusual traffic" page, you can use SeleniumBase UC Mode.<br />Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">SeleniumBase/examples/raw_google.py</a>, which exports results into different formats (PDF, HTML, PNG):</p>
69
69
 
70
70
  ```python
71
71
  from seleniumbase import SB
72
72
 
73
73
  with SB(test=True, uc=True) as sb:
74
74
  sb.open("https://google.com/ncr")
75
- sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
76
- sb.click('[href*="github.com/seleniumbase/"]')
77
- sb.save_screenshot_to_logs() # ./latest_logs/
75
+ sb.type('[title="Search"]', "SeleniumBase GitHub page")
76
+ sb.click("div:not([jsname]) > * > input")
78
77
  print(sb.get_page_title())
78
+ sb.sleep(2) # Wait for the "AI Overview" result
79
+ if sb.is_text_visible("Generating"):
80
+ sb.wait_for_text("AI Overview")
81
+ sb.save_as_pdf_to_logs() # Saved to ./latest_logs/
82
+ sb.save_page_source_to_logs()
83
+ sb.save_screenshot_to_logs()
79
84
  ```
80
85
 
81
86
  > `python raw_google.py`
82
87
 
83
- <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/gif/google_search.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
88
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/img/google_sb_result.png" alt="SeleniumBase on Google" title="SeleniumBase on Google" width="480" /></a>
84
89
 
85
90
  --------
86
91
 
87
- <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
92
+ <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page with UC Mode + CDP Mode: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
88
93
 
89
94
  ```python
90
95
  from seleniumbase import SB
@@ -92,16 +97,36 @@ from seleniumbase import SB
92
97
  with SB(uc=True, test=True, locale="en") as sb:
93
98
  url = "https://gitlab.com/users/sign_in"
94
99
  sb.activate_cdp_mode(url)
95
- sb.sleep(1)
100
+ sb.sleep(2.2)
96
101
  sb.uc_gui_click_captcha()
97
- sb.sleep(2)
102
+ sb.assert_text("Username", '[for="user_login"]', timeout=3)
103
+ sb.assert_element('label[for="user_login"]')
104
+ sb.highlight('button:contains("Sign in")')
105
+ sb.highlight('h1:contains("GitLab.com")')
106
+ sb.post_message("SeleniumBase wasn't detected", duration=4)
98
107
  ```
99
108
 
100
109
  <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">
101
110
 
111
+ <p align="left">📙 You can also use SeleniumBase's pure CDP Mode, which doesn't use chromedriver, Selenium, or a Python context manager at all: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_gitlab.py">SeleniumBase/examples/cdp_mode/raw_cdp_gitlab.py</a></p>
112
+
113
+ ```python
114
+ from seleniumbase import sb_cdp
115
+
116
+ url = "https://gitlab.com/users/sign_in"
117
+ sb = sb_cdp.Chrome(url)
118
+ sb.sleep(2.5)
119
+ sb.gui_click_captcha()
120
+ sb.highlight('h1:contains("GitLab.com")')
121
+ sb.highlight('button:contains("Sign in")')
122
+ sb.driver.stop()
123
+ ```
124
+
125
+ > (Due to a change in Chrome 137 where the --load-extension switch was removed, one limitation with this format is that you can't load extensions directly. The other formats weren't affected by this change.)
126
+
102
127
  --------
103
128
 
104
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">test_get_swag.py</a>, which tests an e-commerce site:</p>
129
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">SeleniumBase/examples/test_get_swag.py</a>, which tests an e-commerce site:</p>
105
130
 
106
131
  ```python
107
132
  from seleniumbase import BaseCase
@@ -133,7 +158,7 @@ class MyTestClass(BaseCase):
133
158
 
134
159
  --------
135
160
 
136
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
161
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">SeleniumBase/examples/test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
137
162
 
138
163
  ```zsh
139
164
  pytest test_coffee_cart.py --demo
@@ -147,7 +172,7 @@ pytest test_coffee_cart.py --demo
147
172
 
148
173
  <a id="multiple_examples"></a>
149
174
 
150
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">test_demo_site.py</a>, which covers several actions:</p>
175
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">SeleniumBase/examples/test_demo_site.py</a>, which covers several actions:</p>
151
176
 
152
177
  ```zsh
153
178
  pytest test_demo_site.py
@@ -1191,6 +1191,15 @@ def set_attributes(context, selector, attribute, value):
1191
1191
  sb.set_attributes(selector, attribute, value)
1192
1192
 
1193
1193
 
1194
+ @step("Save as PDF to logs")
1195
+ @step("Save as PDF to the logs")
1196
+ @step("User saves page as PDF to logs")
1197
+ @step("User saves page as PDF to the logs")
1198
+ def save_as_pdf_to_logs(context):
1199
+ sb = context.sb
1200
+ sb.save_as_pdf_to_logs()
1201
+
1202
+
1194
1203
  @step("Save page source to logs")
1195
1204
  @step("Save the page source to the logs")
1196
1205
  @step("User saves page source to logs")
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.41.6"
2
+ __version__ = "4.41.8"
@@ -522,6 +522,8 @@ def generate_gherkin(srt_actions):
522
522
  )
523
523
  elif action[0] == "ss_tl":
524
524
  sb_actions.append("Save screenshot to logs")
525
+ elif action[0] == "pdftl":
526
+ sb_actions.append("Save as PDF to logs")
525
527
  elif action[0] == "spstl":
526
528
  sb_actions.append("Save page source to logs")
527
529
  elif action[0] == "sh_fc":
@@ -53,7 +53,9 @@ def print_runtime(description=None, limit=None):
53
53
  name = description
54
54
  info = c1 + "<info>" + cr
55
55
  # Print times with a statistically significant number of decimal places
56
- if run_time < 0.0001:
56
+ if run_time < 0.00001:
57
+ print("%s - {%s} ran for %.8f seconds." % (info, name, run_time))
58
+ elif run_time < 0.0001:
57
59
  print("%s - {%s} ran for %.7f seconds." % (info, name, run_time))
58
60
  elif run_time < 0.001:
59
61
  print("%s - {%s} ran for %.6f seconds." % (info, name, run_time))
@@ -865,6 +865,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
865
865
  cdp.scroll_down = CDPM.scroll_down
866
866
  cdp.save_screenshot = CDPM.save_screenshot
867
867
  cdp.print_to_pdf = CDPM.print_to_pdf
868
+ cdp.save_as_pdf = CDPM.save_as_pdf
868
869
  cdp.page = page # async world
869
870
  cdp.driver = driver.cdp_base # async world
870
871
  cdp.tab = cdp.page # shortcut (original)
@@ -1226,6 +1227,7 @@ def _on_a_cf_turnstile_page(driver):
1226
1227
  'data-callback="onCaptchaSuccess"' in source
1227
1228
  or "/challenge-platform/scripts/" in source
1228
1229
  or 'id="challenge-widget-' in source
1230
+ or "challenges.cloudf" in source
1229
1231
  or "cf-turnstile-" in source
1230
1232
  ):
1231
1233
  return True
@@ -556,6 +556,9 @@ def generate_sbase_code(srt_actions):
556
556
  elif action[0] == "ss_tl":
557
557
  method = "save_screenshot_to_logs"
558
558
  sb_actions.append("self.%s()" % method)
559
+ elif action[0] == "pdftl":
560
+ method = "save_as_pdf_to_logs"
561
+ sb_actions.append("self.%s()" % method)
559
562
  elif action[0] == "spstl":
560
563
  method = "save_page_source_to_logs"
561
564
  sb_actions.append("self.%s()" % method)
@@ -1661,11 +1661,16 @@ class CDPMethods():
1661
1661
  self.loop.run_until_complete(self.page.wait())
1662
1662
 
1663
1663
  def _on_a_cf_turnstile_page(self):
1664
+ time.sleep(0.042)
1664
1665
  source = self.get_page_source()
1666
+ if not source or len(source) < 400:
1667
+ time.sleep(0.22)
1668
+ source = self.get_page_source()
1665
1669
  if (
1666
1670
  'data-callback="onCaptchaSuccess"' in source
1667
1671
  or "/challenge-platform/scripts/" in source
1668
1672
  or 'id="challenge-widget-' in source
1673
+ or "challenges.cloudf" in source
1669
1674
  or "cf-turnstile-" in source
1670
1675
  ):
1671
1676
  return True
@@ -1795,6 +1800,7 @@ class CDPMethods():
1795
1800
  self.loop.run_until_complete(self.page.evaluate(script))
1796
1801
  self.loop.run_until_complete(self.page.wait())
1797
1802
  with suppress(Exception):
1803
+ time.sleep(0.08)
1798
1804
  element_rect = self.get_gui_element_rect(selector, timeout=1)
1799
1805
  e_x = element_rect["x"]
1800
1806
  e_y = element_rect["y"]
@@ -1804,6 +1810,7 @@ class CDPMethods():
1804
1810
  else:
1805
1811
  y = e_y + 22
1806
1812
  sb_config._saved_cf_x_y = (x, y)
1813
+ time.sleep(0.08)
1807
1814
  self.gui_click_x_y(x, y)
1808
1815
 
1809
1816
  def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
@@ -2571,6 +2578,9 @@ class CDPMethods():
2571
2578
  filename = os.path.join(folder, name)
2572
2579
  self.loop.run_until_complete(self.page.print_to_pdf(filename))
2573
2580
 
2581
+ def save_as_pdf(self, *args, **kwargs):
2582
+ self.print_to_pdf(*args, **kwargs)
2583
+
2574
2584
 
2575
2585
  class Chrome(CDPMethods):
2576
2586
  def __init__(self, url=None, **kwargs):
@@ -134,6 +134,7 @@ class BaseCase(unittest.TestCase):
134
134
  self.__requests_timeout = None
135
135
  self.__page_source_count = 0
136
136
  self.__screenshot_count = 0
137
+ self.__saved_pdf_count = 0
137
138
  self.__logs_data_count = 0
138
139
  self.__last_data_file = None
139
140
  self.__level_0_visual_f = False
@@ -4492,7 +4493,8 @@ class BaseCase(unittest.TestCase):
4492
4493
  If a provided selector is not found, then takes a full-page screenshot.
4493
4494
  (The last_page / failure screenshot is always "screenshot.png")
4494
4495
  The screenshot will be in PNG format."""
4495
- self.wait_for_ready_state_complete()
4496
+ if not self.__is_cdp_swap_needed():
4497
+ self.wait_for_ready_state_complete()
4496
4498
  test_logpath = os.path.join(self.log_path, self.__get_test_id())
4497
4499
  self.__create_log_path_as_needed(test_logpath)
4498
4500
  if name:
@@ -4510,6 +4512,11 @@ class BaseCase(unittest.TestCase):
4510
4512
  if selector and by:
4511
4513
  selector, by = self.__recalculate_selector(selector, by)
4512
4514
  if page_actions.is_element_present(self.driver, selector, by):
4515
+ if self.__is_cdp_swap_needed():
4516
+ selector = self.convert_to_css_selector(selector, by=by)
4517
+ return self.cdp.save_screenshot(
4518
+ name, folder=test_logpath, selector=selector
4519
+ )
4513
4520
  return page_actions.save_screenshot(
4514
4521
  self.driver, name, test_logpath, selector, by
4515
4522
  )
@@ -4523,8 +4530,49 @@ class BaseCase(unittest.TestCase):
4523
4530
  action = ["ss_tl", "", origin, time_stamp]
4524
4531
  self.__extra_actions.append(action)
4525
4532
  sb_config._has_logs = True
4533
+ if self.__is_cdp_swap_needed():
4534
+ return self.cdp.save_screenshot(name, folder=test_logpath)
4526
4535
  return page_actions.save_screenshot(self.driver, name, test_logpath)
4527
4536
 
4537
+ def save_as_pdf(self, name, folder=None):
4538
+ """Same as self.print_to_pdf()"""
4539
+ return self.print_to_pdf(name, folder=folder)
4540
+
4541
+ def save_as_pdf_to_logs(self, name=None):
4542
+ """Saves the page as a PDF to the "latest_logs/" folder.
4543
+ Naming is automatic:
4544
+ If NO NAME provided: "_1_PDF.pdf", "_2_PDF.pdf", etc.
4545
+ If NAME IS provided, then: "_1_name.pdf", "_2_name.pdf", etc."""
4546
+ if not self.__is_cdp_swap_needed():
4547
+ self.wait_for_ready_state_complete()
4548
+ test_logpath = os.path.join(self.log_path, self.__get_test_id())
4549
+ self.__create_log_path_as_needed(test_logpath)
4550
+ if name:
4551
+ name = str(name)
4552
+ self.__saved_pdf_count += 1
4553
+ if not name or len(name) == 0:
4554
+ name = "_%s_PDF.pdf" % self.__saved_pdf_count
4555
+ else:
4556
+ pre_name = "_%s_" % self.__saved_pdf_count
4557
+ if len(name) >= 4 and name[-4:].lower() == ".pdf":
4558
+ name = name[:-4]
4559
+ if len(name) == 0:
4560
+ name = "PDF"
4561
+ name = "%s%s.pdf" % (pre_name, name)
4562
+ if self.recorder_mode:
4563
+ url = self.get_current_url()
4564
+ if url and len(url) > 0:
4565
+ if ("http:") in url or ("https:") in url or ("file:") in url:
4566
+ if self.get_session_storage_item("pause_recorder") == "no":
4567
+ time_stamp = self.execute_script("return Date.now();")
4568
+ origin = self.get_origin()
4569
+ action = ["pdftl", "", origin, time_stamp]
4570
+ self.__extra_actions.append(action)
4571
+ sb_config._has_logs = True
4572
+ if self.__is_cdp_swap_needed():
4573
+ return self.cdp.print_to_pdf(name, folder=test_logpath)
4574
+ return self.print_to_pdf(name, test_logpath)
4575
+
4528
4576
  def save_page_source_to_logs(self, name=None):
4529
4577
  """Saves the page HTML to the "latest_logs/" folder.
4530
4578
  Naming is automatic:
@@ -5517,6 +5565,7 @@ class BaseCase(unittest.TestCase):
5517
5565
  ext_actions.append("s_scr")
5518
5566
  ext_actions.append("ss_tf")
5519
5567
  ext_actions.append("ss_tl")
5568
+ ext_actions.append("pdftl")
5520
5569
  ext_actions.append("spstl")
5521
5570
  ext_actions.append("da_el")
5522
5571
  ext_actions.append("da_ep")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.41.6
3
+ Version: 4.41.8
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
@@ -164,7 +164,7 @@ Provides-Extra: pdfminer
164
164
  Requires-Dist: pdfminer.six==20250324; python_version < "3.9" and extra == "pdfminer"
165
165
  Requires-Dist: pdfminer.six==20250506; python_version >= "3.9" and extra == "pdfminer"
166
166
  Requires-Dist: cryptography==39.0.2; python_version < "3.9" and extra == "pdfminer"
167
- Requires-Dist: cryptography==45.0.7; python_version >= "3.9" and extra == "pdfminer"
167
+ Requires-Dist: cryptography==46.0.1; python_version >= "3.9" and extra == "pdfminer"
168
168
  Requires-Dist: cffi==1.17.1; python_version < "3.9" and extra == "pdfminer"
169
169
  Requires-Dist: cffi==2.0.0; python_version >= "3.9" and extra == "pdfminer"
170
170
  Requires-Dist: pycparser==2.22; python_version < "3.9" and extra == "pdfminer"
@@ -177,7 +177,7 @@ Requires-Dist: pip-system-certs==4.0; platform_system == "Windows" and extra ==
177
177
  Provides-Extra: proxy
178
178
  Requires-Dist: proxy.py==2.4.3; extra == "proxy"
179
179
  Provides-Extra: psutil
180
- Requires-Dist: psutil==7.0.0; extra == "psutil"
180
+ Requires-Dist: psutil==7.1.0; extra == "psutil"
181
181
  Provides-Extra: pyautogui
182
182
  Requires-Dist: PyAutoGUI==0.9.54; extra == "pyautogui"
183
183
  Provides-Extra: selenium-stealth
@@ -275,26 +275,31 @@ Dynamic: summary
275
275
 
276
276
  --------
277
277
 
278
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">raw_google.py</a>, which performs a Google search:</p>
278
+ <p align="left">📗 For performing a Google Search without hitting the "unusual traffic" page, you can use SeleniumBase UC Mode.<br />Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">SeleniumBase/examples/raw_google.py</a>, which exports results into different formats (PDF, HTML, PNG):</p>
279
279
 
280
280
  ```python
281
281
  from seleniumbase import SB
282
282
 
283
283
  with SB(test=True, uc=True) as sb:
284
284
  sb.open("https://google.com/ncr")
285
- sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
286
- sb.click('[href*="github.com/seleniumbase/"]')
287
- sb.save_screenshot_to_logs() # ./latest_logs/
285
+ sb.type('[title="Search"]', "SeleniumBase GitHub page")
286
+ sb.click("div:not([jsname]) > * > input")
288
287
  print(sb.get_page_title())
288
+ sb.sleep(2) # Wait for the "AI Overview" result
289
+ if sb.is_text_visible("Generating"):
290
+ sb.wait_for_text("AI Overview")
291
+ sb.save_as_pdf_to_logs() # Saved to ./latest_logs/
292
+ sb.save_page_source_to_logs()
293
+ sb.save_screenshot_to_logs()
289
294
  ```
290
295
 
291
296
  > `python raw_google.py`
292
297
 
293
- <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/gif/google_search.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
298
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/img/google_sb_result.png" alt="SeleniumBase on Google" title="SeleniumBase on Google" width="480" /></a>
294
299
 
295
300
  --------
296
301
 
297
- <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
302
+ <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page with UC Mode + CDP Mode: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
298
303
 
299
304
  ```python
300
305
  from seleniumbase import SB
@@ -302,16 +307,36 @@ from seleniumbase import SB
302
307
  with SB(uc=True, test=True, locale="en") as sb:
303
308
  url = "https://gitlab.com/users/sign_in"
304
309
  sb.activate_cdp_mode(url)
305
- sb.sleep(1)
310
+ sb.sleep(2.2)
306
311
  sb.uc_gui_click_captcha()
307
- sb.sleep(2)
312
+ sb.assert_text("Username", '[for="user_login"]', timeout=3)
313
+ sb.assert_element('label[for="user_login"]')
314
+ sb.highlight('button:contains("Sign in")')
315
+ sb.highlight('h1:contains("GitLab.com")')
316
+ sb.post_message("SeleniumBase wasn't detected", duration=4)
308
317
  ```
309
318
 
310
319
  <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">
311
320
 
321
+ <p align="left">📙 You can also use SeleniumBase's pure CDP Mode, which doesn't use chromedriver, Selenium, or a Python context manager at all: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_gitlab.py">SeleniumBase/examples/cdp_mode/raw_cdp_gitlab.py</a></p>
322
+
323
+ ```python
324
+ from seleniumbase import sb_cdp
325
+
326
+ url = "https://gitlab.com/users/sign_in"
327
+ sb = sb_cdp.Chrome(url)
328
+ sb.sleep(2.5)
329
+ sb.gui_click_captcha()
330
+ sb.highlight('h1:contains("GitLab.com")')
331
+ sb.highlight('button:contains("Sign in")')
332
+ sb.driver.stop()
333
+ ```
334
+
335
+ > (Due to a change in Chrome 137 where the --load-extension switch was removed, one limitation with this format is that you can't load extensions directly. The other formats weren't affected by this change.)
336
+
312
337
  --------
313
338
 
314
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">test_get_swag.py</a>, which tests an e-commerce site:</p>
339
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">SeleniumBase/examples/test_get_swag.py</a>, which tests an e-commerce site:</p>
315
340
 
316
341
  ```python
317
342
  from seleniumbase import BaseCase
@@ -343,7 +368,7 @@ class MyTestClass(BaseCase):
343
368
 
344
369
  --------
345
370
 
346
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
371
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py" target="_blank">SeleniumBase/examples/test_coffee_cart.py</a>, which verifies an e-commerce site:</p>
347
372
 
348
373
  ```zsh
349
374
  pytest test_coffee_cart.py --demo
@@ -357,7 +382,7 @@ pytest test_coffee_cart.py --demo
357
382
 
358
383
  <a id="multiple_examples"></a>
359
384
 
360
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">test_demo_site.py</a>, which covers several actions:</p>
385
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py" target="_blank">SeleniumBase/examples/test_demo_site.py</a>, which covers several actions:</p>
361
386
 
362
387
  ```zsh
363
388
  pytest test_demo_site.py
@@ -140,7 +140,7 @@ pycparser==2.22
140
140
 
141
141
  [pdfminer:python_version >= "3.9"]
142
142
  pdfminer.six==20250506
143
- cryptography==45.0.7
143
+ cryptography==46.0.1
144
144
  cffi==2.0.0
145
145
  pycparser==2.23
146
146
 
@@ -161,7 +161,7 @@ pip-system-certs==4.0
161
161
  proxy.py==2.4.3
162
162
 
163
163
  [psutil]
164
- psutil==7.0.0
164
+ psutil==7.1.0
165
165
 
166
166
  [pyautogui]
167
167
  PyAutoGUI==0.9.54
@@ -270,7 +270,7 @@ setup(
270
270
  'pdfminer.six==20250324;python_version<"3.9"',
271
271
  'pdfminer.six==20250506;python_version>="3.9"',
272
272
  'cryptography==39.0.2;python_version<"3.9"',
273
- 'cryptography==45.0.7;python_version>="3.9"',
273
+ 'cryptography==46.0.1;python_version>="3.9"',
274
274
  'cffi==1.17.1;python_version<"3.9"',
275
275
  'cffi==2.0.0;python_version>="3.9"',
276
276
  'pycparser==2.22;python_version<"3.9"',
@@ -296,7 +296,7 @@ setup(
296
296
  ],
297
297
  # pip install -e .[psutil]
298
298
  "psutil": [
299
- "psutil==7.0.0",
299
+ "psutil==7.1.0",
300
300
  ],
301
301
  # pip install -e .[pyautogui]
302
302
  "pyautogui": [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes