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.
- cucu/__init__.py +38 -0
- cucu/ansi_parser.py +58 -0
- cucu/behave_tweaks.py +196 -0
- cucu/browser/__init__.py +0 -0
- cucu/browser/core.py +80 -0
- cucu/browser/frames.py +106 -0
- cucu/browser/selenium.py +323 -0
- cucu/browser/selenium_tweaks.py +27 -0
- cucu/cli/__init__.py +3 -0
- cucu/cli/core.py +788 -0
- cucu/cli/run.py +207 -0
- cucu/cli/steps.py +137 -0
- cucu/cli/thread_dumper.py +55 -0
- cucu/config.py +440 -0
- cucu/edgedriver_autoinstaller/README.md +1 -0
- cucu/edgedriver_autoinstaller/__init__.py +37 -0
- cucu/edgedriver_autoinstaller/utils.py +231 -0
- cucu/environment.py +283 -0
- cucu/external/jquery/jquery-3.5.1.min.js +2 -0
- cucu/formatter/__init__.py +0 -0
- cucu/formatter/cucu.py +261 -0
- cucu/formatter/json.py +321 -0
- cucu/formatter/junit.py +289 -0
- cucu/fuzzy/__init__.py +3 -0
- cucu/fuzzy/core.py +107 -0
- cucu/fuzzy/fuzzy.js +253 -0
- cucu/helpers.py +875 -0
- cucu/hooks.py +205 -0
- cucu/language_server/__init__.py +3 -0
- cucu/language_server/core.py +114 -0
- cucu/lint/__init__.py +0 -0
- cucu/lint/linter.py +397 -0
- cucu/lint/rules/format.yaml +125 -0
- cucu/logger.py +113 -0
- cucu/matcher/__init__.py +0 -0
- cucu/matcher/core.py +30 -0
- cucu/page_checks.py +63 -0
- cucu/reporter/__init__.py +3 -0
- cucu/reporter/external/bootstrap.min.css +7 -0
- cucu/reporter/external/bootstrap.min.js +7 -0
- cucu/reporter/external/dataTables.bootstrap.min.css +1 -0
- cucu/reporter/external/dataTables.bootstrap.min.js +14 -0
- cucu/reporter/external/jquery-3.5.1.min.js +2 -0
- cucu/reporter/external/jquery.dataTables.min.js +192 -0
- cucu/reporter/external/popper.min.js +5 -0
- cucu/reporter/favicon.png +0 -0
- cucu/reporter/html.py +452 -0
- cucu/reporter/templates/feature.html +72 -0
- cucu/reporter/templates/flat.html +48 -0
- cucu/reporter/templates/index.html +49 -0
- cucu/reporter/templates/layout.html +109 -0
- cucu/reporter/templates/scenario.html +200 -0
- cucu/steps/__init__.py +27 -0
- cucu/steps/base_steps.py +88 -0
- cucu/steps/browser_steps.py +337 -0
- cucu/steps/button_steps.py +91 -0
- cucu/steps/checkbox_steps.py +111 -0
- cucu/steps/command_steps.py +181 -0
- cucu/steps/comment_steps.py +17 -0
- cucu/steps/draggable_steps.py +168 -0
- cucu/steps/dropdown_steps.py +467 -0
- cucu/steps/file_input_steps.py +80 -0
- cucu/steps/filesystem_steps.py +144 -0
- cucu/steps/flow_control_steps.py +198 -0
- cucu/steps/image_steps.py +37 -0
- cucu/steps/input_steps.py +301 -0
- cucu/steps/link_steps.py +63 -0
- cucu/steps/menuitem_steps.py +39 -0
- cucu/steps/platform_steps.py +29 -0
- cucu/steps/radio_steps.py +187 -0
- cucu/steps/step_utils.py +55 -0
- cucu/steps/tab_steps.py +68 -0
- cucu/steps/table_steps.py +437 -0
- cucu/steps/tables.js +28 -0
- cucu/steps/text_steps.py +78 -0
- cucu/steps/variable_steps.py +100 -0
- cucu/steps/webserver_steps.py +40 -0
- cucu/utils.py +269 -0
- cucu-1.0.0.dist-info/METADATA +424 -0
- cucu-1.0.0.dist-info/RECORD +83 -0
- cucu-1.0.0.dist-info/WHEEL +4 -0
- cucu-1.0.0.dist-info/entry_points.txt +2 -0
- cucu-1.0.0.dist-info/licenses/LICENSE +32 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from cucu import fuzzy, helpers, retry, step
|
|
2
|
+
from cucu.config import CONFIG
|
|
3
|
+
from cucu.utils import take_saw_element_screenshot
|
|
4
|
+
|
|
5
|
+
from . import base_steps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def find_radio_button(ctx, name, index=0):
|
|
9
|
+
"""
|
|
10
|
+
find a radio button on screen by fuzzy matching on the name provided and
|
|
11
|
+
the target element:
|
|
12
|
+
|
|
13
|
+
* <input type="radio">
|
|
14
|
+
* <* role="radio">
|
|
15
|
+
|
|
16
|
+
parameters:
|
|
17
|
+
ctx(object): behave context object used to share data between steps
|
|
18
|
+
name(str): name that identifies the desired radio button on screen
|
|
19
|
+
index(str): the index of the radio button if there are duplicates
|
|
20
|
+
|
|
21
|
+
returns:
|
|
22
|
+
the WebElement that matches the provided arguments.
|
|
23
|
+
"""
|
|
24
|
+
ctx.check_browser_initialized()
|
|
25
|
+
element = fuzzy.find(
|
|
26
|
+
ctx.browser,
|
|
27
|
+
name,
|
|
28
|
+
[
|
|
29
|
+
'input[type="radio"]',
|
|
30
|
+
'*[role="radio"]',
|
|
31
|
+
],
|
|
32
|
+
index=index,
|
|
33
|
+
direction=fuzzy.Direction.RIGHT_TO_LEFT,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
take_saw_element_screenshot(ctx, "radio button", name, index, element)
|
|
37
|
+
|
|
38
|
+
return element
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def find_n_assert_radio_button(ctx, name, index=0, is_visible=True):
|
|
42
|
+
"""
|
|
43
|
+
find and assert a radio button is visible
|
|
44
|
+
|
|
45
|
+
parameters:
|
|
46
|
+
ctx(object): behave context object used to share data between steps
|
|
47
|
+
name(str): name that identifies the desired radio button on screen
|
|
48
|
+
index(str): the index of the radio button if there are duplicates
|
|
49
|
+
|
|
50
|
+
returns:
|
|
51
|
+
the WebElement that matches the provided arguments.
|
|
52
|
+
"""
|
|
53
|
+
radio = find_radio_button(ctx, name, index=index)
|
|
54
|
+
|
|
55
|
+
if is_visible:
|
|
56
|
+
if radio is None:
|
|
57
|
+
raise Exception(f'unable to find radio button "{name}"')
|
|
58
|
+
else:
|
|
59
|
+
if radio is not None:
|
|
60
|
+
raise Exception(f'able to find radio button "{name}"')
|
|
61
|
+
|
|
62
|
+
return radio
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def find_n_select_radio_button(ctx, name, index=0, ignore_if_selected=False):
|
|
66
|
+
"""
|
|
67
|
+
find and select a radio button
|
|
68
|
+
|
|
69
|
+
parameters:
|
|
70
|
+
ctx(object): behave context object used to share data between steps
|
|
71
|
+
name(str): name that identifies the desired radio button on screen
|
|
72
|
+
index(str): the index of the radio button if there are duplicates
|
|
73
|
+
|
|
74
|
+
returns:
|
|
75
|
+
the WebElement that matches the provided arguments.
|
|
76
|
+
"""
|
|
77
|
+
ctx.check_browser_initialized()
|
|
78
|
+
radio = find_n_assert_radio_button(ctx, name, index=index)
|
|
79
|
+
|
|
80
|
+
if base_steps.is_disabled(radio):
|
|
81
|
+
raise RuntimeError(
|
|
82
|
+
"unable to select the radio button, as it is disabled"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
selected = radio.get_attribute("checked") == "true"
|
|
86
|
+
|
|
87
|
+
if selected:
|
|
88
|
+
if ignore_if_selected:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
raise Exception(f'radio button "{name}" already selected')
|
|
92
|
+
|
|
93
|
+
ctx.browser.click(radio)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def is_selected(radio):
|
|
97
|
+
"""
|
|
98
|
+
internal method to check if radiobox is selected
|
|
99
|
+
"""
|
|
100
|
+
return bool(radio.get_attribute("checked"))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def is_not_selected(radio):
|
|
104
|
+
"""
|
|
105
|
+
internal method to check if a radiobox is not selected
|
|
106
|
+
"""
|
|
107
|
+
return not is_selected(radio)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def select_radio_button(ctx, radiobox):
|
|
111
|
+
"""
|
|
112
|
+
internal method to select a radio button that isn't already selected
|
|
113
|
+
"""
|
|
114
|
+
ctx.check_browser_initialized()
|
|
115
|
+
|
|
116
|
+
if base_steps.is_disabled(radiobox):
|
|
117
|
+
raise RuntimeError(
|
|
118
|
+
"unable to select the radio button, as it is disabled"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
selected = bool(radiobox.get_attribute("checked"))
|
|
122
|
+
|
|
123
|
+
if selected:
|
|
124
|
+
raise Exception("radiobox already selected")
|
|
125
|
+
|
|
126
|
+
ctx.browser.click(radiobox)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
helpers.define_should_see_thing_with_name_steps(
|
|
130
|
+
"radio button", find_radio_button
|
|
131
|
+
)
|
|
132
|
+
helpers.define_action_on_thing_with_name_steps(
|
|
133
|
+
"radio button", "select", find_radio_button, select_radio_button
|
|
134
|
+
)
|
|
135
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
136
|
+
"radio button", "selected", find_radio_button, is_selected, with_nth=True
|
|
137
|
+
)
|
|
138
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
139
|
+
"radio button", "not selected", find_radio_button, is_not_selected
|
|
140
|
+
)
|
|
141
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
142
|
+
"radio button", "disabled", find_radio_button, base_steps.is_disabled
|
|
143
|
+
)
|
|
144
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
145
|
+
"radio button",
|
|
146
|
+
"not disabled",
|
|
147
|
+
find_radio_button,
|
|
148
|
+
base_steps.is_not_disabled,
|
|
149
|
+
)
|
|
150
|
+
helpers.define_run_steps_if_I_can_see_element_with_name_steps(
|
|
151
|
+
"radio", find_radio_button
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@step('I immediately select the radio button "{name}" if it is not selected')
|
|
156
|
+
def immediately_select_the_radio_button_if_not_selected(ctx, name):
|
|
157
|
+
find_n_select_radio_button(ctx, name, ignore_if_selected=True)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@step('I select the radio button "{name}" if it is not selected')
|
|
161
|
+
def select_the_radio_button_if_not_selected(ctx, name):
|
|
162
|
+
retry(
|
|
163
|
+
find_n_select_radio_button,
|
|
164
|
+
retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
|
|
165
|
+
wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
|
|
166
|
+
)(ctx, name, ignore_if_selected=True)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@step('I wait to select the radio button "{name}" if it is not selected')
|
|
170
|
+
def wait_to_select_the_radio_button_if_not_selected(ctx, name):
|
|
171
|
+
retry(find_n_select_radio_button)(ctx, name, ignore_if_selected=True)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@step(
|
|
175
|
+
'I immediately select the "{nth:nth}" radio button "{name}" if it is not selected'
|
|
176
|
+
)
|
|
177
|
+
def immediately_select_nth_radio_button_if_not_selected(ctx, nth, name):
|
|
178
|
+
find_n_select_radio_button(ctx, name, nth, ignore_if_selected=True)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@step('I select the "{nth:nth}" radio button "{name}" if it is not selected')
|
|
182
|
+
def select_nth_radio_button(ctx, nth, name):
|
|
183
|
+
retry(
|
|
184
|
+
find_n_select_radio_button,
|
|
185
|
+
retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
|
|
186
|
+
wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
|
|
187
|
+
)(ctx, name, nth, ignore_if_selected=True)
|
cucu/steps/step_utils.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from cucu import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def search(regex, value):
|
|
7
|
+
"""
|
|
8
|
+
search for a matching regex within the value provided.
|
|
9
|
+
"""
|
|
10
|
+
match = re.search(regex, value)
|
|
11
|
+
|
|
12
|
+
if match is None:
|
|
13
|
+
raise RuntimeError(f'"{regex}" did not match anything in "{value}"')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def search_and_save(regex, value, name, variable):
|
|
17
|
+
"""
|
|
18
|
+
search for a matching regex within the value provided and then save the value
|
|
19
|
+
of the group name provided to the variable name provided.
|
|
20
|
+
"""
|
|
21
|
+
match = re.search(regex, value)
|
|
22
|
+
|
|
23
|
+
if match is None:
|
|
24
|
+
raise RuntimeError(f'"{regex}" did not match anything in "{value}"')
|
|
25
|
+
|
|
26
|
+
groups = match.groupdict()
|
|
27
|
+
|
|
28
|
+
if name in groups:
|
|
29
|
+
config.CONFIG[variable] = groups[name]
|
|
30
|
+
|
|
31
|
+
else:
|
|
32
|
+
raise RuntimeError(
|
|
33
|
+
f'group "{name}" not found when searching for regex "{regex}" in "{value}"'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def match_and_save(regex, value, name, variable):
|
|
38
|
+
"""
|
|
39
|
+
match the regex in the value provided and then save the value of the group
|
|
40
|
+
name provided to the variable name provided.
|
|
41
|
+
"""
|
|
42
|
+
match = re.match(regex, value)
|
|
43
|
+
|
|
44
|
+
if match is None:
|
|
45
|
+
raise RuntimeError(f'"{regex}" did not match "{value}"')
|
|
46
|
+
|
|
47
|
+
groups = match.groupdict()
|
|
48
|
+
|
|
49
|
+
if name in groups:
|
|
50
|
+
config.CONFIG[variable] = groups[name]
|
|
51
|
+
|
|
52
|
+
else:
|
|
53
|
+
raise RuntimeError(
|
|
54
|
+
f'group "{name}" not found when matching regex "{regex}" to "{value}"'
|
|
55
|
+
)
|
cucu/steps/tab_steps.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from cucu import fuzzy, helpers
|
|
2
|
+
from cucu.utils import take_saw_element_screenshot
|
|
3
|
+
|
|
4
|
+
from . import base_steps
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def find_tab(ctx, name, index=0):
|
|
8
|
+
"""
|
|
9
|
+
find a tab containing the text provide.
|
|
10
|
+
|
|
11
|
+
parameters:
|
|
12
|
+
ctx(object): behave context object used to share data between steps
|
|
13
|
+
name(str): name that identifies the desired tab on screen
|
|
14
|
+
index(str): the index of the tab if there are duplicates
|
|
15
|
+
|
|
16
|
+
returns:
|
|
17
|
+
the WebElement that matches the provided arguments.
|
|
18
|
+
"""
|
|
19
|
+
ctx.check_browser_initialized()
|
|
20
|
+
element = fuzzy.find(ctx.browser, name, ['*[role="tab"]'], index=index)
|
|
21
|
+
|
|
22
|
+
take_saw_element_screenshot(ctx, "tab", name, index, element)
|
|
23
|
+
|
|
24
|
+
return element
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def click_tab(ctx, tab):
|
|
28
|
+
"""
|
|
29
|
+
internal method to click a tab
|
|
30
|
+
"""
|
|
31
|
+
ctx.check_browser_initialized()
|
|
32
|
+
|
|
33
|
+
if base_steps.is_disabled(tab):
|
|
34
|
+
raise RuntimeError("unable to click the tab, as it is disabled")
|
|
35
|
+
|
|
36
|
+
ctx.browser.click(tab)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def is_selected(tab):
|
|
40
|
+
"""
|
|
41
|
+
internal method to check if a tab is currently selected
|
|
42
|
+
"""
|
|
43
|
+
return tab.get_attribute("aria-selected") == "true"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_not_selected(tab):
|
|
47
|
+
"""
|
|
48
|
+
internal method to check if a tab is currently not selected
|
|
49
|
+
"""
|
|
50
|
+
return not is_selected(tab)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
helpers.define_should_see_thing_with_name_steps("tab", find_tab)
|
|
54
|
+
helpers.define_action_on_thing_with_name_steps(
|
|
55
|
+
"tab", "click", find_tab, click_tab
|
|
56
|
+
)
|
|
57
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
58
|
+
"tab", "selected", find_tab, is_selected
|
|
59
|
+
)
|
|
60
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
61
|
+
"tab", "not selected", find_tab, is_not_selected
|
|
62
|
+
)
|
|
63
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
64
|
+
"tab", "disabled", find_tab, base_steps.is_disabled
|
|
65
|
+
)
|
|
66
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
67
|
+
"tab", "not disabled", find_tab, base_steps.is_not_disabled
|
|
68
|
+
)
|