seleniumbase 4.41.5__tar.gz → 4.41.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. {seleniumbase-4.41.5/seleniumbase.egg-info → seleniumbase-4.41.7}/PKG-INFO +49 -22
  2. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/README.md +36 -11
  3. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/requirements.txt +6 -6
  4. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/sbase/steps.py +9 -0
  5. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/__version__.py +1 -1
  6. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/behave/behave_helper.py +2 -0
  7. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/browser_launcher.py +1 -0
  8. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/recorder_helper.py +3 -0
  9. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/sb_cdp.py +3 -0
  10. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/base_case.py +92 -1
  11. {seleniumbase-4.41.5 → seleniumbase-4.41.7/seleniumbase.egg-info}/PKG-INFO +49 -22
  12. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase.egg-info/requires.txt +12 -10
  13. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/setup.py +12 -10
  14. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/.gitignore +0 -0
  15. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/LICENSE +0 -0
  16. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/MANIFEST.in +0 -0
  17. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/install.sh +0 -0
  18. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/pyproject.toml +0 -0
  19. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/pytest.ini +0 -0
  20. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/sbase/__init__.py +0 -0
  21. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/sbase/__main__.py +0 -0
  22. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/__init__.py +0 -0
  23. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/__main__.py +0 -0
  24. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/behave/__init__.py +0 -0
  25. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/behave/behave_sb.py +0 -0
  26. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/behave/steps.py +0 -0
  27. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/__init__.py +0 -0
  28. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/decorators.py +0 -0
  29. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/encryption.py +0 -0
  30. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/exceptions.py +0 -0
  31. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/obfuscate.py +0 -0
  32. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/common/unobfuscate.py +0 -0
  33. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/config/__init__.py +0 -0
  34. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/config/ad_block_list.py +0 -0
  35. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/config/proxy_list.py +0 -0
  36. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/config/settings.py +0 -0
  37. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/__init__.py +0 -0
  38. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/logo_helper.py +0 -0
  39. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/rich_helper.py +0 -0
  40. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/run.py +0 -0
  41. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_behave_gui.py +0 -0
  42. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_caseplans.py +0 -0
  43. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_commander.py +0 -0
  44. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_install.py +0 -0
  45. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_mkchart.py +0 -0
  46. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_mkdir.py +0 -0
  47. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_mkfile.py +0 -0
  48. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_mkpres.py +0 -0
  49. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_mkrec.py +0 -0
  50. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_objectify.py +0 -0
  51. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_print.py +0 -0
  52. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/console_scripts/sb_recorder.py +0 -0
  53. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/__init__.py +0 -0
  54. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/application_manager.py +0 -0
  55. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/capabilities_parser.py +0 -0
  56. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/colored_traceback.py +0 -0
  57. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/create_db_tables.sql +0 -0
  58. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/detect_b_ver.py +0 -0
  59. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/download_helper.py +0 -0
  60. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/encoded_images.py +0 -0
  61. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/jqc_helper.py +0 -0
  62. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/log_helper.py +0 -0
  63. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/mysql.py +0 -0
  64. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/proxy_helper.py +0 -0
  65. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/report_helper.py +0 -0
  66. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/s3_manager.py +0 -0
  67. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/sb_driver.py +0 -0
  68. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/session_helper.py +0 -0
  69. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/settings_parser.py +0 -0
  70. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/style_sheet.py +0 -0
  71. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/testcase_manager.py +0 -0
  72. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/tour_helper.py +0 -0
  73. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/core/visual_helper.py +0 -0
  74. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/drivers/__init__.py +0 -0
  75. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/drivers/cft_drivers/__init__.py +0 -0
  76. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/drivers/chs_drivers/__init__.py +0 -0
  77. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/extensions/__init__.py +0 -0
  78. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/extensions/ad_block.zip +0 -0
  79. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/extensions/disable_csp.zip +0 -0
  80. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/extensions/recorder.zip +0 -0
  81. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/extensions/sbase_ext.zip +0 -0
  82. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/__init__.py +0 -0
  83. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/constants.py +0 -0
  84. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/css_to_xpath.py +0 -0
  85. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/errors.py +0 -0
  86. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/js_utils.py +0 -0
  87. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/page_actions.py +0 -0
  88. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/page_utils.py +0 -0
  89. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/shared_utils.py +0 -0
  90. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/unittest_helper.py +0 -0
  91. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/words.py +0 -0
  92. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/fixtures/xpath_to_css.py +0 -0
  93. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/js_code/__init__.py +0 -0
  94. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/js_code/active_css_js.py +0 -0
  95. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/js_code/live_js.py +0 -0
  96. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/js_code/recorder_js.py +0 -0
  97. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/masterqa/__init__.py +0 -0
  98. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/masterqa/master_qa.py +0 -0
  99. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/__init__.py +0 -0
  100. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/base_plugin.py +0 -0
  101. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/basic_test_info.py +0 -0
  102. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/db_reporting_plugin.py +0 -0
  103. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/driver_manager.py +0 -0
  104. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/page_source.py +0 -0
  105. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/pytest_plugin.py +0 -0
  106. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/s3_logging_plugin.py +0 -0
  107. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/sb_manager.py +0 -0
  108. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/screen_shots.py +0 -0
  109. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/plugins/selenium_plugin.py +0 -0
  110. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/resources/__init__.py +0 -0
  111. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/__init__.py +0 -0
  112. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/chinese.py +0 -0
  113. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/dutch.py +0 -0
  114. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/french.py +0 -0
  115. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/italian.py +0 -0
  116. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/japanese.py +0 -0
  117. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/korean.py +0 -0
  118. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/master_dict.py +0 -0
  119. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/portuguese.py +0 -0
  120. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/russian.py +0 -0
  121. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/spanish.py +0 -0
  122. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/translate/translator.py +0 -0
  123. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/__init__.py +0 -0
  124. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp.py +0 -0
  125. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/__init__.py +0 -0
  126. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/_contradict.py +0 -0
  127. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/browser.py +0 -0
  128. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/cdp_util.py +0 -0
  129. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/config.py +0 -0
  130. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/connection.py +0 -0
  131. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/element.py +0 -0
  132. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/cdp_driver/tab.py +0 -0
  133. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/dprocess.py +0 -0
  134. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/options.py +0 -0
  135. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/patcher.py +0 -0
  136. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/reactor.py +0 -0
  137. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/undetected/webelement.py +0 -0
  138. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/__init__.py +0 -0
  139. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/__init__.py +0 -0
  140. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/download_selenium_server.py +0 -0
  141. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/font_color +0 -0
  142. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/grid-hub +0 -0
  143. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/grid-node +0 -0
  144. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/grid_hub.py +0 -0
  145. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/grid_node.py +0 -0
  146. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/register-grid-node.bat +0 -0
  147. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/register-grid-node.sh +0 -0
  148. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.bat +0 -0
  149. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_grid/start-grid-hub.sh +0 -0
  150. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_ide/__init__.py +0 -0
  151. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase/utilities/selenium_ide/convert_ide.py +0 -0
  152. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase.egg-info/SOURCES.txt +0 -0
  153. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase.egg-info/dependency_links.txt +0 -0
  154. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase.egg-info/entry_points.txt +0 -0
  155. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/seleniumbase.egg-info/top_level.txt +0 -0
  156. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/setup.cfg +0 -0
  157. {seleniumbase-4.41.5 → seleniumbase-4.41.7}/virtualenv_install.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seleniumbase
