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,337 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from selenium.webdriver.common.keys import Keys
|
|
5
|
+
|
|
6
|
+
from cucu import config, logger, retry, run_steps, step
|
|
7
|
+
from cucu.browser.selenium import Selenium
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def open_browser(ctx):
|
|
11
|
+
browser_name = config.CONFIG["CUCU_BROWSER"]
|
|
12
|
+
headless = config.CONFIG["CUCU_BROWSER_HEADLESS"]
|
|
13
|
+
selenium_remote_url = config.CONFIG["CUCU_SELENIUM_REMOTE_URL"]
|
|
14
|
+
|
|
15
|
+
browser = Selenium()
|
|
16
|
+
logger.debug(f"opening browser {browser_name}")
|
|
17
|
+
browser.open(
|
|
18
|
+
browser_name,
|
|
19
|
+
headless=headless,
|
|
20
|
+
selenium_remote_url=selenium_remote_url,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return browser
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@step('I open a browser at the url "{url}"')
|
|
27
|
+
def open_a_browser(ctx, url):
|
|
28
|
+
"""
|
|
29
|
+
open a browser at the url provided
|
|
30
|
+
|
|
31
|
+
example:
|
|
32
|
+
Given I open a browser at the url "https://www.google.com"
|
|
33
|
+
"""
|
|
34
|
+
if ctx.browser is None:
|
|
35
|
+
ctx.browser = open_browser(ctx)
|
|
36
|
+
ctx.browsers.append(ctx.browser)
|
|
37
|
+
else:
|
|
38
|
+
logger.debug("browser already open so using existing instance")
|
|
39
|
+
|
|
40
|
+
logger.debug(f"navigating to url #{url}")
|
|
41
|
+
ctx.browser.navigate(url)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@step('I open a new browser at the url "{url}"')
|
|
45
|
+
def open_a_new_browser(ctx, url):
|
|
46
|
+
ctx.browser = open_browser(ctx)
|
|
47
|
+
ctx.browsers.append(ctx.browser)
|
|
48
|
+
logger.debug(f"navigating to url #{url}")
|
|
49
|
+
ctx.browser.navigate(url)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@step("I execute in the current browser the following javascript")
|
|
53
|
+
def execute_javascript(ctx):
|
|
54
|
+
ctx.check_browser_initialized()
|
|
55
|
+
ctx.browser.execute(ctx.text)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@step(
|
|
59
|
+
'I execute in the current browser the following javascript and save the result to the variable "{variable}"'
|
|
60
|
+
)
|
|
61
|
+
def execute_javascript_and_save(ctx, variable):
|
|
62
|
+
ctx.check_browser_initialized()
|
|
63
|
+
result = ctx.browser.execute(ctx.text)
|
|
64
|
+
config.CONFIG[variable] = result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def assert_url_is(ctx, value):
|
|
68
|
+
ctx.check_browser_initialized()
|
|
69
|
+
url = ctx.browser.get_current_url()
|
|
70
|
+
if value == url:
|
|
71
|
+
raise RuntimeError(f"current url is {url}, not {value}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@step('I should see the current url is "{value}"')
|
|
75
|
+
def should_see_the_current_url_is(ctx, value):
|
|
76
|
+
assert_url_is(ctx, value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@step('I wait to see the current url is "{value}"')
|
|
80
|
+
def wait_to_see_the_current_url_is(ctx, value):
|
|
81
|
+
retry(assert_url_is)(ctx, value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@step('I save the current url to the variable "{variable}"')
|
|
85
|
+
def save_current_url_to_variable(ctx, variable):
|
|
86
|
+
ctx.check_browser_initialized()
|
|
87
|
+
config.CONFIG[variable] = ctx.browser.get_current_url()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@step("I refresh the browser")
|
|
91
|
+
def refresh_browser(ctx):
|
|
92
|
+
ctx.check_browser_initialized()
|
|
93
|
+
ctx.browser.refresh()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@step("I go back on the browser")
|
|
97
|
+
def go_back_on_browser(ctx):
|
|
98
|
+
ctx.check_browser_initialized()
|
|
99
|
+
ctx.browser.back()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@step('I save the contents of the clipboard to the variable "{variable}"')
|
|
103
|
+
def save_clipboard_value_to_variable(ctx, variable):
|
|
104
|
+
ctx.check_browser_initialized()
|
|
105
|
+
|
|
106
|
+
# use default frame when adding elements to the document to avoid this errro on access:
|
|
107
|
+
# "selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable"
|
|
108
|
+
ctx.browser.switch_to_default_frame()
|
|
109
|
+
|
|
110
|
+
# create the hidden textarea so we can paste clipboard contents in
|
|
111
|
+
textarea = ctx.browser.execute(
|
|
112
|
+
"""
|
|
113
|
+
var textarea = document.getElementById('cucu-copy-n-paste')
|
|
114
|
+
if (!textarea) {
|
|
115
|
+
textarea = document.createElement('textarea');
|
|
116
|
+
textarea.setAttribute('id', 'cucu-copy-n-paste');
|
|
117
|
+
textarea.style.display = 'hidden';
|
|
118
|
+
textarea.style.height = '0px';
|
|
119
|
+
textarea.style.width = '0px';
|
|
120
|
+
document.body.insertBefore(textarea, document.body.firstChild);
|
|
121
|
+
}
|
|
122
|
+
return textarea;
|
|
123
|
+
"""
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# send ctrl+v or cmd+v to that element
|
|
127
|
+
if "mac" in ctx.browser.execute("return navigator.platform").lower():
|
|
128
|
+
textarea.send_keys(Keys.COMMAND, "v")
|
|
129
|
+
else:
|
|
130
|
+
textarea.send_keys(Keys.CONTROL, "v")
|
|
131
|
+
|
|
132
|
+
clipboard_contents = textarea.get_attribute("value")
|
|
133
|
+
config.CONFIG[variable] = clipboard_contents
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@step('I should see the browser title is "{title}"')
|
|
137
|
+
def should_see_browser_title(ctx, title):
|
|
138
|
+
ctx.check_browser_initialized()
|
|
139
|
+
current_title = ctx.browser.title()
|
|
140
|
+
|
|
141
|
+
if current_title != title:
|
|
142
|
+
raise RuntimeError(f'unexpected browser title, got "{current_title}"')
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@step("I close the current browser")
|
|
146
|
+
def close_browser(ctx):
|
|
147
|
+
ctx.check_browser_initialized()
|
|
148
|
+
browser_index = ctx.browsers.index(ctx.browser)
|
|
149
|
+
|
|
150
|
+
if browser_index > 0:
|
|
151
|
+
ctx.browser = ctx.browsers[browser_index - 1]
|
|
152
|
+
else:
|
|
153
|
+
ctx.browser = None
|
|
154
|
+
|
|
155
|
+
ctx.browsers[browser_index].quit()
|
|
156
|
+
del ctx.browsers[browser_index]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@step('I navigate to the url "{url}"')
|
|
160
|
+
def navigate_to_the_url(ctx, url):
|
|
161
|
+
ctx.check_browser_initialized()
|
|
162
|
+
logger.debug(f"navigating to url #{url}")
|
|
163
|
+
ctx.browser.navigate(url)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@step("I switch to the previous browser")
|
|
167
|
+
def switch_to_previous_browser(ctx):
|
|
168
|
+
browser_index = ctx.browsers.index(ctx.browser)
|
|
169
|
+
|
|
170
|
+
if browser_index > 0:
|
|
171
|
+
ctx.browser = ctx.browsers[browser_index - 1]
|
|
172
|
+
else:
|
|
173
|
+
raise RuntimeError("no previous browser window available")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@step("I switch to the next browser")
|
|
177
|
+
def switch_to_next_browser(ctx):
|
|
178
|
+
browser_index = ctx.browsers.index(ctx.browser)
|
|
179
|
+
|
|
180
|
+
if browser_index < len(ctx.browsers) - 1:
|
|
181
|
+
ctx.browser = ctx.browsers[browser_index + 1]
|
|
182
|
+
else:
|
|
183
|
+
raise RuntimeError("no next browser window available")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@step("I close the current browser tab")
|
|
187
|
+
def close_browser_tab(ctx):
|
|
188
|
+
ctx.check_browser_initialized()
|
|
189
|
+
ctx.browser.close_window()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def switch_to_next_tab(ctx):
|
|
193
|
+
ctx.check_browser_initialized()
|
|
194
|
+
ctx.browser.switch_to_next_tab()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@step("I switch to the next browser tab")
|
|
198
|
+
def switch_to_next_browser_tab(ctx):
|
|
199
|
+
switch_to_next_tab(ctx)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@step("I wait to switch to the next browser tab")
|
|
203
|
+
def wait_to_switch_to_next_browser_tab(ctx):
|
|
204
|
+
retry(switch_to_next_tab)(ctx)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def switch_to_previous_tab(ctx):
|
|
208
|
+
ctx.check_browser_initialized()
|
|
209
|
+
ctx.browser.switch_to_previous_tab()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@step("I switch to the previous browser tab")
|
|
213
|
+
def switch_to_previous_browser_tab(ctx):
|
|
214
|
+
switch_to_previous_tab(ctx)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@step("I wait to switch to the previous browser tab")
|
|
218
|
+
def wait_to_switch_to_previous_browser_tab(ctx):
|
|
219
|
+
retry(switch_to_previous_tab)(ctx)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def save_downloaded_file(ctx, filename):
|
|
223
|
+
ctx.check_browser_initialized()
|
|
224
|
+
|
|
225
|
+
# use default frame when adding elements to the document to avoid this errro on access:
|
|
226
|
+
# "selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable"
|
|
227
|
+
ctx.browser.switch_to_default_frame()
|
|
228
|
+
|
|
229
|
+
elem = ctx.browser.execute(
|
|
230
|
+
"""
|
|
231
|
+
var input = window.document.createElement('INPUT');
|
|
232
|
+
input.setAttribute('type', 'file');
|
|
233
|
+
input.hidden = true;
|
|
234
|
+
input.onchange = function (e) { e.stopPropagation() };
|
|
235
|
+
return window.document.documentElement.appendChild(input);
|
|
236
|
+
"""
|
|
237
|
+
)
|
|
238
|
+
cucu_downloads_dir = config.CONFIG["CUCU_BROWSER_DOWNLOADS_DIR"]
|
|
239
|
+
elem.send_keys(f"{cucu_downloads_dir}/{filename}")
|
|
240
|
+
ctx.browser.execute(
|
|
241
|
+
"""
|
|
242
|
+
var input = arguments[0];
|
|
243
|
+
window.__cucu_downloaded_file = null;
|
|
244
|
+
var reader = new FileReader();
|
|
245
|
+
reader.onload = function (ev) {
|
|
246
|
+
window.__cucu_downloaded_file = reader.result;
|
|
247
|
+
};
|
|
248
|
+
reader.onerror = function (ex) {
|
|
249
|
+
window.__cucu_downloaded_file = ex.message;
|
|
250
|
+
};
|
|
251
|
+
reader.readAsDataURL(input.files[0]);
|
|
252
|
+
input.remove();
|
|
253
|
+
""",
|
|
254
|
+
elem,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def wait_for_file():
|
|
258
|
+
if (
|
|
259
|
+
ctx.browser.execute("return window.__cucu_downloaded_file;")
|
|
260
|
+
is None
|
|
261
|
+
):
|
|
262
|
+
raise RuntimeError(f"waiting on file {filename}")
|
|
263
|
+
|
|
264
|
+
retry(wait_for_file)()
|
|
265
|
+
|
|
266
|
+
result = ctx.browser.execute("return window.__cucu_downloaded_file;")
|
|
267
|
+
if not result.startswith("data:"):
|
|
268
|
+
raise Exception("Failed to get file content: %s" % result)
|
|
269
|
+
|
|
270
|
+
filedata = base64.b64decode(result[result.find("base64,") + 7 :])
|
|
271
|
+
scenario_downloads_dir = config.CONFIG["SCENARIO_DOWNLOADS_DIR"]
|
|
272
|
+
download_filepath = os.path.join(scenario_downloads_dir, filename)
|
|
273
|
+
open(download_filepath, "wb").write(filedata)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@step('I wait to see the downloaded file "{filename}"')
|
|
277
|
+
def wait_to_see_downloaded_file(ctx, filename):
|
|
278
|
+
"""
|
|
279
|
+
wait to see the expected downloaded filename appears in the current
|
|
280
|
+
browsers download directory and internally we then copy the contents of
|
|
281
|
+
that file to the SCENARIO_DOWNLOADS_DIR so the test can continue to
|
|
282
|
+
use the file as it deems necessary.
|
|
283
|
+
"""
|
|
284
|
+
retry(save_downloaded_file)(ctx, filename)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@step(
|
|
288
|
+
'I wait up to "{seconds}" seconds to see the downloaded file "{filename}"'
|
|
289
|
+
)
|
|
290
|
+
def wait_up_to_seconds_to_see_downloaded_file(ctx, seconds, filename):
|
|
291
|
+
seconds = float(seconds)
|
|
292
|
+
retry(save_downloaded_file, wait_up_to_s=seconds)(ctx, filename)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@step('I download an mht archive of the current page to "{file_path}"')
|
|
296
|
+
def download_mht_archive(ctx, file_path):
|
|
297
|
+
ctx.browser.download_mht(file_path)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@step('I run the following steps if the current browser is "{name}"')
|
|
301
|
+
def run_if_browser(ctx, name):
|
|
302
|
+
if config.CONFIG["CUCU_BROWSER"].lower() == name.lower():
|
|
303
|
+
run_steps(ctx, ctx.text)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@step('I do not run the following steps if the current browser is "{name}"')
|
|
307
|
+
def run_if_not_browser(ctx, name):
|
|
308
|
+
if config.CONFIG["CUCU_BROWSER"].lower() != name.lower():
|
|
309
|
+
run_steps(ctx, ctx.text)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@step('I skip this scenario if the current browser is "{name}"')
|
|
313
|
+
def skip_if_browser(ctx, name):
|
|
314
|
+
if config.CONFIG["CUCU_BROWSER"].lower() == name.lower():
|
|
315
|
+
ctx.scenario.skip(reason=f"skipping scenario since we're on {name}")
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@step('I skip this scenario if the current browser is not "{name}"')
|
|
319
|
+
def skip_if_not_browser(ctx, name):
|
|
320
|
+
if config.CONFIG["CUCU_BROWSER"].lower() != name.lower():
|
|
321
|
+
ctx.scenario.skip(
|
|
322
|
+
reason=f"skipping scenario since we're not on {name}"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@step('I save the browser cookie "{cookie_name}" to the variable "{variable}"')
|
|
327
|
+
def save_browser_cookie(ctx, cookie_name, variable):
|
|
328
|
+
ctx.check_browser_initialized()
|
|
329
|
+
config.CONFIG[variable] = ctx.browser.driver.get_cookie(cookie_name)[
|
|
330
|
+
"value"
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@step('I set the browser cookie "{name}" a value of "{value}"')
|
|
335
|
+
def add_browser_cookie(ctx, name, value):
|
|
336
|
+
ctx.check_browser_initialized()
|
|
337
|
+
ctx.browser.driver.add_cookie({"name": name, "value": value})
|
|
@@ -0,0 +1,91 @@
|
|
|
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_button(ctx, name, index=0):
|
|
8
|
+
"""
|
|
9
|
+
find a button on screen by fuzzy matching on the name and index provided.
|
|
10
|
+
|
|
11
|
+
* <button>
|
|
12
|
+
* <input type="button">
|
|
13
|
+
* <input type="submit">
|
|
14
|
+
* <a>
|
|
15
|
+
* <* role="button">
|
|
16
|
+
* <* role="link">
|
|
17
|
+
* <* role="menuitem">
|
|
18
|
+
* <* role="treetem">
|
|
19
|
+
* <* role="option">
|
|
20
|
+
* <* role="radio">
|
|
21
|
+
|
|
22
|
+
note: the reason we're allowing other items such as menuitem, option, etc
|
|
23
|
+
is that on screen they can present themselves like "buttons". When
|
|
24
|
+
searching for more things to include use the following image
|
|
25
|
+
reference:
|
|
26
|
+
|
|
27
|
+
https://www.w3.org/TR/2009/WD-wai-aria-20091215/rdf_model.png
|
|
28
|
+
|
|
29
|
+
parameters:
|
|
30
|
+
ctx(object): behave context object used to share data between steps
|
|
31
|
+
name(str): name that identifies the desired button on screen
|
|
32
|
+
index(str): the index of the button if there are duplicates
|
|
33
|
+
|
|
34
|
+
returns:
|
|
35
|
+
the WebElement that matches the provided arguments.
|
|
36
|
+
"""
|
|
37
|
+
ctx.check_browser_initialized()
|
|
38
|
+
element = fuzzy.find(
|
|
39
|
+
ctx.browser,
|
|
40
|
+
name,
|
|
41
|
+
[
|
|
42
|
+
"button",
|
|
43
|
+
'input[type="button"]',
|
|
44
|
+
'input[type="submit"]',
|
|
45
|
+
"a",
|
|
46
|
+
'*[role="button"]',
|
|
47
|
+
'*[role="link"]',
|
|
48
|
+
'*[role="menuitem"]',
|
|
49
|
+
'*[role="treeitem"]',
|
|
50
|
+
'*[role="option"]',
|
|
51
|
+
'*[role="radio"]',
|
|
52
|
+
],
|
|
53
|
+
index=index,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
take_saw_element_screenshot(ctx, "button", name, index, element)
|
|
57
|
+
|
|
58
|
+
return element
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def click_button(ctx, button):
|
|
62
|
+
"""
|
|
63
|
+
internal method used to simply click a button element
|
|
64
|
+
"""
|
|
65
|
+
ctx.check_browser_initialized()
|
|
66
|
+
|
|
67
|
+
if base_steps.is_disabled(button):
|
|
68
|
+
raise RuntimeError("unable to click the button, as it is disabled")
|
|
69
|
+
|
|
70
|
+
ctx.browser.click(button)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
helpers.define_should_see_thing_with_name_steps(
|
|
74
|
+
"button", find_button, with_nth=True
|
|
75
|
+
)
|
|
76
|
+
helpers.define_action_on_thing_with_name_steps(
|
|
77
|
+
"button", "click", find_button, click_button, with_nth=True
|
|
78
|
+
)
|
|
79
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
80
|
+
"button", "disabled", find_button, base_steps.is_disabled, with_nth=True
|
|
81
|
+
)
|
|
82
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
83
|
+
"button",
|
|
84
|
+
"not disabled",
|
|
85
|
+
find_button,
|
|
86
|
+
base_steps.is_not_disabled,
|
|
87
|
+
with_nth=True,
|
|
88
|
+
)
|
|
89
|
+
helpers.define_run_steps_if_I_can_see_element_with_name_steps(
|
|
90
|
+
"button", find_button
|
|
91
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
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_checkbox(ctx, name, index=0):
|
|
8
|
+
"""
|
|
9
|
+
find a checkbox on screen using name and index provided
|
|
10
|
+
|
|
11
|
+
* <input type="checkbox">
|
|
12
|
+
* <* role="checkbox">
|
|
13
|
+
|
|
14
|
+
parameters:
|
|
15
|
+
ctx(object): behave context object used to share data between steps
|
|
16
|
+
name(str): name that identifies the desired checkbox on screen
|
|
17
|
+
index(str): the index of the checkbox if there are duplicates
|
|
18
|
+
|
|
19
|
+
returns:
|
|
20
|
+
the WebElement that matches the provided arguments.
|
|
21
|
+
"""
|
|
22
|
+
ctx.check_browser_initialized()
|
|
23
|
+
|
|
24
|
+
element = fuzzy.find(
|
|
25
|
+
ctx.browser,
|
|
26
|
+
name,
|
|
27
|
+
[
|
|
28
|
+
'input[type="checkbox"]',
|
|
29
|
+
'*[role="checkbox"]',
|
|
30
|
+
],
|
|
31
|
+
index=index,
|
|
32
|
+
direction=fuzzy.Direction.RIGHT_TO_LEFT,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
take_saw_element_screenshot(ctx, "checkbox", name, index, element)
|
|
36
|
+
|
|
37
|
+
return element
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_checked(checkbox):
|
|
41
|
+
"""
|
|
42
|
+
internal method to check a checkbox is checked
|
|
43
|
+
"""
|
|
44
|
+
return (
|
|
45
|
+
checkbox.get_attribute("checked")
|
|
46
|
+
or checkbox.get_attribute("aria-checked") == "true"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def is_not_checked(checkbox):
|
|
51
|
+
"""
|
|
52
|
+
internal method to check a checkbox is not checked
|
|
53
|
+
"""
|
|
54
|
+
return not is_checked(checkbox)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def check_checkbox(ctx, checkbox):
|
|
58
|
+
"""
|
|
59
|
+
internal method used to check a checkbox if it is not already checked
|
|
60
|
+
"""
|
|
61
|
+
ctx.check_browser_initialized()
|
|
62
|
+
|
|
63
|
+
if is_checked(checkbox):
|
|
64
|
+
raise Exception("checkbox already checked")
|
|
65
|
+
|
|
66
|
+
if base_steps.is_disabled(checkbox):
|
|
67
|
+
raise RuntimeError("unable to check the checkbox, as it is disabled")
|
|
68
|
+
|
|
69
|
+
ctx.browser.click(checkbox)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def uncheck_checkbox(ctx, checkbox):
|
|
73
|
+
"""
|
|
74
|
+
internal method used to uncheck a checkbox if it is not already unchecked
|
|
75
|
+
"""
|
|
76
|
+
ctx.check_browser_initialized()
|
|
77
|
+
|
|
78
|
+
if is_not_checked(checkbox):
|
|
79
|
+
raise Exception("checkbox already unchecked")
|
|
80
|
+
|
|
81
|
+
ctx.browser.click(checkbox)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
helpers.define_should_see_thing_with_name_steps("checkbox", find_checkbox)
|
|
85
|
+
helpers.define_action_on_thing_with_name_steps(
|
|
86
|
+
"checkbox", "check", find_checkbox, check_checkbox
|
|
87
|
+
)
|
|
88
|
+
helpers.define_action_on_thing_with_name_steps(
|
|
89
|
+
"checkbox", "uncheck", find_checkbox, uncheck_checkbox
|
|
90
|
+
)
|
|
91
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
92
|
+
"checkbox", "checked", find_checkbox, is_checked
|
|
93
|
+
)
|
|
94
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
95
|
+
"checkbox", "not checked", find_checkbox, is_not_checked
|
|
96
|
+
)
|
|
97
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
98
|
+
"checkbox", "disabled", find_checkbox, base_steps.is_disabled
|
|
99
|
+
)
|
|
100
|
+
helpers.define_thing_with_name_in_state_steps(
|
|
101
|
+
"checkbox", "not disabled", find_checkbox, base_steps.is_not_disabled
|
|
102
|
+
)
|
|
103
|
+
helpers.define_run_steps_if_I_can_see_element_with_name_steps(
|
|
104
|
+
"checkbox", find_checkbox
|
|
105
|
+
)
|
|
106
|
+
helpers.define_ensure_state_on_thing_with_name_steps(
|
|
107
|
+
"checkbox", "checked", find_checkbox, is_checked, check_checkbox
|
|
108
|
+
)
|
|
109
|
+
helpers.define_ensure_state_on_thing_with_name_steps(
|
|
110
|
+
"checkbox", "not checked", find_checkbox, is_not_checked, uncheck_checkbox
|
|
111
|
+
)
|