cucu 1.0.0__py3-none-any.whl

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 cucu might be problematic. Click here for more details.

Files changed (83) hide show
  1. cucu/__init__.py +38 -0
  2. cucu/ansi_parser.py +58 -0
  3. cucu/behave_tweaks.py +196 -0
  4. cucu/browser/__init__.py +0 -0
  5. cucu/browser/core.py +80 -0
  6. cucu/browser/frames.py +106 -0
  7. cucu/browser/selenium.py +323 -0
  8. cucu/browser/selenium_tweaks.py +27 -0
  9. cucu/cli/__init__.py +3 -0
  10. cucu/cli/core.py +788 -0
  11. cucu/cli/run.py +207 -0
  12. cucu/cli/steps.py +137 -0
  13. cucu/cli/thread_dumper.py +55 -0
  14. cucu/config.py +440 -0
  15. cucu/edgedriver_autoinstaller/README.md +1 -0
  16. cucu/edgedriver_autoinstaller/__init__.py +37 -0
  17. cucu/edgedriver_autoinstaller/utils.py +231 -0
  18. cucu/environment.py +283 -0
  19. cucu/external/jquery/jquery-3.5.1.min.js +2 -0
  20. cucu/formatter/__init__.py +0 -0
  21. cucu/formatter/cucu.py +261 -0
  22. cucu/formatter/json.py +321 -0
  23. cucu/formatter/junit.py +289 -0
  24. cucu/fuzzy/__init__.py +3 -0
  25. cucu/fuzzy/core.py +107 -0
  26. cucu/fuzzy/fuzzy.js +253 -0
  27. cucu/helpers.py +875 -0
  28. cucu/hooks.py +205 -0
  29. cucu/language_server/__init__.py +3 -0
  30. cucu/language_server/core.py +114 -0
  31. cucu/lint/__init__.py +0 -0
  32. cucu/lint/linter.py +397 -0
  33. cucu/lint/rules/format.yaml +125 -0
  34. cucu/logger.py +113 -0
  35. cucu/matcher/__init__.py +0 -0
  36. cucu/matcher/core.py +30 -0
  37. cucu/page_checks.py +63 -0
  38. cucu/reporter/__init__.py +3 -0
  39. cucu/reporter/external/bootstrap.min.css +7 -0
  40. cucu/reporter/external/bootstrap.min.js +7 -0
  41. cucu/reporter/external/dataTables.bootstrap.min.css +1 -0
  42. cucu/reporter/external/dataTables.bootstrap.min.js +14 -0
  43. cucu/reporter/external/jquery-3.5.1.min.js +2 -0
  44. cucu/reporter/external/jquery.dataTables.min.js +192 -0
  45. cucu/reporter/external/popper.min.js +5 -0
  46. cucu/reporter/favicon.png +0 -0
  47. cucu/reporter/html.py +452 -0
  48. cucu/reporter/templates/feature.html +72 -0
  49. cucu/reporter/templates/flat.html +48 -0
  50. cucu/reporter/templates/index.html +49 -0
  51. cucu/reporter/templates/layout.html +109 -0
  52. cucu/reporter/templates/scenario.html +200 -0
  53. cucu/steps/__init__.py +27 -0
  54. cucu/steps/base_steps.py +88 -0
  55. cucu/steps/browser_steps.py +337 -0
  56. cucu/steps/button_steps.py +91 -0
  57. cucu/steps/checkbox_steps.py +111 -0
  58. cucu/steps/command_steps.py +181 -0
  59. cucu/steps/comment_steps.py +17 -0
  60. cucu/steps/draggable_steps.py +168 -0
  61. cucu/steps/dropdown_steps.py +467 -0
  62. cucu/steps/file_input_steps.py +80 -0
  63. cucu/steps/filesystem_steps.py +144 -0
  64. cucu/steps/flow_control_steps.py +198 -0
  65. cucu/steps/image_steps.py +37 -0
  66. cucu/steps/input_steps.py +301 -0
  67. cucu/steps/link_steps.py +63 -0
  68. cucu/steps/menuitem_steps.py +39 -0
  69. cucu/steps/platform_steps.py +29 -0
  70. cucu/steps/radio_steps.py +187 -0
  71. cucu/steps/step_utils.py +55 -0
  72. cucu/steps/tab_steps.py +68 -0
  73. cucu/steps/table_steps.py +437 -0
  74. cucu/steps/tables.js +28 -0
  75. cucu/steps/text_steps.py +78 -0
  76. cucu/steps/variable_steps.py +100 -0
  77. cucu/steps/webserver_steps.py +40 -0
  78. cucu/utils.py +269 -0
  79. cucu-1.0.0.dist-info/METADATA +424 -0
  80. cucu-1.0.0.dist-info/RECORD +83 -0
  81. cucu-1.0.0.dist-info/WHEEL +4 -0
  82. cucu-1.0.0.dist-info/entry_points.txt +2 -0
  83. cucu-1.0.0.dist-info/licenses/LICENSE +32 -0
@@ -0,0 +1,323 @@
1
+ import logging
2
+
3
+ import chromedriver_autoinstaller
4
+ import geckodriver_autoinstaller
5
+ import urllib3
6
+ from selenium import webdriver
7
+ from selenium.webdriver.chrome.options import Options as ChromeOptions
8
+ from selenium.webdriver.common.by import By
9
+ from selenium.webdriver.edge.options import Options as EdgeOptions
10
+ from selenium.webdriver.firefox.options import Options as FirefoxOptions
11
+ from selenium.webdriver.remote.remote_connection import RemoteConnection
12
+
13
+ from cucu import config, edgedriver_autoinstaller, logger
14
+ from cucu.browser.core import Browser
15
+ from cucu.browser.frames import search_in_all_frames
16
+
17
+
18
+ class DisableLogger:
19
+ def __enter__(self):
20
+ logging.disable(logging.CRITICAL)
21
+
22
+ def __exit__(self, exit_type, exit_value, exit_traceback):
23
+ logging.disable(logging.NOTSET)
24
+
25
+
26
+ def init():
27
+ """
28
+ initialize any selenium specific needs
29
+ """
30
+ if config.CONFIG["CUCU_BROWSER"] == "chrome":
31
+ try:
32
+ with DisableLogger():
33
+ # auto install chromedriver if not present
34
+ chromedriver_autoinstaller.install()
35
+ except: # noqa: E722
36
+ logging.debug("unable to auto install chromedriver")
37
+
38
+ if config.CONFIG["CUCU_BROWSER"] == "firefox":
39
+ # https://github.com/mozilla/geckodriver/issues/330
40
+ logger.warn("browser console logs not available on firefox")
41
+ geckodriver_autoinstaller.install()
42
+
43
+ if config.CONFIG["CUCU_BROWSER"] == "edge":
44
+ edgedriver_autoinstaller.install()
45
+
46
+
47
+ class Selenium(Browser):
48
+ def __init__(self):
49
+ self.driver = None
50
+
51
+ def open(
52
+ self, browser, headless=False, selenium_remote_url=None, detach=False
53
+ ):
54
+ if selenium_remote_url is None:
55
+ init()
56
+
57
+ timeout = float(config.CONFIG["CUCU_SELENIUM_DEFAULT_TIMEOUT_S"])
58
+ RemoteConnection.set_timeout(timeout)
59
+
60
+ height = config.CONFIG["CUCU_BROWSER_WINDOW_HEIGHT"]
61
+ width = config.CONFIG["CUCU_BROWSER_WINDOW_WIDTH"]
62
+ cucu_downloads_dir = config.CONFIG["CUCU_BROWSER_DOWNLOADS_DIR"]
63
+ ignore_ssl_certificate_errors = config.CONFIG[
64
+ "CUCU_IGNORE_SSL_CERTIFICATE_ERRORS"
65
+ ]
66
+
67
+ if browser.startswith("chrome"):
68
+ options = ChromeOptions()
69
+ options.add_experimental_option("detach", detach)
70
+
71
+ prefs = {"download.default_directory": cucu_downloads_dir}
72
+ options.add_experimental_option("prefs", prefs)
73
+
74
+ options.add_argument(f"--window-size={width},{height}")
75
+ options.add_argument("--disable-dev-shm-usage")
76
+
77
+ if headless:
78
+ options.add_argument("--headless")
79
+
80
+ if ignore_ssl_certificate_errors:
81
+ options.add_argument("ignore-certificate-errors")
82
+ options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
83
+
84
+ if selenium_remote_url is not None:
85
+ logger.debug(f"webdriver.Remote init: {selenium_remote_url}")
86
+ try:
87
+ self.driver = webdriver.Remote(
88
+ command_executor=selenium_remote_url,
89
+ options=options,
90
+ )
91
+ except urllib3.exceptions.ReadTimeoutError:
92
+ print("*" * 80)
93
+ print(
94
+ "* unable to connect to the remote selenium setup,"
95
+ " you may need to restart it"
96
+ )
97
+ print("*" * 80)
98
+ print("")
99
+ raise
100
+ else:
101
+ logger.debug("webdriver.Chrome init")
102
+ self.driver = webdriver.Chrome(
103
+ options=options,
104
+ )
105
+
106
+ elif browser.startswith("firefox"):
107
+ options = FirefoxOptions()
108
+
109
+ options.set_preference("browser.download.folderList", 2)
110
+ options.set_preference(
111
+ "browser.download.manager.showWhenStarting", False
112
+ )
113
+ options.set_preference("browser.download.dir", cucu_downloads_dir)
114
+ options.set_preference(
115
+ "browser.helperApps.neverAsk.saveToDisk",
116
+ "images/jpeg, application/pdf, application/octet-stream, text/plain",
117
+ )
118
+
119
+ if ignore_ssl_certificate_errors:
120
+ options.accept_insecure_certs = True
121
+
122
+ options.add_argument(f"--width={width}")
123
+ options.add_argument(f"--height={height}")
124
+
125
+ # TODO: re-enable once Firefox supports this
126
+ # options.set_capability("loggingPrefs", {"browser": "ALL"})
127
+
128
+ if headless:
129
+ options.add_argument("--headless")
130
+
131
+ if selenium_remote_url is not None:
132
+ logger.debug(f"webdriver.Remote init: {selenium_remote_url}")
133
+ try:
134
+ self.driver = webdriver.Remote(
135
+ command_executor=selenium_remote_url,
136
+ options=options,
137
+ )
138
+ except urllib3.exceptions.ReadTimeoutError:
139
+ print("*" * 80)
140
+ print(
141
+ "* unable to connect to the remote selenium setup,"
142
+ " you may need to restart it"
143
+ )
144
+ print("*" * 80)
145
+ print("")
146
+ raise
147
+ else:
148
+ logger.debug("webdriver.Firefox init")
149
+ self.driver = webdriver.Firefox(
150
+ options=options,
151
+ )
152
+
153
+ elif browser.startswith("edge"):
154
+ options = EdgeOptions()
155
+
156
+ options.add_experimental_option(
157
+ "prefs", {"download.default_directory": cucu_downloads_dir}
158
+ )
159
+
160
+ if headless:
161
+ options.use_chromium = True
162
+ options.add_argument("--headless")
163
+
164
+ if ignore_ssl_certificate_errors:
165
+ options.set_capability("acceptSslCerts", True)
166
+
167
+ if selenium_remote_url is not None:
168
+ logger.debug(f"webdriver.Remote init: {selenium_remote_url}")
169
+ try:
170
+ self.driver = webdriver.Remote(
171
+ command_executor=selenium_remote_url,
172
+ options=options,
173
+ )
174
+ except urllib3.exceptions.ReadTimeoutError:
175
+ print("*" * 80)
176
+ print(
177
+ "* unable to connect to the remote selenium setup,"
178
+ " you may need to restart it"
179
+ )
180
+ print("*" * 80)
181
+ print("")
182
+ raise
183
+ else:
184
+ logger.debug("webdriver.Edge init")
185
+ edgedriver_filepath = (
186
+ edgedriver_autoinstaller.utils.download_edgedriver()
187
+ )
188
+ self.driver = webdriver.Edge(
189
+ executable_path=edgedriver_filepath,
190
+ options=options,
191
+ )
192
+
193
+ else:
194
+ raise Exception(f"unknown browser {browser}")
195
+
196
+ self.driver.set_window_size(width, height)
197
+
198
+ def get_log(self):
199
+ if config.CONFIG["CUCU_BROWSER"] == "firefox":
200
+ return []
201
+
202
+ return self.driver.get_log("browser")
203
+
204
+ def get_current_url(self):
205
+ return self.driver.current_url
206
+
207
+ def navigate(self, url):
208
+ self.driver.get(url)
209
+ self.wait_for_page_to_load()
210
+
211
+ def switch_to_next_tab(self):
212
+ window_handles = self.driver.window_handles
213
+ window_handle = self.driver.current_window_handle
214
+ window_handle_index = window_handles.index(window_handle)
215
+
216
+ if window_handle_index == len(window_handles) - 1:
217
+ raise RuntimeError("no next browser tab available")
218
+
219
+ self.driver.switch_to.window(window_handles[window_handle_index + 1])
220
+
221
+ def switch_to_previous_tab(self):
222
+ window_handles = self.driver.window_handles
223
+ window_handle = self.driver.current_window_handle
224
+ window_handle_index = window_handles.index(window_handle)
225
+
226
+ if window_handle_index == 0:
227
+ raise RuntimeError("no previous browser tab available")
228
+
229
+ self.driver.switch_to.window(window_handles[window_handle_index - 1])
230
+
231
+ def back(self):
232
+ self.driver.back()
233
+ self.wait_for_page_to_load()
234
+
235
+ def refresh(self):
236
+ self.driver.refresh()
237
+ self.wait_for_page_to_load()
238
+
239
+ def title(self):
240
+ return self.driver.title
241
+
242
+ def css_find_elements(self, selector):
243
+ def find_elements_in_frame():
244
+ elements = self.driver.find_elements(By.CSS_SELECTOR, selector)
245
+
246
+ def visible(element):
247
+ return element.is_displayed()
248
+
249
+ return list(filter(visible, elements))
250
+
251
+ return search_in_all_frames(self, find_elements_in_frame)
252
+
253
+ def execute(self, javascript, *args):
254
+ return self.driver.execute_script(javascript, *args)
255
+
256
+ def click(self, element):
257
+ element.click()
258
+ # let cucu's own wait for page to load checks run
259
+ self.wait_for_page_to_load()
260
+
261
+ def switch_to_default_frame(self):
262
+ self.driver.switch_to.default_content()
263
+
264
+ def switch_to_frame(self, frame):
265
+ self.driver.switch_to.frame(frame)
266
+
267
+ def screenshot(self, filepath):
268
+ self.driver.get_screenshot_as_file(filepath)
269
+
270
+ def close_window(self):
271
+ window_handles = self.driver.window_handles
272
+ window_handle = self.driver.current_window_handle
273
+ window_handle_index = window_handles.index(window_handle)
274
+
275
+ self.driver.close()
276
+ self.driver.switch_to.window(window_handles[window_handle_index - 1])
277
+
278
+ def download_mht(self, target_filepath):
279
+ if self.driver is None:
280
+ logger.warn(
281
+ "No active browser; will not attempt to download .mht file."
282
+ )
283
+ return
284
+
285
+ browser_name = self.driver.name.lower()
286
+ if "chrome" not in browser_name:
287
+ logger.warn(
288
+ "The web driver is not using Chrome as a web browser"
289
+ f", but {browser_name}. This browser does not support"
290
+ "dowloading .mht files; will not attempt to download one."
291
+ )
292
+ return
293
+
294
+ mht_data = None
295
+ if self.driver._is_remote:
296
+ cdp_url = f"{self.driver.command_executor._url}/session/{self.driver.session_id}/chromium/send_command_and_get_result"
297
+ cdp_request_body = '{"cmd": "Page.captureSnapshot", "params": {}}'
298
+ cdp_response = self.driver.command_executor._request(
299
+ "POST", cdp_url, cdp_request_body
300
+ )
301
+ # Flaky: Adding try catch block to handle the situation where we do not get a dictionary object.
302
+ # Could not reproduce this in local. So far we have seen this issue only when running on a remote web driver.
303
+ try:
304
+ mht_data = cdp_response.get("value").get("data")
305
+ except Exception as e:
306
+ logger.error(f'object "{cdp_response.get("value")}" : {e}')
307
+ else:
308
+ mht_response = self.driver.execute_cdp_cmd(
309
+ "Page.captureSnapshot", {}
310
+ )
311
+ mht_data = mht_response.get("data")
312
+
313
+ if mht_data is None:
314
+ logger.warn(
315
+ "Something unexpected has happened: fetched MHT data, but that data was empty. Not writing MHT file."
316
+ )
317
+ return
318
+
319
+ with open(target_filepath, "w") as mht_file:
320
+ mht_file.write(mht_data)
321
+
322
+ def quit(self):
323
+ self.driver.quit()
@@ -0,0 +1,27 @@
1
+ from selenium.webdriver.remote import webelement
2
+
3
+ from cucu.browser.frames import search_in_all_frames
4
+ from cucu.config import CONFIG
5
+
6
+ # monkey patch some methods at the WebElement level
7
+ __ORIGINAL_FIND_ELEMENTS = webelement.WebElement.find_elements
8
+
9
+
10
+ def init():
11
+ """
12
+ initialize various selenium tweaks to change the behavior of the underlying
13
+ selenium classes.
14
+ """
15
+
16
+ # intercept the find_elements calls and use the nifty frames method to allow
17
+ # the expression to be executed against every visible frame to find the
18
+ # desired element.
19
+ def find_elements(self, by="id", value=None):
20
+ ctx = CONFIG["__CUCU_CTX"]
21
+
22
+ def find_elements_in_frame():
23
+ return __ORIGINAL_FIND_ELEMENTS(self, by, value)
24
+
25
+ return search_in_all_frames(ctx.browser, find_elements_in_frame)
26
+
27
+ webelement.WebElement.find_elements = find_elements
cucu/cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ # flake8: noqa
2
+ # nopycln: file
3
+ from cucu.cli.core import main