3
- Version: 4.41.5
3
+ Version: 4.41.7
4
4
  Summary: A complete web automation framework for end-to-end testing.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -75,7 +75,7 @@ Requires-Dist: filelock~=3.16.1; python_version < "3.9"
75
75
  Requires-Dist: filelock>=3.19.1; python_version >= "3.9"
76
76
  Requires-Dist: fasteners>=0.20
77
77
  Requires-Dist: mycdp>=1.2.0
78
- Requires-Dist: pynose>=1.5.4
78
+ Requires-Dist: pynose>=1.5.5
79
79
  Requires-Dist: platformdirs>=4.3.6; python_version < "3.9"
80
80
  Requires-Dist: platformdirs>=4.4.0; python_version >= "3.9"
81
81
  Requires-Dist: typing-extensions>=4.13.2
@@ -98,15 +98,15 @@ Requires-Dist: charset-normalizer<4,>=3.4.3
98
98
  Requires-Dist: urllib3<2,>=1.26.20; python_version < "3.10"
99
99
  Requires-Dist: urllib3<2.6.0,>=1.26.20; python_version >= "3.10"
100
100
  Requires-Dist: requests==2.32.4; python_version < "3.9"
101
- Requires-Dist: requests<2.33,>=2.32.5; python_version >= "3.9"
101
+ Requires-Dist: requests~=2.32.5; python_version >= "3.9"
102
102
  Requires-Dist: sniffio==1.3.1
