seleniumbase 4.49.14__tar.gz → 4.50.1__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.

Potentially problematic release.


This version of seleniumbase might be problematic. Click here for more details.

Files changed (164) hide show
  1. {seleniumbase-4.49.14/seleniumbase.egg-info → seleniumbase-4.50.1}/PKG-INFO +5 -3
  2. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/README.md +2 -0
  3. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/requirements.txt +2 -2
  4. seleniumbase-4.50.1/seleniumbase/__version__.py +2 -0
  5. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/run.py +2 -0
  6. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_mkrec.py +10 -0
  7. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_recorder.py +233 -70
  8. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/browser_launcher.py +1 -0
  9. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/sb_cdp.py +28 -0
  10. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/base_case.py +327 -119
  11. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/pytest_plugin.py +32 -9
  12. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/sb_manager.py +28 -9
  13. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/selenium_plugin.py +32 -9
  14. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/browser.py +19 -0
  15. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/tab.py +5 -1
  16. {seleniumbase-4.49.14 → seleniumbase-4.50.1/seleniumbase.egg-info}/PKG-INFO +5 -3
  17. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase.egg-info/requires.txt +2 -2
  18. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/setup.py +2 -2
  19. seleniumbase-4.49.14/seleniumbase/__version__.py +0 -2
  20. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/.gitignore +0 -0
  21. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/LICENSE +0 -0
  22. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/MANIFEST.in +0 -0
  23. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/install.sh +0 -0
  24. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/pyproject.toml +0 -0
  25. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/pytest.ini +0 -0
  26. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/sbase/__init__.py +0 -0
  27. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/sbase/__main__.py +0 -0
  28. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/sbase/steps.py +0 -0
  29. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/__init__.py +0 -0
  30. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/__main__.py +0 -0
  31. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/behave/__init__.py +0 -0
  32. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/behave/behave_helper.py +0 -0
  33. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/behave/behave_sb.py +0 -0
  34. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/behave/steps.py +0 -0
  35. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/__init__.py +0 -0
  36. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/decorators.py +0 -0
  37. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/encryption.py +0 -0
  38. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/exceptions.py +0 -0
  39. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/obfuscate.py +0 -0
  40. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/common/unobfuscate.py +0 -0
  41. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/config/__init__.py +0 -0
  42. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/config/ad_block_list.py +0 -0
  43. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/config/proxy_list.py +0 -0
  44. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/config/settings.py +0 -0
  45. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/__init__.py +0 -0
  46. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/logo_helper.py +0 -0
  47. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/rich_helper.py +0 -0
  48. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  49. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  50. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_commander.py +0 -0
  51. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_install.py +0 -0
  52. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  53. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_mkdir.py +0 -0
  54. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
  55. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  56. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  57. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/console_scripts/sb_print.py +0 -0
  58. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/__init__.py +0 -0
  59. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/application_manager.py +0 -0
  60. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/capabilities_parser.py +0 -0
  61. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/colored_traceback.py +0 -0
  62. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/create_db_tables.sql +0 -0
  63. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/detect_b_ver.py +0 -0
  64. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/download_helper.py +0 -0
  65. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/encoded_images.py +0 -0
  66. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/jqc_helper.py +0 -0
  67. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/log_helper.py +0 -0
  68. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/mysql.py +0 -0
  69. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/nest_asyncio.py +0 -0
  70. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/proxy_helper.py +0 -0
  71. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/recorder_helper.py +0 -0
  72. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/report_helper.py +0 -0
  73. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/s3_manager.py +0 -0
  74. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/sb_driver.py +0 -0
  75. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/session_helper.py +0 -0
  76. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/settings_parser.py +0 -0
  77. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/style_sheet.py +0 -0
  78. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/testcase_manager.py +0 -0
  79. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/tour_helper.py +0 -0
  80. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/core/visual_helper.py +0 -0
  81. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/__init__.py +0 -0
  82. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  83. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  84. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  85. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  86. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  87. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  88. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  89. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/extensions/__init__.py +0 -0
  90. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/extensions/ad_block.zip +0 -0
  91. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/extensions/disable_csp.zip +0 -0
  92. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/extensions/recorder.zip +0 -0
  93. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/extensions/sbase_ext.zip +0 -0
  94. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/__init__.py +0 -0
  95. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/constants.py +0 -0
  96. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  97. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/errors.py +0 -0
  98. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/js_utils.py +0 -0
  99. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/page_actions.py +0 -0
  100. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/page_utils.py +0 -0
  101. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/shared_utils.py +0 -0
  102. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/unittest_helper.py +0 -0
  103. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/words.py +0 -0
  104. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  105. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/js_code/__init__.py +0 -0
  106. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/js_code/active_css_js.py +0 -0
  107. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/js_code/live_js.py +0 -0
  108. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/js_code/recorder_js.py +0 -0
  109. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/masterqa/__init__.py +0 -0
  110. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/masterqa/master_qa.py +0 -0
  111. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/__init__.py +0 -0
  112. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/base_plugin.py +0 -0
  113. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/basic_test_info.py +0 -0
  114. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  115. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/driver_manager.py +0 -0
  116. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/page_source.py +0 -0
  117. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  118. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/plugins/screen_shots.py +0 -0
  119. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/resources/__init__.py +0 -0
  120. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/__init__.py +0 -0
  121. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/chinese.py +0 -0
  122. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/dutch.py +0 -0
  123. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/french.py +0 -0
  124. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/italian.py +0 -0
  125. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/japanese.py +0 -0
  126. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/korean.py +0 -0
  127. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/master_dict.py +0 -0
  128. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/portuguese.py +0 -0
  129. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/russian.py +0 -0
  130. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/spanish.py +0 -0
  131. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/translate/translator.py +0 -0
  132. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/__init__.py +0 -0
  133. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp.py +0 -0
  134. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  135. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  136. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  137. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  138. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  139. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  140. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/dprocess.py +0 -0
  141. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/options.py +0 -0
  142. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/patcher.py +0 -0
  143. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/reactor.py +0 -0
  144. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/undetected/webelement.py +0 -0
  145. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/__init__.py +0 -0
  146. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  147. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  148. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  149. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  150. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  151. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  152. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  153. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  154. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  155. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  156. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  157. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  158. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  159. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase.egg-info/SOURCES.txt +0 -0
  160. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase.egg-info/dependency_links.txt +0 -0
  161. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase.egg-info/entry_points.txt +0 -0
  162. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/seleniumbase.egg-info/top_level.txt +0 -0
  163. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/setup.cfg +0 -0
  164. {seleniumbase-4.49.14 → seleniumbase-4.50.1}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.49.14
3
+ Version: 4.50.1
4
4
  Summary: SeleniumBase is a framework for web crawling, scraping, and testing. Supports pytest. CDP Mode adds stealth. Includes many tools.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -66,7 +66,7 @@ Requires-Dist: setuptools~=70.2; python_version < "3.10"
66
66
  Requires-Dist: setuptools>=82.0.1; python_version >= "3.10"
67
67
  Requires-Dist: wheel>=0.47.0
68
68
  Requires-Dist: attrs>=26.1.0
69
- Requires-Dist: certifi>=2026.5.20
69
+ Requires-Dist: certifi>=2026.6.17
70
70
  Requires-Dist: exceptiongroup>=1.3.1
71
71
  Requires-Dist: websockets~=15.0.1; python_version < "3.10"
72
72
  Requires-Dist: websockets>=16.0; python_version >= "3.10"
@@ -107,7 +107,7 @@ Requires-Dist: wsproto==1.2.0; python_version < "3.10"
107
107
  Requires-Dist: wsproto~=1.3.2; python_version >= "3.10"
108
108
  Requires-Dist: websocket-client~=1.9.0
109
109
  Requires-Dist: selenium==4.32.0; python_version < "3.10"
110
- Requires-Dist: selenium==4.44.0; python_version >= "3.10"
110
+ Requires-Dist: selenium==4.45.0; python_version >= "3.10"
111
111
  Requires-Dist: cssselect==1.3.0; python_version < "3.10"
112
112
  Requires-Dist: cssselect<2,>=1.4.0; python_version >= "3.10"
113
113
  Requires-Dist: sortedcontainers==2.4.0
@@ -1009,6 +1009,8 @@ pytest test_coffee_cart.py --trace
1009
1009
  --verify-delay=SECONDS # (The delay before MasterQA verification checks.)
1010
1010
  --ee | --esc-end # (Lets the user end the current test via the ESC key.)
1011
1011
  --recorder # (Enables the Recorder for turning browser actions into code.)
1012
+ --rec-sb-mgr # (A Recorder Mode that generates SB() context manager code.)
1013
+ --rec-sb-cdp # (A Recorder Mode that generates Pure CDP Mode sb_cdp code.)
1012
1014
  --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.)
1013
1015
  --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.)
1014
1016
  --rec-print # (If the Recorder is enabled, prints output after tests end.)
@@ -823,6 +823,8 @@ pytest test_coffee_cart.py --trace
823
823
  --verify-delay=SECONDS # (The delay before MasterQA verification checks.)
824
824
  --ee | --esc-end # (Lets the user end the current test via the ESC key.)
825
825
  --recorder # (Enables the Recorder for turning browser actions into code.)
826
+ --rec-sb-mgr # (A Recorder Mode that generates SB() context manager code.)
827
+ --rec-sb-cdp # (A Recorder Mode that generates Pure CDP Mode sb_cdp code.)
826
828
  --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.)
827
829
  --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.)
828
830
  --rec-print # (If the Recorder is enabled, prints output after tests end.)
@@ -5,7 +5,7 @@ setuptools~=70.2;python_version<"3.10"
5
5
  setuptools>=82.0.1;python_version>="3.10"
6
6
  wheel>=0.47.0
7
7
  attrs>=26.1.0
8
- certifi>=2026.5.20
8
+ certifi>=2026.6.17
9
9
  exceptiongroup>=1.3.1
10
10
  websockets~=15.0.1;python_version<"3.10"
11
11
  websockets>=16.0;python_version>="3.10"
@@ -46,7 +46,7 @@ wsproto==1.2.0;python_version<"3.10"
46
46
  wsproto~=1.3.2;python_version>="3.10"
47
47
  websocket-client~=1.9.0
48
48
  selenium==4.32.0;python_version<"3.10"
49
- selenium==4.44.0;python_version>="3.10"
49
+ selenium==4.45.0;python_version>="3.10"
50
50
  cssselect==1.3.0;python_version<"3.10"
51
51
  cssselect>=1.4.0,<2;python_version>="3.10"
52
52
  sortedcontainers==2.4.0
@@ -0,0 +1,2 @@
1
+ # seleniumbase package
2
+ __version__ = "4.50.1"
@@ -834,6 +834,8 @@ def show_options():
834
834
  op += "--help / -h (Display list of all available pytest options.)\n"
835
835
  op += "--ftrace / --final-trace (Enter Debug Mode after tests end.)\n"
836
836
  op += "--recorder / --rec (Save browser actions as Python scripts.)\n"
837
+ op += "--rec-sb-mgr (Save Recorder actions as SB() context manager.)\n"
838
+ op += "--rec-sb-cdp (Save Recorder actions as Pure CDP Mode sb_cdp.)\n"
837
839
  op += "--rec-behave / --rec-gherkin (Save actions as Gherkin code.)\n"
838
840
  op += "--rec-print (Display recorded scripts when they are created.)\n"
839
841
  op += "--save-screenshot (Save a screenshot at the end of each test.)\n"
@@ -96,6 +96,8 @@ def main():
96
96
  use_colors = True
97
97
  force_gui = False
98
98
  rec_behave = False
99
+ rec_sb_mgr = False
100
+ rec_sb_cdp = False
99
101
 
100
102
  sys_executable = sys.executable
101
103
  if " " in sys_executable:
@@ -164,6 +166,10 @@ def main():
164
166
  use_uc = True
165
167
  elif option.lower() in ("--rec-behave", "--behave", "--gherkin"):
166
168
  rec_behave = True
169
+ elif option.lower() in ("--rec-sb-mgr"):
170
+ rec_sb_mgr = True
171
+ elif option.lower() in ("--rec-sb-cdp"):
172
+ rec_sb_cdp = True
167
173
  elif option.lower().startswith("--url="):
