seleniumbase 4.44.1__tar.gz → 4.44.3__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 (161) hide show
  1. {seleniumbase-4.44.1/seleniumbase.egg-info → seleniumbase-4.44.3}/PKG-INFO +1 -1
  2. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/__version__.py +1 -1
  3. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/browser_launcher.py +52 -24
  4. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/sb_cdp.py +99 -27
  5. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/base_case.py +29 -22
  6. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp.py +1 -12
  7. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/browser.py +4 -6
  8. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/config.py +12 -4
  9. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/connection.py +2 -3
  10. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/element.py +75 -18
  11. {seleniumbase-4.44.1 → seleniumbase-4.44.3/seleniumbase.egg-info}/PKG-INFO +1 -1
  12. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/.gitignore +0 -0
  13. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/LICENSE +0 -0
  14. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/MANIFEST.in +0 -0
  15. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/README.md +0 -0
  16. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/install.sh +0 -0
  17. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/pyproject.toml +0 -0
  18. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/pytest.ini +0 -0
  19. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/requirements.txt +0 -0
  20. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/sbase/__init__.py +0 -0
  21. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/sbase/__main__.py +0 -0
  22. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/sbase/steps.py +0 -0
  23. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/__init__.py +0 -0
  24. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/__main__.py +0 -0
  25. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/behave/__init__.py +0 -0
  26. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/behave/behave_helper.py +0 -0
  27. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/behave/behave_sb.py +0 -0
  28. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/behave/steps.py +0 -0
  29. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/__init__.py +0 -0
  30. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/decorators.py +0 -0
  31. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/encryption.py +0 -0
  32. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/exceptions.py +0 -0
  33. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/obfuscate.py +0 -0
  34. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/common/unobfuscate.py +0 -0
  35. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/config/__init__.py +0 -0
  36. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/config/ad_block_list.py +0 -0
  37. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/config/proxy_list.py +0 -0
  38. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/config/settings.py +0 -0
  39. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/__init__.py +0 -0
  40. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/logo_helper.py +0 -0
  41. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/rich_helper.py +0 -0
  42. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/run.py +0 -0
  43. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  44. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  45. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_commander.py +0 -0
  46. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_install.py +0 -0
  47. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  48. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_mkdir.py +0 -0
  49. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
  50. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  51. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_mkrec.py +0 -0
  52. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  53. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_print.py +0 -0
  54. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  55. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/__init__.py +0 -0
  56. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/application_manager.py +0 -0
  57. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/capabilities_parser.py +0 -0
  58. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/colored_traceback.py +0 -0
  59. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/create_db_tables.sql +0 -0
  60. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/detect_b_ver.py +0 -0
  61. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/download_helper.py +0 -0
  62. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/encoded_images.py +0 -0
  63. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/jqc_helper.py +0 -0
  64. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/log_helper.py +0 -0
  65. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/mysql.py +0 -0
  66. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/proxy_helper.py +0 -0
  67. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/recorder_helper.py +0 -0
  68. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/report_helper.py +0 -0
  69. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/s3_manager.py +0 -0
  70. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/sb_driver.py +0 -0
  71. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/session_helper.py +0 -0
  72. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/settings_parser.py +0 -0
  73. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/style_sheet.py +0 -0
  74. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/testcase_manager.py +0 -0
  75. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/tour_helper.py +0 -0
  76. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/core/visual_helper.py +0 -0
  77. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/__init__.py +0 -0
  78. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  79. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  80. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  81. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  82. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  83. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  84. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/extensions/__init__.py +0 -0
  85. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/extensions/ad_block.zip +0 -0
  86. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/extensions/disable_csp.zip +0 -0
  87. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/extensions/recorder.zip +0 -0
  88. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/extensions/sbase_ext.zip +0 -0
  89. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/__init__.py +0 -0
  90. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/constants.py +0 -0
  91. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  92. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/errors.py +0 -0
  93. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/js_utils.py +0 -0
  94. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/page_actions.py +0 -0
  95. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/page_utils.py +0 -0
  96. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/shared_utils.py +0 -0
  97. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/unittest_helper.py +0 -0
  98. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/words.py +0 -0
  99. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  100. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/js_code/__init__.py +0 -0
  101. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/js_code/active_css_js.py +0 -0
  102. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/js_code/live_js.py +0 -0
  103. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/js_code/recorder_js.py +0 -0
  104. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/masterqa/__init__.py +0 -0
  105. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/masterqa/master_qa.py +0 -0
  106. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/__init__.py +0 -0
  107. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/base_plugin.py +0 -0
  108. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/basic_test_info.py +0 -0
  109. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  110. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/driver_manager.py +0 -0
  111. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/page_source.py +0 -0
  112. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/pytest_plugin.py +0 -0
  113. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  114. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/sb_manager.py +0 -0
  115. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/screen_shots.py +0 -0
  116. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/plugins/selenium_plugin.py +0 -0
  117. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/resources/__init__.py +0 -0
  118. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/__init__.py +0 -0
  119. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/chinese.py +0 -0
  120. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/dutch.py +0 -0
  121. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/french.py +0 -0
  122. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/italian.py +0 -0
  123. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/japanese.py +0 -0
  124. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/korean.py +0 -0
  125. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/master_dict.py +0 -0
  126. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/portuguese.py +0 -0
  127. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/russian.py +0 -0
  128. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/spanish.py +0 -0
  129. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/translate/translator.py +0 -0
  130. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/__init__.py +0 -0
  131. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  132. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  133. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  134. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/cdp_driver/tab.py +0 -0
  135. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/dprocess.py +0 -0
  136. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/options.py +0 -0
  137. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/patcher.py +0 -0
  138. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/reactor.py +0 -0
  139. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/undetected/webelement.py +0 -0
  140. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/__init__.py +0 -0
  141. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  142. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  143. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  144. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  145. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  146. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  147. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  148. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  149. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  150. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  151. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  152. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  153. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  154. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase.egg-info/SOURCES.txt +0 -0
  155. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase.egg-info/dependency_links.txt +0 -0
  156. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase.egg-info/entry_points.txt +0 -0
  157. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase.egg-info/requires.txt +0 -0
  158. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/seleniumbase.egg-info/top_level.txt +0 -0
  159. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/setup.cfg +0 -0
  160. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/setup.py +0 -0
  161. {seleniumbase-4.44.1 → seleniumbase-4.44.3}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.44.1