103
103
  Requires-Dist: h11==0.16.0
104
104
  Requires-Dist: outcome==1.3.0.post0
105
105
  Requires-Dist: trio==0.27.0; python_version < "3.9"
106
- Requires-Dist: trio==0.30.0; python_version >= "3.9"
107
- Requires-Dist: trio-websocket==0.12.2
106
+ Requires-Dist: trio~=0.30.0; python_version >= "3.9"
107
+ Requires-Dist: trio-websocket~=0.12.2
108
108
  Requires-Dist: wsproto==1.2.0
109
- Requires-Dist: websocket-client==1.8.0
109
+ Requires-Dist: websocket-client~=1.8.0
110
110
  Requires-Dist: selenium==4.27.1; python_version < "3.9"
111
111
  Requires-Dist: selenium==4.32.0; python_version >= "3.9" and python_version < "3.10"
112
112
  Requires-Dist: selenium==4.35.0; python_version >= "3.10"
@@ -145,7 +145,7 @@ Provides-Extra: coverage
145
145
  Requires-Dist: coverage>=7.6.1; python_version < "3.9" and extra == "coverage"
146
146
  Requires-Dist: coverage>=7.10.6; python_version >= "3.9" and extra == "coverage"
147
147
  Requires-Dist: pytest-cov>=5.0.0; python_version < "3.9" and extra == "coverage"
148
- Requires-Dist: pytest-cov>=6.3.0; python_version >= "3.9" and extra == "coverage"
148
+ Requires-Dist: pytest-cov>=7.0.0; python_version >= "3.9" and extra == "coverage"
149
149
  Provides-Extra: flake8
150
150
  Requires-Dist: flake8==5.0.4; python_version < "3.9" and extra == "flake8"
151
151
  Requires-Dist: flake8==7.3.0; python_version >= "3.9" and extra == "flake8"
@@ -164,9 +164,11 @@ 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"
168
- Requires-Dist: cffi==1.17.1; extra == "pdfminer"
169
- Requires-Dist: pycparser==2.22; extra == "pdfminer"
167
+ Requires-Dist: cryptography==46.0.1; python_version >= "3.9" and extra == "pdfminer"
168
+ Requires-Dist: cffi==1.17.1; python_version < "3.9" and extra == "pdfminer"
169
+ Requires-Dist: cffi==2.0.0; python_version >= "3.9" and extra == "pdfminer"
170
+ Requires-Dist: pycparser==2.22; python_version < "3.9" and extra == "pdfminer"
171
+ Requires-Dist: pycparser==2.23; python_version >= "3.9" and extra == "pdfminer"
170
172
  Provides-Extra: pillow
171
173
  Requires-Dist: Pillow>=10.4.0; python_version < "3.9" and extra == "pillow"
172
174
  Requires-Dist: Pillow>=11.3.0; python_version >= "3.9" and extra == "pillow"
@@ -175,7 +177,7 @@ Requires-Dist: pip-system-certs==4.0; platform_system == "Windows" and extra ==
175
177
  Provides-Extra: proxy
176
178
  Requires-Dist: proxy.py==2.4.3; extra == "proxy"
177
179
  Provides-Extra: psutil
178
- Requires-Dist: psutil==7.0.0; extra == "psutil"
180
+ Requires-Dist: psutil==7.1.0; extra == "psutil"
179
181
  Provides-Extra: pyautogui
180
182
  Requires-Dist: PyAutoGUI==0.9.54; extra == "pyautogui"
181
183
  Provides-Extra: selenium-stealth
@@ -273,26 +275,31 @@ Dynamic: summary
273
275
 
274
276
  --------
275
277
 
276
- <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. Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">SeleniumBase/examples/raw_google.py</a>, which exports the search results into different formats (PDF, HTML, PNG):</p>
277
279
 
278
280
  ```python
279
281
  from seleniumbase import SB
280
282
 
281
283
  with SB(test=True, uc=True) as sb:
282
284
  sb.open("https://google.com/ncr")
283
- sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
284
- sb.click('[href*="github.com/seleniumbase/"]')
285
- sb.save_screenshot_to_logs() # ./latest_logs/
285
+ sb.type('[title="Search"]', "SeleniumBase GitHub page")
286
+ sb.click("div:not([jsname]) > * > input")
286
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()
287
294
  ```
288
295
 
289
296
  > `python raw_google.py`
290
297
 
291
- <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="440" /></a>
292
299
 
293
300
  --------
294
301
 
295
- <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>
296
303
 
297
304
  ```python
298
305
  from seleniumbase import SB
@@ -300,16 +307,36 @@ from seleniumbase import SB
300
307
  with SB(uc=True, test=True, locale="en") as sb:
301
308
  url = "https://gitlab.com/users/sign_in"
302
309
  sb.activate_cdp_mode(url)
303
- sb.sleep(1)
310
+ sb.sleep(2.2)
304
311
  sb.uc_gui_click_captcha()
305
- 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)
306
317
  ```
307
318
 
308
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">
309
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
+
310
337
  --------
311
338
 
312
- <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>
313
340
 
314
341
  ```python
315
342
  from seleniumbase import BaseCase
@@ -341,7 +368,7 @@ class MyTestClass(BaseCase):
341
368
 
342
369
  --------
343
370
 
344
- <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>
345
372
 
346
373
  ```zsh
347
374
  pytest test_coffee_cart.py --demo
@@ -355,7 +382,7 @@ pytest test_coffee_cart.py --demo
355
382
 
356
383
  <a id="multiple_examples"></a>
357
384
 
358
- <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>
359
386
 
360
387
  ```zsh
361
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. Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">SeleniumBase/examples/raw_google.py</a>, which exports the search 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="440" /></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
@@ -13,7 +13,7 @@ filelock~=3.16.1;python_version<"3.9"
13
13
  filelock>=3.19.1;python_version>="3.9"
14
14
  fasteners>=0.20
15
15
  mycdp>=1.2.0
16
- pynose>=1.5.4
16
+ pynose>=1.5.5
17
17
  platformdirs>=4.3.6;python_version<"3.9"
18
18
  platformdirs>=4.4.0;python_version>="3.9"
19
19
  typing-extensions>=4.13.2
@@ -36,15 +36,15 @@ charset-normalizer>=3.4.3,<4
36
36
  urllib3>=1.26.20,<2;python_version<"3.10"
37
37
  urllib3>=1.26.20,<2.6.0;python_version>="3.10"
38
38
  requests==2.32.4;python_version<"3.9"
39
- requests>=2.32.5,<2.33;python_version>="3.9"
39
+ requests~=2.32.5;python_version>="3.9"
40
40
  sniffio==1.3.1
41
41
  h11==0.16.0
42
42
  outcome==1.3.0.post0
43
43
  trio==0.27.0;python_version<"3.9"
44
- trio==0.30.0;python_version>="3.9"
45
- trio-websocket==0.12.2
44
+ trio~=0.30.0;python_version>="3.9"
45
+ trio-websocket~=0.12.2
46
46
  wsproto==1.2.0
47
- websocket-client==1.8.0
47
+ websocket-client~=1.8.0
48
48
  selenium==4.27.1;python_version<"3.9"
49
49
  selenium==4.32.0;python_version>="3.9" and python_version<"3.10"
50
50
  selenium==4.35.0;python_version>="3.10"
@@ -82,7 +82,7 @@ rich>=14.1.0,<15
82
82
  coverage>=7.6.1;python_version<"3.9"
83
83
  coverage>=7.10.6;python_version>="3.9"
84
84
  pytest-cov>=5.0.0;python_version<"3.9"
85
- pytest-cov>=6.3.0;python_version>="3.9"
85
+ pytest-cov>=7.0.0;python_version>="3.9"
86
86
  flake8==5.0.4;python_version<"3.9"
87
87
  flake8==7.3.0;python_version>="3.9"
88
88
  mccabe==0.7.0
@@ -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.5"
2
+ __version__ = "4.41.7"
@@ -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":
@@ -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)
@@ -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)
@@ -2571,6 +2571,9 @@ class CDPMethods():
2571
2571
  filename = os.path.join(folder, name)
2572
2572
  self.loop.run_until_complete(self.page.print_to_pdf(filename))
2573
2573
 
2574
+ def save_as_pdf(self, *args, **kwargs):
2575
+ self.print_to_pdf(*args, **kwargs)
2576
+
2574
2577
 
2575
2578
  class Chrome(CDPMethods):
2576
2579
  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")
@@ -7245,6 +7294,7 @@ class BaseCase(unittest.TestCase):
7245
7294
  page_search = [page]
7246
7295
  else:
7247
7296
  page_search = None
7297
+ logging.getLogger("pdfminer").setLevel(logging.ERROR)
7248
7298
  pdf_text = extract_text(
7249
7299
  file_path,
7250
7300
  password="",
@@ -7552,6 +7602,47 @@ class BaseCase(unittest.TestCase):
7552
7602
  folder = constants.Files.DOWNLOADS_FOLDER
7553
7603
  return page_utils._get_file_data(folder, file_name)
7554
7604
 
7605
+ def print_to_pdf(self, name, folder=None):
7606
+ """Saves the current page as a PDF.
7607
+ If no folder is specified, uses the folder where pytest was called.
7608
+ If the folder provided doesn't exist, it will get created.
7609
+ @Params
7610
+ name - The name to give the PDF file. Must end in ".pdf".
7611
+ folder - The directory where you want to save the PDF."""
7612
+ import base64
7613
+ from selenium.webdriver.common.print_page_options import PrintOptions
7614
+
7615
+ if not name.lower().endswith(".pdf"):
7616
+ raise Exception('PDF name {%s} must end in ".pdf"!)' % name)
7617
+ download_file_lock = fasteners.InterProcessLock(
7618
+ constants.MultiBrowser.DOWNLOAD_FILE_LOCK
7619
+ )
7620
+ if self.__is_cdp_swap_needed():
7621
+ with download_file_lock:
7622
+ with suppress(Exception):
7623
+ shared_utils.make_writable(
7624
+ constants.MultiBrowser.DOWNLOAD_FILE_LOCK
7625
+ )
7626
+ if folder and not os.path.exists(folder):
7627
+ os.makedirs(folder)
7628
+ self.cdp.print_to_pdf(name, folder)
7629
+ return
7630
+ self.wait_for_ready_state_complete()
7631
+ print_options = PrintOptions()
7632
+ pdf_base64 = self.driver.print_page(print_options)
7633
+ with download_file_lock:
7634
+ with suppress(Exception):
7635
+ shared_utils.make_writable(
7636
+ constants.MultiBrowser.DOWNLOAD_FILE_LOCK
7637
+ )
7638
+ if folder and not os.path.exists(folder):
7639
+ os.makedirs(folder)
7640
+ filename = name
7641
+ if folder:
7642
+ filename = os.path.join(folder, name)
7643
+ with open(filename, "wb") as f:
7644
+ f.write(base64.b64decode(pdf_base64))
7645
+
7555
7646
  def get_downloads_folder(self):
7556
7647
  """Returns the path of the SeleniumBase "downloaded_files/" folder.
7557
7648
  Calling self.download_file(file_url) will put that file in here.