168
174
  start_page = option[len("--url="):]
169
175
  elif option.lower() == "--url":
@@ -306,6 +312,10 @@ def main():
306
312
  run_cmd += " --uc"
307
313
  if rec_behave:
308
314
  run_cmd += " --rec-behave"
315
+ if rec_sb_mgr:
316
+ run_cmd += " --rec-sb-mgr"
317
+ if rec_sb_cdp:
318
+ run_cmd += " --rec-sb-cdp"
309
319
  print(run_cmd)
310
320
  os.system(run_cmd)
311
321
  if os.path.exists(file_path):
@@ -20,7 +20,10 @@ import os
20
20
  import subprocess
21
21
  import sys
22
22
  import tkinter as tk
23
+ from tkinter import ttk
24
+ from contextlib import suppress
23
25
  from seleniumbase import config as sb_config
26
+ from seleniumbase.core import detect_b_ver
24
27
  from seleniumbase.fixtures import page_utils
25
28
  from seleniumbase.fixtures import shared_utils
26
29
  from tkinter import messagebox
@@ -76,7 +79,15 @@ def file_name_error(file_name):
76
79
  return error_msg
77
80
 
78
81
 
79
- def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
82
+ def do_recording(
83
+ file_name,
84
+ url,
85
+ overwrite_enabled,
86
+ brx,
87
+ ucb,
88
+ output_format,
89
+ window,
90
+ ):
80
91
  poll = None
81
92
  if sb_config.rec_subprocess_used:
82
93
  poll = sb_config.rec_subprocess_p.poll()
@@ -101,7 +112,13 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
101
112
  if not page_utils.is_valid_url(url):
102
113
  if page_utils.is_valid_url("https://" + url):
103
114
  url = "https://" + url
104
- if not page_utils.is_valid_url(url):
115
+ if "edge" in brx.lower() and ucb:
116
+ messagebox.showwarning(
117
+ "Invalid selection",
118
+ "MS Edge cannot be combined with UC Mode "
119
+ "because it uses msedgedriver, not chromedriver!",
120
+ )
121
+ elif not page_utils.is_valid_url(url):
105
122
  messagebox.showwarning(
106
123
  "Invalid URL", "Enter a valid URL! (Eg. seleniumbase.io)"
107
124
  )
@@ -121,13 +138,12 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
121
138
  else:
122
139
  os.remove(file_name)
123
140
  add_on = ""
124
- command_args = sys.argv[2:]
125
- if (
126
- "--rec-behave" in command_args
127
- or "--behave" in command_args
128
- or "--gherkin" in command_args
129
- ):
141
+ if "Behave" in output_format:
130
142
  add_on = " --rec-behave"
143
+ elif "SB()" in output_format:
144
+ add_on = " --rec-sb-mgr"
145
+ elif "sb_cdp" in output_format:
146
+ add_on = " --rec-sb-cdp"
131
147
  command = (
132
148
  "%s -m seleniumbase mkrec %s --url=%s --gui"
133
149
  % (sys_executable, file_name, url)
@@ -142,25 +158,21 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
142
158
  "%s -m seleniumbase mkrec %s --url='%s' --gui"
143
159
  % (sys_executable, file_name, url)
144
160
  )
145
- if not use_chrome:
161
+ if "edge" in brx.lower():
146
162
  command += " --edge"
147
- elif "--opera" in command_args:
163
+ elif "opera" in brx.lower():
148
164
  command += " --opera"
149
- elif "--brave" in command_args:
165
+ elif "brave" in brx.lower():
150
166
  command += " --brave"
151
- elif "--comet" in command_args:
167
+ elif "comet" in brx.lower():
152
168
  command += " --comet"
153
- elif "--atlas" in command_args:
169
+ elif "atlas" in brx.lower():
154
170
  command += " --atlas"
155
- elif "--use-chromium" in command_args:
171
+ elif "chromium" in brx.lower():
156
172
  command += " --use-chromium"
157
- if (
158
- "--uc" in command_args
159
- or "--cdp" in command_args
160
- or "--undetected" in command_args
161
- or "--undetectable" in command_args
162
- ):
173
+ if ucb:
163
174
  command += " --uc"
175
+ command_args = sys.argv[2:]
164
176
  if "--ee" in command_args:
165
177
  command += " --ee"
166
178
  command += add_on
@@ -175,7 +187,7 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
175
187
  send_window_to_front(window)
176
188
 
177
189
 
178
- def do_playback(file_name, use_chrome, window, demo_mode=False):
190
+ def do_playback(file_name, brx, window, demo_mode=False):
179
191
  file_name = file_name.strip()
180
192
  error_msg = file_name_error(file_name)
181
193
  if error_msg:
@@ -189,11 +201,22 @@ def do_playback(file_name, use_chrome, window, demo_mode=False):
189
201
  'File "%s" does not exist in the current directory!' % file_name,
190
202
  )
191
203
  return
192
- command = "%s -m pytest %s -q -s" % (sys_executable, file_name)
204
+ # command = "%s -m pytest %s -q -s" % (sys_executable, file_name)
205
+ command = "%s %s -q -s" % (sys_executable, file_name)
193
206
  if shared_utils.is_linux():
194
207
  command += " --gui"
195
- if not use_chrome:
208
+ if "edge" in brx.lower():
196
209
  command += " --edge"
210
+ elif "opera" in brx.lower():
211
+ command += " --opera"
212
+ elif "brave" in brx.lower():
213
+ command += " --brave"
214
+ elif "comet" in brx.lower():
215
+ command += " --comet"
216
+ elif "atlas" in brx.lower():
217
+ command += " --atlas"
218
+ elif "chromium" in brx.lower():
219
+ command += " --use-chromium"
197
220
  if demo_mode:
198
221
  command += " --demo"
199
222
  command_args = sys.argv[2:]
@@ -223,100 +246,233 @@ def create_tkinter_gui():
223
246
  default_file_name = "new_recording.py"
224
247
  window = tk.Tk()
225
248
  window.title("SeleniumBase Recorder App")
226
- window.geometry("344x388")
249
+ window.geometry("344x560")
250
+ my_font = ("TkDefaultFont", 11)
227
251
  frame = tk.Frame(window)
228
252
  frame.pack()
229
253
 
230
- tk.Label(window, text="").pack()
231
254
  fname = tk.StringVar(value=default_file_name)
232
- tk.Label(window, text="Enter filename to save recording as:").pack()
255
+ label = tk.Label(window, text="Enter filename to save recording as:")
256
+ label.pack(pady=(20, 0.2))
233
257
  entry = tk.Entry(window, textvariable=fname)
234
- entry.pack()
258
+ entry.config(width=22)
259
+ entry.pack(pady=(0.6, 0.2))
235
260
  cbx = tk.IntVar()
236
261
  chk = tk.Checkbutton(window, text="Overwrite existing files", variable=cbx)
237
262
  chk.pack()
238
263
  chk.select()
239
264
  use_stealth = False
265
+ use_behave = False
266
+ use_sb_mgr = False
267
+ use_sb_cdp = False
268
+ use_atlas = False
240
269
  command_args = sys.argv[2:]
241
270
  if (
242
271
  "--uc" in command_args
243
272
  or "--cdp" in command_args
273
+ or "--stealth" in command_args
244
274
  or "--undetected" in command_args
245
275
  or "--undetectable" in command_args
246
276
  ):
247
277
  use_stealth = True