3
+ Version: 4.44.3
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
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.44.1"
2
+ __version__ = "4.44.3"
@@ -293,7 +293,17 @@ def extend_driver(
293
293
  )
294
294
  if hasattr(driver, "proxy"):
295
295
  driver.set_wire_proxy = DM.set_wire_proxy
296
+ completed_loads = []
297
+ for ext_dir in sb_config._ext_dirs:
298
+ with suppress(Exception):
299
+ if ext_dir not in completed_loads:
300
+ completed_loads.append(ext_dir)
301
+ if not use_uc and os.path.exists(os.path.abspath(ext_dir)):
302
+ driver.webextension.install(os.path.abspath(ext_dir))
296
303
  if proxy_auth:
304
+ with suppress(Exception):
305
+ if not use_uc and os.path.exists(proxy_helper.PROXY_DIR_PATH):
306
+ driver.webextension.install(proxy_helper.PROXY_DIR_PATH)
297
307
  # Proxy needs a moment to load in Manifest V3
298
308
  if use_uc:
299
309
  time.sleep(0.14)
@@ -764,6 +774,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
764
774
  cdp.click_active_element = CDPM.click_active_element
765
775
  cdp.click_if_visible = CDPM.click_if_visible
766
776
  cdp.click_visible_elements = CDPM.click_visible_elements
777
+ cdp.click_with_offset = CDPM.click_with_offset
767
778
  cdp.mouse_click = CDPM.mouse_click
768
779
  cdp.get_parent = CDPM.get_parent
769
780
  cdp.remove_element = CDPM.remove_element
@@ -793,11 +804,13 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
793
804
  cdp.set_attributes = CDPM.set_attributes
794
805
  cdp.is_attribute_present = CDPM.is_attribute_present
795
806
  cdp.is_online = CDPM.is_online
807
+ cdp.solve_captcha = CDPM.solve_captcha
796
808
  cdp.gui_press_key = CDPM.gui_press_key
797
809
  cdp.gui_press_keys = CDPM.gui_press_keys
798
810
  cdp.gui_write = CDPM.gui_write
799
811
  cdp.gui_click_x_y = CDPM.gui_click_x_y
800
812
  cdp.gui_click_element = CDPM.gui_click_element
813
+ cdp.gui_click_with_offset = CDPM.gui_click_with_offset
801
814
  cdp.gui_click_captcha = CDPM.gui_click_captcha
802
815
  cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points
803
816
  cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop
@@ -2084,6 +2097,7 @@ def _add_chrome_proxy_extension(
2084
2097
  """Implementation of https://stackoverflow.com/a/35293284/7058266
2085
2098
  for https://stackoverflow.com/q/12848327/7058266
2086
2099
  (Run Selenium on a proxy server that requires authentication.)"""
2100
+ zip_it = False
2087
2101
  args = " ".join(sys.argv)
2088
2102
  bypass_list = proxy_bypass_list
2089
2103
  if (
@@ -2464,13 +2478,27 @@ def _set_chrome_options(
2464
2478
  extension_zip_list = extension_zip.split(",")
2465
2479
  for extension_zip_item in extension_zip_list:
2466
2480
  abs_path = os.path.abspath(extension_zip_item)
2467
- chrome_options.add_extension(abs_path)
2481
+ if os.path.exists(abs_path):
2482
+ try:
2483
+ abs_path_dir = os.path.join(
2484
+ DOWNLOADS_FOLDER, abs_path.split(".")[0]
2485
+ )
2486
+ _unzip_to_new_folder(abs_path, abs_path_dir)
2487
+ chrome_options = add_chrome_ext_dir(
2488
+ chrome_options, abs_path_dir
2489
+ )
2490
+ sb_config._ext_dirs.append(abs_path_dir)
2491
+ except Exception:
2492
+ with suppress(Exception):
2493
+ chrome_options.add_extension(abs_path)
2468
2494
  if extension_dir:
2469
2495
  # load-extension input can be a comma-separated list
2470
2496
  abs_path = (
2471
2497
  ",".join(os.path.abspath(p) for p in extension_dir.split(","))
2472
2498
  )
2473
2499
  chrome_options = add_chrome_ext_dir(chrome_options, abs_path)
2500
+ for p in extension_dir.split(","):
2501
+ sb_config._ext_dirs.append(os.path.abspath(p))
2474
2502
  if (
2475
2503
  page_load_strategy
2476
2504
  and page_load_strategy.lower() in ["eager", "none"]
@@ -2505,37 +2533,32 @@ def _set_chrome_options(
2505
2533
  if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless:
2506
2534
  # Headless Chrome does not support extensions, which are required
2507
2535
  # for disabling the Content Security Policy on Chrome.
2508
- if is_using_uc(undetectable, browser_name):
2509
- disable_csp_zip = DISABLE_CSP_ZIP_PATH
2510
- disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
2511
- _unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
2512
- chrome_options = add_chrome_ext_dir(
2513
- chrome_options, disable_csp_dir
2514
- )
2515
- else:
2516
- chrome_options = _add_chrome_disable_csp_extension(chrome_options)
2536
+ disable_csp_zip = DISABLE_CSP_ZIP_PATH
2537
+ disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
2538
+ _unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
2539
+ chrome_options = add_chrome_ext_dir(
2540
+ chrome_options, disable_csp_dir
2541
+ )
2542
+ sb_config._ext_dirs.append(disable_csp_dir)
2517
2543
  if ad_block_on and not headless:
2518
2544
  # Headless Chrome does not support extensions.
2519
- if is_using_uc(undetectable, browser_name):
2520
- ad_block_zip = AD_BLOCK_ZIP_PATH
2521
- ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
2522
- _unzip_to_new_folder(ad_block_zip, ad_block_dir)
2523
- chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
2524
- else:
2525
- chrome_options = _add_chrome_ad_block_extension(chrome_options)
2545
+ ad_block_zip = AD_BLOCK_ZIP_PATH
2546
+ ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
2547
+ _unzip_to_new_folder(ad_block_zip, ad_block_dir)
2548
+ chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
2549
+ sb_config._ext_dirs.append(ad_block_dir)
2526
2550
  if recorder_ext and not headless:
2527
- if is_using_uc(undetectable, browser_name):
2528
- recorder_zip = RECORDER_ZIP_PATH
2529
- recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
2530
- _unzip_to_new_folder(recorder_zip, recorder_dir)
2531
- chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
2532
- else:
2533
- chrome_options = _add_chrome_recorder_extension(chrome_options)
2551
+ recorder_zip = RECORDER_ZIP_PATH
2552
+ recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
2553
+ _unzip_to_new_folder(recorder_zip, recorder_dir)
2554
+ chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
2555
+ sb_config._ext_dirs.append(recorder_dir)
2534
2556
  if chromium_arg and "sbase" in chromium_arg:
2535
2557
  sbase_ext_zip = SBASE_EXT_ZIP_PATH
2536
2558
  sbase_ext_dir = os.path.join(DOWNLOADS_FOLDER, "sbase_ext")
2537
2559
  _unzip_to_new_folder(sbase_ext_zip, sbase_ext_dir)
2538
2560
  chrome_options = add_chrome_ext_dir(chrome_options, sbase_ext_dir)
2561
+ sb_config._ext_dirs.append(sbase_ext_dir)
2539
2562
  if proxy_string:
2540
2563
  if proxy_auth:
2541
2564
  zip_it = True
@@ -2721,6 +2744,10 @@ def _set_chrome_options(
2721
2744
  chrome_options.add_argument("--disable-features=%s" % d_f_string)
2722
2745
  if proxy_auth:
2723
2746
  chrome_options.add_argument("--test-type")
2747
+ if proxy_auth or sb_config._ext_dirs:
2748
+ if not is_using_uc(undetectable, browser_name):
2749
+ chrome_options.enable_webextensions = True
2750
+ chrome_options.enable_bidi = True
2724
2751
  if (
2725
2752
  is_using_uc(undetectable, browser_name)
2726
2753
  and (
@@ -2985,6 +3012,7 @@ def get_driver(
2985
3012
  device_pixel_ratio=None,
2986
3013
  browser=None, # A duplicate of browser_name to avoid confusion
2987
3014
  ):
3015
+ sb_config._ext_dirs = []
2988
3016
  driver_dir = DRIVER_DIR
2989
3017
  if (
2990
3018
  hasattr(sb_config, "binary_location")
@@ -64,6 +64,11 @@ class CDPMethods():
64
64
  )
65
65
  element.highlight_overlay = lambda: self.__highlight_overlay(element)
66
66
  element.mouse_click = lambda: self.__mouse_click(element)
67
+ element.click_with_offset = (
68
+ lambda *args, **kwargs: self.__mouse_click_with_offset_async(
69
+ element, *args, **kwargs
70
+ )
71
+ )
67
72
  element.mouse_drag = (
68
73
  lambda destination: self.__mouse_drag(element, destination)
69
74
  )
@@ -447,6 +452,15 @@ class CDPMethods():
447
452
  self.loop.run_until_complete(self.page.wait())
448
453
  return result
449
454
 
455
+ def __mouse_click_with_offset_async(self, element, *args, **kwargs):
456
+ result = (
457
+ self.loop.run_until_complete(
458
+ element.mouse_click_with_offset_async(*args, **kwargs)
459
+ )
460
+ )
461
+ self.loop.run_until_complete(self.page.wait())
462
+ return result
463
+
450
464
  def __mouse_drag(self, element, destination):
451
465
  return (
452
466
  self.loop.run_until_complete(element.mouse_drag_async(destination))
@@ -689,10 +703,16 @@ class CDPMethods():
689
703
  self.__slow_mode_pause_if_set()
690
704
  element = self.find_element(selector, timeout=timeout)
691
705
  element.scroll_into_view()
692
- if element.tag_name == "div" or element.tag_name == "input":
693
- element.mouse_click() # Simulated click (not PyAutoGUI)
706
+ tag_name = element.tag_name
707
+ if tag_name:
708
+ tag_name = tag_name.lower().strip()
709
+ if (
710
+ tag_name in ["a", "button", "canvas", "div", "input", "li", "span"]
711
+ and "contains(" not in selector
712
+ ):
713
+ element.mouse_click() # Simulated click (NOT PyAutoGUI)
694
714
  else:
695
- element.click()
715
+ element.click() # Standard CDP click
696
716
  self.__slow_mode_pause_if_set()
697
717
  self.loop.run_until_complete(self.page.wait())
698
718
 
@@ -738,7 +758,7 @@ class CDPMethods():
738
758
  element.scroll_into_view()
739
759
  element.click()
740
760
  click_count += 1
741
- time.sleep(0.042)
761
+ time.sleep(0.044)
742
762
  self.__slow_mode_pause_if_set()
743
763
  self.loop.run_until_complete(self.page.wait())
744
764
  except Exception:
@@ -1757,11 +1777,32 @@ class CDPMethods():
1757
1777
  self.__slow_mode_pause_if_set()
1758
1778
  self.loop.run_until_complete(self.page.wait())
1759
1779
 
1760
- def _on_a_cf_turnstile_page(self):
1761
- time.sleep(0.042)
1762
- source = self.get_page_source()
1780
+ def gui_click_with_offset(
1781
+ self, selector, x, y, timeframe=0.25, center=False
1782
+ ):
1783
+ """Click an element at an {X,Y}-offset location.
1784
+ {0,0} is the top-left corner of the element.
1785
+ If center==True, {0,0} becomes the center of the element.
1786
+ The timeframe is the time spent moving the mouse."""
1787
+ if center:
1788
+ px, py = self.get_gui_element_center(selector)
1789
+ self.gui_click_x_y(px + x, py + y, timeframe=timeframe)
1790
+ else:
1791
+ element_rect = self.get_gui_element_rect(selector)
1792
+ px = element_rect["x"]
1793
+ py = element_rect["y"]
1794
+ self.gui_click_x_y(px + x, py + y, timeframe=timeframe)
1795
+
1796
+ def click_with_offset(self, selector, x, y, center=False):
1797
+ element = self.find_element(selector)
1798
+ element.scroll_into_view()
1799
+ element.click_with_offset(x=x, y=y, center=center)
1800
+ self.__slow_mode_pause_if_set()
1801
+ self.loop.run_until_complete(self.page.wait())
1802
+
1803
+ def _on_a_cf_turnstile_page(self, source=None):
1763
1804
  if not source or len(source) < 400:
1764
- time.sleep(0.22)
1805
+ time.sleep(0.2)
1765
1806
  source = self.get_page_source()
1766
1807
  if (
1767
1808
  'data-callback="onCaptchaSuccess"' in source
@@ -1773,20 +1814,21 @@ class CDPMethods():
1773
1814
  return True
1774
1815
  return False
1775
1816
 
1776
- def _on_a_g_recaptcha_page(self):
1777
- time.sleep(0.042)
1778
- source = self.get_page_source()
1817
+ def _on_a_g_recaptcha_page(self, source=None):
1779
1818
  if not source or len(source) < 400:
1780
- time.sleep(0.22)
1819
+ time.sleep(0.2)
1781
1820
  source = self.get_page_source()
1782
1821
  if (
1783
1822
  'id="recaptcha-token"' in source
1784
1823
  or 'title="reCAPTCHA"' in source
1785
1824
  ):
1786
1825
  return True
1826
+ elif "/recaptcha/api.js" in source:
1827
+ time.sleep(1.6) # Still loading
1828
+ return True
1787
1829
  return False
1788
1830
 
1789
- def __gui_click_recaptcha(self):
1831
+ def __gui_click_recaptcha(self, use_cdp=False):
1790
1832
  selector = None
1791
1833
  if self.is_element_visible('iframe[title="reCAPTCHA"]'):
1792
1834
  selector = 'iframe[title="reCAPTCHA"]'
@@ -1797,19 +1839,39 @@ class CDPMethods():
1797
1839
  element_rect = self.get_gui_element_rect(selector, timeout=1)
1798
1840
  e_x = element_rect["x"]
1799
1841
  e_y = element_rect["y"]
1800
- x = e_x + 29
1801
- y = e_y + 35
1842
+ x_offset = 26
1843
+ y_offset = 35
1844
+ if shared_utils.is_windows():
1845
+ x_offset = 29
1846
+ x = e_x + x_offset
1847
+ y = e_y + y_offset
1802
1848
  sb_config._saved_cf_x_y = (x, y)
1803
1849
  time.sleep(0.08)
1804
- self.gui_click_x_y(x, y)
1850
+ if use_cdp:
1851
+ self.sleep(0.03)
1852
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1853
+ with gui_lock: # Prevent issues with multiple processes
1854
+ self.bring_active_window_to_front()
1855
+ time.sleep(0.056)
1856
+ self.click_with_offset(selector, x_offset, y_offset)
1857
+ time.sleep(0.056)
1858
+ else:
1859
+ self.gui_click_x_y(x, y)
1860
+
1861
+ def solve_captcha(self):
1862
+ self.__click_captcha(use_cdp=True)
1805
1863
 
1806
1864
  def gui_click_captcha(self):
1807
- if self._on_a_cf_turnstile_page():
1808
- pass
1809
- elif self._on_a_g_recaptcha_page():
1810
- self.__gui_click_recaptcha()
1865
+ self.__click_captcha(use_cdp=False)
1866
+
1867
+ def __click_captcha(self, use_cdp=False):
1868
+ """Uses PyAutoGUI unless use_cdp == True"""
1869
+ self.sleep(0.056)
1870
+ source = self.get_page_source()
1871
+ if self._on_a_g_recaptcha_page(source):
1872
+ self.__gui_click_recaptcha(use_cdp)
1811
1873
  return
1812
- else:
1874
+ elif not self._on_a_cf_turnstile_page(source):
1813
1875
  return
1814
1876
  selector = None
1815
1877
  if (
@@ -1970,14 +2032,24 @@ class CDPMethods():
1970
2032
  element_rect = self.get_gui_element_rect(selector, timeout=1)
1971
2033
  e_x = element_rect["x"]
1972
2034
  e_y = element_rect["y"]
1973
- x = e_x + 32
1974
- if not shared_utils.is_windows():
1975
- y = e_y + 32
1976
- else:
1977
- y = e_y + 28
2035
+ x_offset = 32
2036
+ y_offset = 32
2037
+ if shared_utils.is_windows():
2038
+ y_offset = 28
2039
+ x = e_x + x_offset
2040
+ y = e_y + y_offset
1978
2041
  sb_config._saved_cf_x_y = (x, y)
1979
2042
  time.sleep(0.08)
1980
- self.gui_click_x_y(x, y)
2043
+ if use_cdp:
2044
+ self.sleep(0.03)
2045
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2046
+ with gui_lock: # Prevent issues with multiple processes
2047
+ self.bring_active_window_to_front()
2048
+ time.sleep(0.056)
2049
+ self.click_with_offset(selector, x_offset, y_offset)
2050
+ time.sleep(0.056)
2051
+ else:
2052
+ self.gui_click_x_y(x, y)
1981
2053
 
1982
2054
  def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
1983
2055
  self.__install_pyautogui_if_missing()
@@ -4694,7 +4694,7 @@ class BaseCase(unittest.TestCase):
4694
4694
  if not os.path.exists(file_path):
4695
4695
  os.makedirs(file_path)
4696
4696
  cookies_file_path = os.path.join(file_path, name)
4697
- cookies_file = open(cookies_file_path, "w+", encoding="utf-8")
4697
+ cookies_file = open(cookies_file_path, mode="w+", encoding="utf-8")
4698
4698
  cookies_file.writelines(json_cookies)
4699
4699
  cookies_file.close()
4700
4700
 
@@ -5008,6 +5008,9 @@ class BaseCase(unittest.TestCase):
5008
5008
  self.get_new_driver(undetectable=True)
5009
5009
  self.driver.uc_open_with_cdp_mode(url, **kwargs)
5010
5010
  self.cdp = self.driver.cdp
5011
+ if hasattr(self.cdp, "solve_captcha"):
5012
+ self.solve_captcha = self.cdp.solve_captcha
5013
+ self.undetectable = True
5011
5014
 
5012
5015
  def activate_recorder(self):
5013
5016
  """Activate Recorder Mode on the current tab/window.
@@ -5748,7 +5751,7 @@ class BaseCase(unittest.TestCase):
5748
5751
  extra_file_name = "__init__.py"
5749
5752
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5750
5753
  if not os.path.exists(extra_file_path):
5751
- out_file = open(extra_file_path, "w+", "utf-8")
5754
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5752
5755
  out_file.writelines("\r\n".join(data))
5753
5756
  out_file.close()
5754
5757
  sys.stdout.write("\nCreated recordings%s__init__.py" % os.sep)
@@ -5796,7 +5799,7 @@ class BaseCase(unittest.TestCase):
5796
5799
  extra_file_name = "pytest.ini"
5797
5800
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5798
5801
  if not os.path.exists(extra_file_path):
5799
- out_file = open(extra_file_path, "w+", "utf-8")
5802
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5800
5803
  out_file.writelines("\r\n".join(data))
5801
5804
  out_file.close()
5802
5805
  sys.stdout.write("\nCreated recordings%spytest.ini" % os.sep)
@@ -5817,7 +5820,7 @@ class BaseCase(unittest.TestCase):
5817
5820
  extra_file_name = "setup.cfg"
5818
5821
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5819
5822
  if not os.path.exists(extra_file_path):
5820
- out_file = open(extra_file_path, "w+", "utf-8")
5823
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5821
5824
  out_file.writelines("\r\n".join(data))
5822
5825
  out_file.close()
5823
5826
  sys.stdout.write("\nCreated recordings%ssetup.cfg" % os.sep)
@@ -5835,7 +5838,7 @@ class BaseCase(unittest.TestCase):
5835
5838
  elif context_filename:
5836
5839
  file_name = context_filename
5837
5840
  file_path = os.path.join(recordings_folder, file_name)
5838
- out_file = open(file_path, "w+", "utf-8")
5841
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5839
5842
  out_file.writelines("\r\n".join(data))
5840
5843
  out_file.close()
5841
5844
  rec_message = ">>> RECORDING SAVED as: "
@@ -5937,7 +5940,7 @@ class BaseCase(unittest.TestCase):
5937
5940
  file_name = sb_config.behave_scenario.filename.replace(".", "_")
5938
5941
  file_name = file_name.split("/")[-1].split("\\")[-1] + "_rec.feature"
5939
5942
  file_path = os.path.join(features_folder, file_name)
5940
- out_file = open(file_path, "w+", "utf-8")
5943
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5941
5944
  out_file.writelines("\r\n".join(data))
5942
5945
  out_file.close()
5943
5946
 
@@ -5975,7 +5978,7 @@ class BaseCase(unittest.TestCase):
5975
5978
  file_name = "__init__.py"
5976
5979
  file_path = os.path.join(features_folder, file_name)
5977
5980
  if not os.path.exists(file_path):
5978
- out_file = open(file_path, "w+", "utf-8")
5981
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5979
5982
  out_file.writelines("\r\n".join(data))
5980
5983
  out_file.close()
5981
5984
  print("Created recordings/features/__init__.py")
@@ -5988,7 +5991,7 @@ class BaseCase(unittest.TestCase):
5988
5991
  file_name = "behave.ini"
5989
5992
  file_path = os.path.join(features_folder, file_name)
5990
5993
  if not os.path.exists(file_path):
5991
- out_file = open(file_path, "w+", "utf-8")
5994
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5992
5995
  out_file.writelines("\r\n".join(data))
5993
5996
  out_file.close()
5994
5997
  print("Created recordings/features/behave.ini")
@@ -6027,7 +6030,7 @@ class BaseCase(unittest.TestCase):
6027
6030
  file_name = "environment.py"
6028
6031
  file_path = os.path.join(features_folder, file_name)
6029
6032
  if not os.path.exists(file_path):
6030
- out_file = open(file_path, "w+", "utf-8")
6033
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6031
6034
  out_file.writelines("\r\n".join(data))
6032
6035
  out_file.close()
6033
6036
  print("Created recordings/features/environment.py")
@@ -6037,7 +6040,7 @@ class BaseCase(unittest.TestCase):
6037
6040
  file_name = "__init__.py"
6038
6041
  file_path = os.path.join(steps_folder, file_name)
6039
6042
  if not os.path.exists(file_path):
6040
- out_file = open(file_path, "w+", "utf-8")
6043
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6041
6044
  out_file.writelines("\r\n".join(data))
6042
6045
  out_file.close()
6043
6046
  print("Created recordings/features/steps/__init__.py")
@@ -6048,7 +6051,7 @@ class BaseCase(unittest.TestCase):
6048
6051
  file_name = "imported.py"
6049
6052
  file_path = os.path.join(steps_folder, file_name)
6050
6053
  if not os.path.exists(file_path):
6051
- out_file = open(file_path, "w+", "utf-8")
6054
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6052
6055
  out_file.writelines("\r\n".join(data))
6053
6056
  out_file.close()
6054
6057
  print("Created recordings/features/steps/imported.py")
@@ -11117,7 +11120,7 @@ class BaseCase(unittest.TestCase):
11117
11120
  return # Skip the rest when deferred visual asserts are used
11118
11121
  the_html = visual_helper.get_sbs_html()
11119
11122
  file_path = os.path.join(test_logpath, constants.SideBySide.HTML_FILE)
11120
- out_file = open(file_path, "w+", encoding="utf-8")
11123
+ out_file = open(file_path, mode="w+", encoding="utf-8")
11121
11124
  out_file.writelines(the_html)
11122
11125
  out_file.close()
11123
11126
 
@@ -11277,16 +11280,16 @@ class BaseCase(unittest.TestCase):
11277
11280
  self.save_screenshot(
11278
11281
  baseline_png, visual_baseline_path, selector="body"
11279
11282
  )
11280
- out_file = open(page_url_file, "w+", encoding="utf-8")
11283
+ out_file = open(page_url_file, mode="w+", encoding="utf-8")
11281
11284
  out_file.writelines(page_url)
11282
11285
  out_file.close()
11283
- out_file = open(level_1_file, "w+", encoding="utf-8")
11286
+ out_file = open(level_1_file, mode="w+", encoding="utf-8")
11284
11287
  out_file.writelines(json.dumps(level_1))
11285
11288
  out_file.close()
11286
- out_file = open(level_2_file, "w+", encoding="utf-8")
11289
+ out_file = open(level_2_file, mode="w+", encoding="utf-8")
11287
11290
  out_file.writelines(json.dumps(level_2))
11288
11291
  out_file.close()
11289
- out_file = open(level_3_file, "w+", encoding="utf-8")
11292
+ out_file = open(level_3_file, mode="w+", encoding="utf-8")
11290
11293
  out_file.writelines(json.dumps(level_3))
11291
11294
  out_file.close()
11292
11295
 
@@ -11425,7 +11428,7 @@ class BaseCase(unittest.TestCase):
11425
11428
  alpha_n_d_name = "".join([x if x.isalnum() else "_" for x in name])
11426
11429
  side_by_side_name = "side_by_side_%s.html" % alpha_n_d_name
11427
11430
  file_path = os.path.join(test_logpath, side_by_side_name)
11428
- out_file = open(file_path, "w+", encoding="utf-8")
11431
+ out_file = open(file_path, mode="w+", encoding="utf-8")
11429
11432
  out_file.writelines(the_html)
11430
11433
  out_file.close()
11431
11434
 
@@ -11452,6 +11455,7 @@ class BaseCase(unittest.TestCase):
11452
11455
  if cdp_swap_needed:
11453
11456
  if not self.cdp:
11454
11457
  self.cdp = self.driver.cdp
11458
+ self.undetectable = True
11455
11459
  return True
11456
11460
  else:
11457
11461
  return False
@@ -12116,7 +12120,7 @@ class BaseCase(unittest.TestCase):
12116
12120
  with suppress(Exception):
12117
12121
  os.makedirs(saved_presentations_folder)
12118
12122
  file_path = os.path.join(saved_presentations_folder, filename)
12119
- out_file = open(file_path, "w+", encoding="utf-8")
12123
+ out_file = open(file_path, mode="w+", encoding="utf-8")
12120
12124
  out_file.writelines(the_html)
12121
12125
  out_file.close()
12122
12126
  if self._output_file_saves:
@@ -12811,7 +12815,7 @@ class BaseCase(unittest.TestCase):
12811
12815
  with suppress(Exception):
12812
12816
  os.makedirs(saved_charts_folder)
12813
12817
  file_path = os.path.join(saved_charts_folder, filename)
12814
- out_file = open(file_path, "w+", encoding="utf-8")
12818
+ out_file = open(file_path, mode="w+", encoding="utf-8")
12815
12819
  out_file.writelines(the_html)
12816
12820
  out_file.close()
12817
12821
  if self._output_file_saves:
@@ -13970,6 +13974,9 @@ class BaseCase(unittest.TestCase):
13970
13974
  timeout=None,
13971
13975
  center=None,
13972
13976
  ):
13977
+ if self.__is_cdp_swap_needed():
13978
+ self.cdp.click_with_offset(selector, x, y, center=center)
13979
+ return
13973
13980
  self.wait_for_ready_state_complete()
13974
13981
  if self.__needs_minimum_wait():
13975
13982
  time.sleep(0.14)
@@ -16371,7 +16378,7 @@ class BaseCase(unittest.TestCase):
16371
16378
  dash_pie = json.dumps(sb_config._saved_dashboard_pie)
16372
16379
  dash_pie_loc = constants.Dashboard.DASH_PIE
16373
16380
  pie_path = os.path.join(abs_path, dash_pie_loc)
16374
- pie_file = open(pie_path, "w+", encoding="utf-8")
16381
+ pie_file = open(pie_path, mode="w+", encoding="utf-8")
16375
16382
  pie_file.writelines(dash_pie)
16376
16383
  pie_file.close()
16377
16384
  DASH_PIE_PNG_1 = constants.Dashboard.get_dash_pie_1()
@@ -16531,7 +16538,7 @@ class BaseCase(unittest.TestCase):
16531
16538
  )
16532
16539
  abs_path = os.path.abspath(".")
16533
16540
  file_path = os.path.join(abs_path, "dashboard.html")
16534
- out_file = open(file_path, "w+", encoding="utf-8")
16541
+ out_file = open(file_path, mode="w+", encoding="utf-8")
16535
16542
  out_file.writelines(the_html)
16536
16543
  out_file.close()
16537
16544
  sb_config._dash_html = the_html
@@ -16544,7 +16551,7 @@ class BaseCase(unittest.TestCase):
16544
16551
  dash_json = json.dumps((_results, _display_id, _rt, _tlp, d_stats))
16545
16552
  dash_json_loc = constants.Dashboard.DASH_JSON
16546
16553
  dash_jsonpath = os.path.join(abs_path, dash_json_loc)
16547
- dash_json_file = open(dash_jsonpath, "w+", encoding="utf-8")
16554
+ dash_json_file = open(dash_jsonpath, mode="w+", encoding="utf-8")
16548
16555
  dash_json_file.writelines(dash_json)
16549
16556
  dash_json_file.close()
16550
16557
 
@@ -1,9 +1,7 @@
1
- import fasteners
2
1
  import json
3
2
  import logging
4
3
  import requests
5
- from seleniumbase.fixtures import constants
6
- from seleniumbase.fixtures import shared_utils
4
+ import websockets
7
5
 
8
6
  log = logging.getLogger(__name__)
9
7
 
@@ -107,15 +105,6 @@ class CDP:
107
105
  return resp.json()
108
106
 
109
107
  async def send(self, method, params):
110
- pip_find_lock = fasteners.InterProcessLock(
111
- constants.PipInstall.FINDLOCK
112
- )
113
- with pip_find_lock:
114
- try:
115
- import websockets
116
- except Exception:
117
- shared_utils.pip_install("websockets")
118
- import websockets
119
108
  self._reqid += 1
120
109
  async with websockets.connect(self.wsurl) as ws:
121
110
  await ws.send(
@@ -350,8 +350,7 @@ class Browser:
350
350
  if _cdp_geolocation:
351
351
  await connection.send(cdp.page.navigate("about:blank"))
352
352
  await connection.set_geolocation(_cdp_geolocation)
353
- # This part isn't needed now, but may be needed later
354
- """
353
+ # (The code below is for the Chrome 142 extension fix)
355
354
  if (
356
355
  hasattr(sb_config, "_cdp_proxy")
357
356
  and "@" in sb_config._cdp_proxy
@@ -363,7 +362,6 @@ class Browser:
363
362
  proxy_pass = username_and_password.split(":")[1]
364
363
  await connection.set_auth(proxy_user, proxy_pass, self.tabs[0])
365
364
  time.sleep(0.25)
366
- """
367
365
  if "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
368
366
  username_and_password = kwargs["auth"]
369
367
  proxy_user = username_and_password.split(":")[0]
@@ -375,12 +373,12 @@ class Browser:
375
373
  cdp.page.navigate(url)
376
374
  )
377
375
  if _cdp_recorder:
378
- pass # (The code below was for the Chrome 137 extension fix)
379
- '''from seleniumbase.js_code.recorder_js import recorder_js
376
+ # (The code below is for the Chrome 142 extension fix)
377
+ from seleniumbase.js_code.recorder_js import recorder_js
380
378
  recorder_code = (
381
379
  """window.onload = function() { %s };""" % recorder_js
382
380
  )
383
- await connection.send(cdp.runtime.evaluate(recorder_code))'''
381
+ await connection.send(cdp.runtime.evaluate(recorder_code))
384
382
  # Update the frame_id on the tab
385
383
  connection.frame_id = frame_id
386
384
  connection.browser = self
@@ -79,7 +79,8 @@ class Config:
79
79
  if not browser_args:
80
80
  browser_args = []
81
81
  if not user_data_dir:
82
- self._user_data_dir = temp_profile_dir()
82
+ self.user_data_dir = temp_profile_dir()
83
+ self._user_data_dir = self.user_data_dir
83
84
  self._custom_data_dir = False
84
85
  else:
85
86
  self.user_data_dir = user_data_dir
@@ -315,10 +316,13 @@ def find_chrome_executable(return_all=False):
315
316
  for item in os.environ.get("PATH").split(os.pathsep):
316
317
  for subitem in (
317
318
  "google-chrome",
319
+ "google-chrome-stable",
320
+ "google-chrome-beta",
321
+ "google-chrome-dev",
322
+ "google-chrome-unstable",
323
+ "chrome",
318
324
  "chromium",
319
325
  "chromium-browser",
320
- "chrome",
321
- "google-chrome-stable",
322
326
  ):
323
327
  candidates.append(os.sep.join((item, subitem)))
324
328
  if "darwin" in sys.platform:
@@ -347,7 +351,11 @@ def find_chrome_executable(return_all=False):
347
351
  )
348
352
  rv = []
349
353
  for candidate in candidates:
350
- if os.path.exists(candidate) and os.access(candidate, os.X_OK):
354
+ if (
355
+ os.path.exists(candidate)
356
+ and os.access(candidate, os.R_OK)
357
+ and os.access(candidate, os.X_OK)
358
+ ):
351
359
  logger.debug("%s is a valid candidate... " % candidate)
352
360
  rv.append(candidate)
353
361
  else: