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,181 @@
1
+ import atexit
2
+ import os
3
+ import shlex
4
+ import subprocess
5
+ import tempfile
6
+
7
+ from cucu import config, logger, step
8
+
9
+
10
+ def run_command(
11
+ command,
12
+ stdout_var=None,
13
+ stderr_var=None,
14
+ exit_code_var=None,
15
+ check_exit_code=None,
16
+ ):
17
+ args = shlex.split(command)
18
+ process = subprocess.run(args, capture_output=True)
19
+
20
+ if exit_code_var:
21
+ config.CONFIG[exit_code_var] = str(process.returncode)
22
+
23
+ stdout = process.stdout.decode("utf8")
24
+ stderr = process.stderr.decode("utf8")
25
+
26
+ if stdout_var:
27
+ config.CONFIG[stdout_var] = config.CONFIG.escape(stdout)
28
+
29
+ if stderr_var:
30
+ config.CONFIG[stderr_var] = config.CONFIG.escape(stderr)
31
+
32
+ return_code = process.returncode
33
+ if check_exit_code is not None and int(check_exit_code) != return_code:
34
+ logger.error(f"STDOUT:\n{stdout}\n")
35
+ logger.error(f"STDERR:\n{stderr}\n")
36
+ raise RuntimeError(
37
+ f"expected exit code {check_exit_code}, got {return_code}, see above for details"
38
+ )
39
+
40
+ else:
41
+ logger.debug(f"STDOUT:\n{stdout}\n")
42
+ logger.debug(f"STDERR:\n{stderr}\n")
43
+
44
+
45
+ def run_script(
46
+ script,
47
+ stdout_var=None,
48
+ stderr_var=None,
49
+ exit_code_var=None,
50
+ check_exit_code=None,
51
+ ):
52
+ script_fd, script_filename = tempfile.mkstemp()
53
+ os.close(script_fd)
54
+ atexit.register(os.remove, script_filename)
55
+
56
+ with open(script_filename, "wb") as script_file:
57
+ script_file.write(script.encode())
58
+
59
+ os.chmod(script_filename, 0o755)
60
+ process = subprocess.run(
61
+ [script_file.name],
62
+ capture_output=True,
63
+ shell=True,
64
+ ) # nosec
65
+
66
+ if exit_code_var:
67
+ config.CONFIG[exit_code_var] = str(process.returncode)
68
+
69
+ stdout = process.stdout.decode("utf8")
70
+ stderr = process.stderr.decode("utf8")
71
+
72
+ if stdout_var:
73
+ config.CONFIG[stdout_var] = config.CONFIG.escape(stdout)
74
+
75
+ if stderr_var:
76
+ config.CONFIG[stderr_var] = config.CONFIG.escape(stderr)
77
+
78
+ return_code = process.returncode
79
+ if check_exit_code is not None and int(check_exit_code) != return_code:
80
+ logger.error(f"STDOUT:\n{stdout}\n")
81
+ logger.error(f"STDERR:\n{stderr}\n")
82
+ raise RuntimeError(
83
+ f"expected exit code {check_exit_code}, got {return_code}, see above for details"
84
+ )
85
+ else:
86
+ logger.debug(f"STDOUT:\n{stdout}\n")
87
+ logger.debug(f"STDERR:\n{stderr}\n")
88
+
89
+
90
+ @step(
91
+ 'I run the command "{command}" and save stdout to "{stdout_var}", stderr to "{stderr_var}", exit code to "{exit_code_var}"'
92
+ )
93
+ def runs_command_and_save_everything(
94
+ context, command, stdout_var, stderr_var, exit_code_var
95
+ ):
96
+ run_command(
97
+ command,
98
+ stdout_var=stdout_var,
99
+ stderr_var=stderr_var,
100
+ exit_code_var=exit_code_var,
101
+ )
102
+
103
+
104
+ @step(
105
+ 'I run the command "{command}" and save stdout to "{stdout_var}", stderr to "{stderr_var}" and expect exit code "{exit_code}"'
106
+ )
107
+ def runs_command_and_check_exit_code(
108
+ context, command, stdout_var, stderr_var, exit_code
109
+ ):
110
+ run_command(
111
+ command,
112
+ stdout_var=stdout_var,
113
+ stderr_var=stderr_var,
114
+ check_exit_code=exit_code,
115
+ )
116
+
117
+
118
+ @step(
119
+ 'I run the command "{command}" and save stdout to "{stdout_var}" and expect exit code "{exit_code}"'
120
+ )
121
+ def runs_command__and_save_stdout_and_check_exit_code(
122
+ context, command, stdout_var, exit_code
123
+ ):
124
+ run_command(
125
+ command,
126
+ stdout_var=stdout_var,
127
+ check_exit_code=exit_code,
128
+ )
129
+
130
+
131
+ @step(
132
+ 'I run the command "{command}" and save stdout to "{stdout_var}", exit code to "{exit_code_var}"'
133
+ )
134
+ def run_command_and_save_stdout_and_exit_code(
135
+ context, command, stdout_var, exit_code_var
136
+ ):
137
+ run_command(command, stdout_var=stdout_var, exit_code_var=exit_code_var)
138
+
139
+
140
+ @step('I run the command "{command}" and save exit code to "{exit_code_var}"')
141
+ def run_command_and_save_exit_code(context, command, exit_code_var):
142
+ run_command(command, exit_code_var=exit_code_var)
143
+
144
+
145
+ @step('I run the command "{command}" and expect exit code "{exit_code}"')
146
+ def run_command_and_expect_exit_code(context, command, exit_code):
147
+ run_command(command, check_exit_code=exit_code)
148
+
149
+
150
+ @step(
151
+ 'I run the following script and save stdout to "{stdout_var}", stderr to "{stderr_var}", exit code to "{exit_code_var}"'
152
+ )
153
+ def run_script_and_save_everything(
154
+ context, stdout_var, stderr_var, exit_code_var
155
+ ):
156
+ run_script(
157
+ context.text,
158
+ stdout_var=stdout_var,
159
+ stderr_var=stderr_var,
160
+ exit_code_var=exit_code_var,
161
+ )
162
+
163
+
164
+ @step(
165
+ 'I run the following script and save stdout to "{stdout_var}", stderr to "{stderr_var}" and expect exit code "{exit_code}"'
166
+ )
167
+ def run_script_and_check_exit_code(context, stdout_var, stderr_var, exit_code):
168
+ run_script(
169
+ context.text,
170
+ stdout_var=stdout_var,
171
+ stderr_var=stderr_var,
172
+ check_exit_code=exit_code,
173
+ )
174
+
175
+
176
+ @step('I run the following script and expect exit code "{exit_code}"')
177
+ def run_script_and_expect_exit_code(context, exit_code):
178
+ run_script(
179
+ context.text,
180
+ check_exit_code=exit_code,
181
+ )
@@ -0,0 +1,17 @@
1
+ from behave import use_step_matcher
2
+
3
+ from cucu import step
4
+
5
+ use_step_matcher("re") # use regex to match comments
6
+
7
+
8
+ @step("#.*")
9
+ def comment_step(ctx):
10
+ """
11
+ A no-op step so that we can see "comments" in results and report.
12
+ Usage: add `* #` to the line you want to show up.
13
+ """
14
+ pass
15
+
16
+
17
+ use_step_matcher("parse") # set this back to cucu's default matcher parser
@@ -0,0 +1,168 @@
1
+ from selenium.webdriver.support.ui import WebDriverWait
2
+
3
+ from cucu import fuzzy, helpers, logger
4
+ from cucu.utils import take_saw_element_screenshot
5
+
6
+
7
+ def find_draggable_element(ctx, name, index=0):
8
+ """
9
+ find a draggable element on screen by fuzzy matching on the name provided
10
+ and the target element:
11
+
12
+ * <* draggable="true">
13
+
14
+ parameters:
15
+ ctx(object): behave context object used to share data between steps
16
+ name(str): name that identifies the desired draggable element on screen
17
+ index(str): the index of the draggable element if there are duplicates
18
+ returns:
19
+ the WebElement that matches the provided arguments.
20
+ """
21
+ ctx.check_browser_initialized()
22
+ element = fuzzy.find(
23
+ ctx.browser, name, ['*[draggable="true"]'], index=index
24
+ )
25
+
26
+ take_saw_element_screenshot(ctx, "draggable", name, index, element)
27
+
28
+ return element
29
+
30
+
31
+ def is_draggable(element):
32
+ """
33
+ Checks if an element is draggable.
34
+ Args:
35
+ element (WebElement): The element to check.
36
+ Returns:
37
+ bool: True if the element is draggable, False otherwise.
38
+ """
39
+ return element.get_attribute("draggable") == "true"
40
+
41
+
42
+ def is_not_draggable(element):
43
+ """
44
+ Checks if an element is not draggable.
45
+ Args:
46
+ element (WebElement): The element to check.
47
+ Returns:
48
+ bool: True if the element is not draggable, False otherwise.
49
+ """
50
+ return not is_draggable(element)
51
+
52
+
53
+ def find_target_element(ctx, name, index=0):
54
+ ctx.check_browser_initialized()
55
+ _element = fuzzy.find(ctx.browser, name, ["*"], index=index)
56
+
57
+ return _element
58
+
59
+
60
+ JS_DRAG_AND_DROP = """
61
+ const cucuDragAndDrop = async (dragElem, dropElem) => {
62
+
63
+ function triggerEvent(elem, eventName, dataTransfer = null) {
64
+ return new Promise((resolve) => {
65
+ const eventObj = new DragEvent(eventName, {
66
+ bubbles: true,
67
+ cancelable: true,
68
+ composed: true,
69
+ dataTransfer: dataTransfer,
70
+ });
71
+
72
+ function listener() {
73
+ resolve(eventObj);
74
+ elem.removeEventListener(eventName, listener);
75
+ }
76
+
77
+ elem.addEventListener(eventName, listener);
78
+ elem.dispatchEvent(eventObj);
79
+ });
80
+ }
81
+
82
+ const dragstartObj = await triggerEvent(dragElem, 'dragstart', new DataTransfer());
83
+ await triggerEvent(dropElem, 'dragenter', dragstartObj.dataTransfer);
84
+ const dropeventObj = await triggerEvent(dropElem, 'drop', dragstartObj.dataTransfer);
85
+ await triggerEvent(dragElem, 'dragend', dropeventObj.dataTransfer);
86
+
87
+ window.dragAndDropCompleted = true;
88
+ };
89
+
90
+ cucuDragAndDrop(arguments[0], arguments[1]);
91
+ """
92
+
93
+
94
+ def drag_element_to_element(ctx, drag_name, drop_name):
95
+ driver = ctx.browser.driver
96
+
97
+ driver.execute_script("window.dragAndDropCompleted = false;")
98
+
99
+ start_drag_rect = drag_name.rect
100
+ logger.debug(
101
+ f"Start location of drag element {drag_name.text}: {start_drag_rect}"
102
+ )
103
+ logger.debug("Executing drag-and-drop via JavaScript.")
104
+
105
+ driver.execute_script(JS_DRAG_AND_DROP, drag_name, drop_name)
106
+
107
+ # Wait for the JavaScript flag to be set to True
108
+ WebDriverWait(driver, 10).until(
109
+ lambda driver: driver.execute_script(
110
+ "return window.dragAndDropCompleted;"
111
+ )
112
+ )
113
+
114
+ end_drag_rect = drag_name.rect
115
+ logger.debug(
116
+ f"End location of drag element {drag_name.text}: {end_drag_rect}"
117
+ )
118
+
119
+ if start_drag_rect == end_drag_rect:
120
+ raise RuntimeError(
121
+ f"Drag element {drag_name.text} position did not change"
122
+ )
123
+ else:
124
+ logger.debug("Drag element position changed successfully.")
125
+
126
+ logger.debug("Drag-and-drop operation executed.")
127
+
128
+
129
+ helpers.define_thing_with_name_in_state_steps(
130
+ "element", "draggable", find_target_element, is_draggable
131
+ )
132
+
133
+ helpers.define_thing_with_name_in_state_steps(
134
+ "element", "not draggable", find_target_element, is_not_draggable
135
+ )
136
+
137
+ helpers.define_thing_with_name_in_state_steps(
138
+ "element", "draggable", find_target_element, is_draggable, with_nth=True
139
+ )
140
+
141
+ helpers.define_thing_with_name_in_state_steps(
142
+ "element",
143
+ "not draggable",
144
+ find_target_element,
145
+ is_not_draggable,
146
+ with_nth=True,
147
+ )
148
+
149
+ helpers.define_two_thing_interaction_steps(
150
+ "drag",
151
+ drag_element_to_element,
152
+ "element",
153
+ find_draggable_element,
154
+ "to",
155
+ "element",
156
+ find_target_element,
157
+ )
158
+
159
+ helpers.define_two_thing_interaction_steps(
160
+ "drag",
161
+ drag_element_to_element,
162
+ "element",
163
+ find_draggable_element,
164
+ "to",
165
+ "element",
166
+ find_target_element,
167
+ with_nth=True,
168
+ )