248
- browser_display = "Use Chrome over Edge"
249
- if "--opera" in command_args:
250
- browser_display = "Use Opera over Edge"
251
- elif "--brave" in command_args:
252
- browser_display = "Use Brave over Edge"
253
- elif "--comet" in command_args:
254
- browser_display = "Use Comet over Edge"
255
- elif "--atlas" in command_args:
256
- browser_display = "Use Atlas over Edge"
257
- cbb = tk.IntVar()
258
- if not use_stealth:
259
- chkb = tk.Checkbutton(window, text=browser_display, variable=cbb)
260
- chkb.pack()
261
- if "--edge" not in command_args:
262
- chkb.select()
278
+ if (
279
+ "--rec-behave" in command_args
280
+ or "--behave" in command_args
281
+ or "--gherkin" in command_args
282
+ ):
283
+ use_behave = True
284
+ if "--rec-sb-mgr" in command_args:
285
+ use_sb_mgr = True
286
+ if "--rec-sb-cdp" in command_args:
287
+ use_sb_cdp = True
288
+ if "--atlas" in command_args:
289
+ use_atlas = True
290
+
291
+ tk.Label(window, text="\nSelect a web browser to use:").pack()
292
+ br_count = 2
293
+ br_order = {"chrome": 0, "chromium": 1}
294
+ options_list = ["Google Chrome"]
295
+ options_list.append("Chromium Browser")
296
+ if shared_utils.is_windows():
297
+ options_list.append("MS Edge (No Stealth)")
298
+ br_order["edge"] = br_count
299
+ br_count += 1
263
300
  else:
264
- chkb = tk.Checkbutton(
265
- window, text="Stealthy Chrome Mode", variable=cbb
266
- )
267
- chkb.pack()
301
+ with suppress(Exception):
302
+ if os.path.exists(detect_b_ver.get_binary_location("edge")):
303
+ options_list.append("MS Edge (No Stealth)")
304
+ br_order["edge"] = br_count
305
+ br_count += 1
306
+ with suppress(Exception):
307
+ if os.path.exists(detect_b_ver.get_binary_location("opera")):
308
+ options_list.append("Opera Browser")
309
+ br_order["opera"] = br_count
310
+ br_count += 1
311
+ with suppress(Exception):
312
+ if os.path.exists(detect_b_ver.get_binary_location("brave")):
313
+ options_list.append("Brave Browser")
314
+ br_order["brave"] = br_count
315
+ br_count += 1
316
+ with suppress(Exception):
317
+ if os.path.exists(detect_b_ver.get_binary_location("comet")):
318
+ options_list.append("Comet Browser")
319
+ br_order["comet"] = br_count
320
+ br_count += 1
321
+ if use_atlas:
322
+ with suppress(Exception):
323
+ if os.path.exists(detect_b_ver.get_binary_location("atlas")):
324
+ options_list.append("Atlas Browser")
325
+ br_order["atlas"] = br_count
326
+ br_count += 1
327
+ brx = tk.StringVar(window)
328
+ if "--use-chromium" in command_args or "--chromium" in command_args:
329
+ brx.set(options_list[1])
330
+ elif (
331
+ "--edge" in command_args
332
+ and "edge" in br_order
333
+ ):
334
+ brx.set(options_list[2])
335
+ use_stealth = False
336
+ elif "--opera" in command_args and "opera" in br_order:
337
+ brx.set(options_list[br_order["opera"]])
338
+ elif "--brave" in command_args and "brave" in br_order:
339
+ brx.set(options_list[br_order["brave"]])
340
+ elif "--comet" in command_args and "comet" in br_order:
341
+ brx.set(options_list[br_order["comet"]])
342
+ elif "--atlas" in command_args and "atlas" in br_order:
343
+ brx.set(options_list[br_order["atlas"]])
344
+ else:
345
+ brx.set(options_list[0])
346
+ question_menu = tk.OptionMenu(window, brx, *options_list)
347
+ question_menu.config(width=16)
348
+ question_menu.pack(pady=(0.6, 0.2))
349
+
350
+ ucb = tk.IntVar()
351
+ chkb = tk.Checkbutton(
352
+ window, text="Stealth Mode / UC + CDP Mode", variable=ucb
353
+ )
354
+ chkb.pack(pady=(0.4, 0.4))
355
+ if use_stealth:
268
356
  chkb.select()
269
- chkb.config(state=tk.DISABLED)
270
- tk.Label(window, text="").pack()
357
+ # chkb.config(state=tk.DISABLED)
358
+
359
+ tk.Label(window, text="\nSelect an output format to use:").pack()
360
+ options_list = ["pytest format / BaseCase"]
361
+ options_list.append("Context Manager / SB()")
362
+ options_list.append("Pure CDP Mode / sb_cdp")
363
+ if use_behave:
364
+ options_list.append("BehaveBDD Gherkin File")
365
+ frx = tk.StringVar(window)
366
+ if use_behave and not use_sb_mgr and not use_sb_cdp:
367
+ frx.set(options_list[3])
368
+ elif use_sb_mgr:
369
+ frx.set(options_list[1])
370
+ elif use_sb_cdp:
371
+ frx.set(options_list[2])
372
+ else:
373
+ frx.set(options_list[0])
374
+ question_menu = tk.OptionMenu(window, frx, *options_list)
375
+ question_menu.config(width=21)
376
+ question_menu.pack(pady=(0.6, 0.2))
377
+
271
378
  url = tk.StringVar()
272
- tk.Label(window, text="Enter the URL to start recording on:").pack()
379
+ label = tk.Label(window, text="Enter a URL to start recording on:")
380
+ label.pack(pady=(20, 0.2))
273
381
  entry = tk.Entry(window, textvariable=url)
382
+ entry.config(width=24)
274
383
  entry.pack()
275
384
  entry.focus()
276
385
  entry.bind(
277
386
  "<Return>",
278
387
  (
279
388
  lambda _: do_recording(
280
- fname.get(), url.get(), cbx.get(), cbb.get(), window
389
+ fname.get(),
390
+ url.get(),
391
+ cbx.get(),
392
+ brx.get(),
393
+ ucb.get(),
394
+ frx.get(),
395
+ window,
281
396
  )
282
397
  ),
283
398
  )
284
- tk.Button(
399
+ # Automatically set focus on URL field when clicking back into the app
400
+ window.bind(
401
+ "<FocusIn>", lambda event: entry.focus_set()
402
+ if event.widget == window
403
+ else None
404
+ )
405
+ style = ttk.Style()
406
+ style.configure(
407
+ "Record.TButton",
408
+ foreground="red",
409
+ font=("TkDefaultFont", 12, "bold"),
410
+ width="8",
411
+ padding=(4, 3, 4, 1)
412
+ )
413
+ ttk.Button(
285
414
  window,
286
415
  text="Record",
287
- fg="red",
416
+ style="Record.TButton",
288
417
  command=lambda: do_recording(
289
- fname.get(), url.get(), cbx.get(), cbb.get(), window
418
+ fname.get(),
419
+ url.get(),
420
+ cbx.get(),
421
+ brx.get(),
422
+ ucb.get(),
423
+ frx.get(),
424
+ window,
290
425
  ),
291
- ).pack()
292
- tk.Label(window, text="").pack()
293
- tk.Label(window, text="Playback recording (Normal Mode):").pack()
294
- tk.Button(
426
+ ).pack(pady=0.2)
427
+ label = tk.Label(window, text="Playback recording (Normal Mode):")
428
+ label.pack(pady=(17, 0))
429
+
430
+ style.configure(
431
+ "Playback.TButton",
432
+ foreground="green",
433
+ font=("TkDefaultFont", 11, "bold"),
434
+ width="8",
435
+ padding=(4, 3, 4, 1)
436
+ )
437
+ ttk.Button(
295
438
  window,
296
439
  text="Playback",
297
- fg="green",
298
- command=lambda: do_playback(fname.get(), cbb.get(), window),
299
- ).pack()
300
- tk.Label(window, text="").pack()
301
- tk.Label(window, text="Playback recording (Demo Mode):").pack()
440
+ style="Playback.TButton",
441
+ command=lambda: do_playback(fname.get(), brx.get(), window),
442
+ ).pack(pady=0.2)
443
+ label = tk.Label(window, text="Playback recording (Demo Mode):")
444
+ label.pack(pady=(13, 0))
302
445
  try:
303
- tk.Button(
446
+ style.configure(
447
+ "PlaybackDemo.TButton",
448
+ foreground="teal",
449
+ font=("TkDefaultFont", 11, "bold"),
450
+ width="18",
451
+ padding=(4, 3, 4, 1)
452
+ )
453
+ ttk.Button(
304
454
  window,
305
455
  text="Playback (Demo Mode)",
306
- fg="teal",
456
+ style="PlaybackDemo.TButton",
307
457
  command=lambda: do_playback(
308
- fname.get(), cbb.get(), window, demo_mode=True
458
+ fname.get(), brx.get(), window, demo_mode=True
309
459
  ),
310
- ).pack()
460
+ ).pack(pady=0.2)
311
461
  except Exception:
312
- tk.Button(
462
+ style.configure(
463
+ "PlaybackDemo.TButton",
464
+ foreground="blue",
465
+ font=("TkDefaultFont", 11, "bold"),
466
+ padding=(4, 3, 4, 1)
467
+ )
468
+ ttk.Button(
313
469
  window,
314
470
  text="Playback (Demo Mode)",
315
- fg="blue",
471
+ style="PlaybackDemo.TButton",
316
472
  command=lambda: do_playback(
317
- fname.get(), cbb.get(), window, demo_mode=True
473
+ fname.get(), brx.get(), window, demo_mode=True
318
474
  ),
319
- ).pack()
475
+ ).pack(pady=0.2)
320
476
 
321
477
  # Bring form window to front
322
478
  send_window_to_front(window)
@@ -328,6 +484,13 @@ def create_tkinter_gui():
328
484
  decoy.deiconify()
329
485
  decoy.destroy()
330
486
  # Start tkinter
487
+ for widget in window.winfo_children():
488
+ if isinstance(
489
+ widget, (tk.Label, tk.Entry, tk.Checkbutton, tk.OptionMenu)
490
+ ):
491
+ widget.configure(font=my_font)
492
+ window.deiconify() # Force the OS to redraw it actively on top
493
+ window.focus_force() # Force keyboard focus into the entry fields
331
494
  window.mainloop()
332
495
  end_program()
333
496
 
@@ -761,6 +761,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
761
761
  cdp.goto = CDPM.goto
762
762
  cdp.reload = CDPM.reload
763
763
  cdp.refresh = CDPM.refresh
764
+ cdp.goto_if_not_url = CDPM.goto_if_not_url
764
765
  cdp.add_handler = CDPM.add_handler
765
766
  cdp.get_event_loop = CDPM.get_event_loop
766
767
  cdp.get_rd_host = CDPM.get_rd_host
@@ -180,6 +180,30 @@ class CDPMethods():
180
180
  def refresh(self, *args, **kwargs):
181
181
  self.reload(*args, **kwargs)
182
182
 
183
+ def goto_if_not_url(self, url):
184
+ """Opens the url in the browser if it's not the current url (*).
185
+ Parameters tagged on by search engines are ignored for this method.
186
+ Eg. If the current url is:
187
+ * https://www.bing.com/search?q=SeleniumBase&source=hp
188
+ And the url privided by this method call is:
189
+ * https://www.bing.com/search?q=SeleniumBase
190
+ Then the urls will be considered the same,
191
+ and no open() action will be performed.
192
+ This method is primarily used by Recorder Mode script generation,
193
+ where both clicks and opens are recorded. So if a click() action
194
+ leads to an goto() action, then the script generator will attempt
195
+ to convert the goto() action into goto_if_not_url() so that the
196
+ same page isn't opened again if the user is already on the page."""
197
+ current_url = self.get_current_url()
198
+ if current_url != url:
199
+ if (
200
+ "?q=" not in current_url
201
+ or "&" not in current_url
202
+ or current_url.find("?q=") >= current_url.find("&")
203
+ or current_url.split("&")[0] != url
204
+ ):
205
+ self.goto(url)
206
+
183
207
  def get_event_loop(self):
184
208
  return self.loop
185
209
 
@@ -2276,6 +2300,10 @@ class CDPMethods():
2276
2300
  x2, y2 = self.get_gui_element_center("div.sliderTarget")
2277
2301
  self.close_active_tab()
2278
2302
  self.switch_to_tab(tab)
2303
+ if shared_utils.is_windows():
2304
+ time.sleep(0.48)
2305
+ self.loop.run_until_complete(self.page.wait(0.1))
2306
+ x2 = x2 + 22.5 # Overshoot drop to maximize compatibility
2279
2307
  self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.55)
2280
2308
  time.sleep(0.25)
2281
2309
  self.loop.run_until_complete(self.page.wait(0.2))