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
cucu/helpers.py ADDED
@@ -0,0 +1,875 @@
1
+ import sys
2
+
3
+ import humanize
4
+
5
+ from cucu import logger, retry, run_steps
6
+ from cucu.config import CONFIG
7
+
8
+
9
+ class step(object):
10
+ """
11
+ Only to be used in this file as we're redefining the @step decorator in
12
+ order to fix the code location of the @step() usage.
13
+
14
+ Basically when you use any of the `define_xxx` steps below internally they
15
+ call @step() and when that decorator executes in behave it will take the
16
+ location of the function provided to the decorator and use that to both
17
+ report as the location of the step's "source"
18
+ (ie uv run behave --no-summary --format steps.doc --dry-run). Now that
19
+ would be confusing as the step is likely defined somewhere under
20
+ `src/cucu/steps` but the location in the behave steps output would be wrong
21
+ and then at runtime if there's an exception throw from one of the other
22
+ functions inside the `define_xxx` method that trace would also not originate
23
+ from the `src/cucu/steps` location where the `define_xxx` was called.
24
+
25
+ So this redefinition of @step will basically use a hook in the existing
26
+ @step definition in `src/cucu/behave_tweaks.py` and provide a function
27
+ to "fix" the inner_step function so its own code location now points back
28
+ to the location where the `define_xxx` was called from.
29
+ """
30
+
31
+ def __init__(self, step_text):
32
+ self.step_text = step_text
33
+ frame = sys._getframe(1).f_back
34
+
35
+ def fix_inner_step(func):
36
+ #
37
+ # Here is where we "fix" the code location for the function being
38
+ # passed to the step() decorator. We are basically realigning the
39
+ # filename and line location so it points to the original caller
40
+ # of the `define_xxx` method. To do this alignment though we have
41
+ # to subtract from the original file line number the number of code
42
+ # lines in the `src/cucu/behave_tweaks.py` between the functions
43
+ # wrapper and inner_step as that is the "actual code location" at
44
+ # runtime and below we can only set the `firstlineno` which is the
45
+ # "first line" of our __code__ point and by the time those N lines
46
+ # execute the line number python would print would be N lines ahead
47
+ # of the original location of the code we're trying to link to.
48
+ #
49
+ func.__code__ = func.__code__.replace(
50
+ co_filename=frame.f_code.co_filename,
51
+ co_firstlineno=frame.f_lineno - 3,
52
+ co_name=frame.f_code.co_name,
53
+ )
54
+
55
+ self.fix_inner_step = fix_inner_step
56
+
57
+ def __call__(self, func):
58
+ from behave import step
59
+
60
+ @step(self.step_text, fix_inner_step=self.fix_inner_step)
61
+ def wrapper(*args, **kwargs):
62
+ func(*args, **kwargs)
63
+
64
+ return wrapper
65
+
66
+
67
+ def nth_to_ordinal(index):
68
+ return "" if index == 0 else f'"{humanize.ordinal(index + 1)}" '
69
+
70
+
71
+ def define_should_see_thing_with_name_steps(thing, find_func, with_nth=False):
72
+ """
73
+ defines steps with with the following signatures:
74
+
75
+ I should immediately see the {thing} "{name}"
76
+ I should see the {thing} "{name}"
77
+ I wait to see the {thing} "{name}"
78
+ I wait up to "{seconds}" seconds to see the {thing} "{name}"
79
+
80
+ when with_nth=True we also define:
81
+ I should immediately see the "{nth}" {thing} "{name}"
82
+ I should see the "{nth}" {thing} "{name}"
83
+ I wait to see the "{nth}" {thing} "{name}"
84
+ I wait up to "{seconds}" seconds to see the "{nth}" {thing} "{name}"
85
+
86
+ I should immediately not see the {thing} "{name}"
87
+ I should not see the {thing} "{name}"
88
+ I wait to not see the {thing} "{name}"
89
+ I wait up to "{seconds}" seconds to not see the {thing} "{name}"
90
+
91
+ when with_nth=True we also define:
92
+ I should immediately not see the "{nth}" {thing} "{name}"
93
+ I should not see the "{nth}" {thing} "{name}"
94
+ I wait to not see the "{nth}" {thing} "{name}"
95
+ I wait up to "{seconds}" seconds to not see the "{nth}" {thing} "{name}"
96
+
97
+ parameters:
98
+ thing(string): name of the thing we're creating the steps for such
99
+ as button, dialog, etc.
100
+ find_func(function): function that returns the desired element:
101
+
102
+ def find_func(ctx, name, index=):
103
+ '''
104
+ ctx(object): behave context object
105
+ name(string): name of the thing to find
106
+ index(int): when there are multiple elements
107
+ with the same name and you've
108
+ specified with_nth=True
109
+ '''
110
+ with_nth(bool): when set to True we'll define the expanded set of
111
+ "nth" steps. default: False
112
+ """
113
+
114
+ # should see
115
+ # undecorated def for reference below
116
+ def base_should_see_the(ctx, thing, name, index=0):
117
+ prefix = nth_to_ordinal(index)
118
+ element = find_func(ctx, name, index=index)
119
+
120
+ if element is None:
121
+ raise RuntimeError(f'unable to find the {prefix}{thing} "{name}"')
122
+ logger.debug(f'Success: saw {prefix}{thing} "{name}"')
123
+
124
+ @step(f'I should immediately see the {thing} "{{name}}"')
125
+ def should_immediately_see_the(ctx, thing, name):
126
+ base_should_see_the(ctx, thing, name)
127
+
128
+ @step(f'I should see the {thing} "{{name}}"')
129
+ def should_see_the(ctx, name):
130
+ retry(
131
+ base_should_see_the,
132
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
133
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
134
+ )(ctx, thing, name)
135
+
136
+ @step(f'I wait to see the {thing} "{{name}}"')
137
+ def wait_to_see_the(ctx, name):
138
+ retry(base_should_see_the)(ctx, thing, name)
139
+
140
+ @step(f'I wait up to "{{seconds}}" seconds to see the {thing} "{{name}}"')
141
+ def wait_up_to_seconds_to_see_the(ctx, seconds, name):
142
+ milliseconds = float(seconds)
143
+ retry(base_should_see_the, wait_up_to_s=milliseconds)(ctx, thing, name)
144
+
145
+ if with_nth:
146
+
147
+ @step(f'I should immediately see the "{{nth:nth}}" {thing} "{{name}}"')
148
+ def base_should_see_the_nth(ctx, nth, name):
149
+ base_should_see_the(ctx, thing, name, index=nth)
150
+
151
+ @step(f'I should see the "{{nth:nth}}" {thing} "{{name}}"')
152
+ def should_see_the_nth(ctx, nth, name):
153
+ retry(
154
+ base_should_see_the,
155
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
156
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
157
+ )(ctx, thing, name, index=nth)
158
+
159
+ @step(f'I wait to see the "{{nth:nth}}" {thing} "{{name}}"')
160
+ def wait_to_see_the_nth(ctx, nth, name):
161
+ retry(base_should_see_the)(ctx, thing, name, index=nth)
162
+
163
+ @step(
164
+ f'I wait up to "{{seconds}}" seconds to see the "{{nth:nth}}" {thing} "{{name}}"'
165
+ )
166
+ def wait_up_to_seconds_to_see_the_nth(ctx, seconds, nth, name):
167
+ seconds = float(seconds)
168
+ retry(base_should_see_the, wait_up_to_s=seconds)(
169
+ ctx, thing, name, index=nth
170
+ )
171
+
172
+ # should not see
173
+ # undecorated def for reference below
174
+ def base_should_not_see_the(ctx, thing, name, index=0):
175
+ prefix = nth_to_ordinal(index)
176
+ element = find_func(ctx, name, index=index)
177
+
178
+ if element is not None:
179
+ raise RuntimeError(f'able to find the {prefix}{thing} "{name}"')
180
+ logger.debug(f'Success: did not see {prefix}{thing} "{name}"')
181
+
182
+ @step(f'I should immediately not see the {thing} "{{name}}"')
183
+ def should_immediately_not_see_the(ctx, thing, name):
184
+ base_should_not_see_the(ctx, thing, name)
185
+
186
+ @step(f'I should not see the {thing} "{{name}}"')
187
+ def should_not_see_the(ctx, name):
188
+ retry(
189
+ base_should_not_see_the,
190
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
191
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
192
+ )(ctx, thing, name)
193
+
194
+ @step(f'I wait to not see the {thing} "{{name}}"')
195
+ def wait_to_not_see_the(ctx, name):
196
+ retry(base_should_not_see_the)(ctx, thing, name)
197
+
198
+ @step(
199
+ f'I wait up to "{{seconds}}" seconds to not see the {thing} "{{name}}"'
200
+ )
201
+ def wait_up_to_seconds_to_not_see_the(ctx, seconds, name):
202
+ milliseconds = float(seconds)
203
+ retry(base_should_not_see_the, wait_up_to_s=milliseconds)(
204
+ ctx, thing, name
205
+ )
206
+
207
+ if with_nth:
208
+
209
+ @step(
210
+ f'I should immediately not see the "{{nth:nth}}" {thing} "{{name}}"'
211
+ )
212
+ def should_immediately_not_see_the_nth(ctx, nth, name):
213
+ base_should_not_see_the(ctx, thing, name, index=nth)
214
+
215
+ @step(f'I should not see the "{{nth:nth}}" {thing} "{{name}}"')
216
+ def should_not_see_the_nth(ctx, nth, name):
217
+ retry(
218
+ base_should_not_see_the,
219
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
220
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
221
+ )(ctx, thing, name, index=nth)
222
+
223
+ @step(f'I wait to not see the "{{nth:nth}}" {thing} "{{name}}"')
224
+ def wait_to_not_see_the_nth(ctx, nth, name):
225
+ retry(base_should_not_see_the)(ctx, thing, name, index=nth)
226
+
227
+ @step(
228
+ f'I wait up to "{{seconds}}" seconds to not see the "{{nth:nth}}" {thing} "{{name}}"'
229
+ )
230
+ def wait_up_to_seconds_to_not_see_the_nth(ctx, seconds, nth, name):
231
+ seconds = float(seconds)
232
+ retry(base_should_not_see_the, wait_up_to_s=seconds)(
233
+ ctx, thing, name, index=nth
234
+ )
235
+
236
+
237
+ def define_action_on_thing_with_name_steps(
238
+ thing, action, find_func, action_func, with_nth=False
239
+ ):
240
+ """
241
+ defines steps with with the following signatures:
242
+
243
+ I {action} the {thing} "{name}"
244
+ I wait to {action} the {thing} "{name}"
245
+ I wait up to "{seconds}" seconds to {action} the {thing} "{name}"
246
+ ...
247
+ I {action} the {thing} "{name}" if it exists
248
+
249
+ when with_nth=True we also define:
250
+
251
+ I {action} the "{nth}" {thing} "{name}"
252
+ I wait to {action} the "{nth}" {thing} "{name}"
253
+ I wait up to "{seconds}" seconds to "{nth}" {action} the {thing} "{name}"
254
+ ...
255
+ I {action} the "{nth}" {thing} "{name}" if it exists
256
+
257
+ parameters:
258
+ thing(string): name of the thing we're creating the steps for such
259
+ as button, dialog, etc.
260
+ action(string): the name of the action being performed, such as:
261
+ click, disable, etc.
262
+ find_func(function): function that returns the desired element:
263
+
264
+ def find_func(ctx, name, index=):
265
+ '''
266
+ ctx(object): behave context object
267
+ name(string): name of the thing to find
268
+ index(int): when there are multiple elements
269
+ with the same name and you've
270
+ specified with_nth=True
271
+ '''
272
+ action_func(function): function that performs the desired action:
273
+
274
+ def action_func(ctx, element):
275
+ '''
276
+ ctx(object): behave context object
277
+ element(object): the element found
278
+ '''
279
+ with_nth(bool): when set to True we'll define the expanded set of
280
+ "nth" steps. default: False
281
+ """
282
+
283
+ # undecorated def for reference below
284
+ def base_action_the(ctx, thing, name, index=0, must_exist=True):
285
+ prefix = nth_to_ordinal(index)
286
+ element = find_func(ctx, name, index=index)
287
+
288
+ if element is None:
289
+ if must_exist:
290
+ raise RuntimeError(
291
+ f'unable to find the {prefix}{thing} "{name}"'
292
+ )
293
+ else:
294
+ action_func(ctx, element)
295
+ logger.debug(
296
+ f'Successfully executed {action} {prefix}{thing} "{name}"'
297
+ )
298
+
299
+ @step(f'I immediately {action} the {thing} "{{name}}"')
300
+ def immediately_action_the(ctx, name):
301
+ base_action_the(ctx, thing, name)
302
+
303
+ @step(f'I {action} the {thing} "{{name}}"')
304
+ def action_the(ctx, name):
305
+ retry(
306
+ base_action_the,
307
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
308
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
309
+ )(ctx, thing, name)
310
+
311
+ @step(f'I immediately {action} the {thing} "{{name}}" if it exists')
312
+ def immediately_action_the_if_it_exists(ctx, name):
313
+ base_action_the(ctx, thing, name, must_exist=False)
314
+
315
+ @step(f'I {action} the {thing} "{{name}}" if it exists')
316
+ def action_the_if_it_exists(ctx, name):
317
+ retry(
318
+ base_action_the,
319
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
320
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
321
+ )(ctx, thing, name, must_exist=False)
322
+
323
+ @step(f'I wait to {action} the {thing} "{{name}}"')
324
+ def wait_to_action_the(ctx, name):
325
+ retry(base_action_the)(ctx, thing, name)
326
+
327
+ @step(f'I wait to {action} the {thing} "{{name}}" if it exists')
328
+ def wait_to_action_the_if_it_exists(ctx, name):
329
+ retry(base_action_the)(ctx, thing, name, must_exist=False)
330
+
331
+ @step(
332
+ f'I wait up to "{{seconds}}" seconds to {action} the {thing} "{{name}}"'
333
+ )
334
+ def wait_up_to_seconds_to_action_the(ctx, seconds, name):
335
+ seconds = float(seconds)
336
+ retry(base_action_the, wait_up_to_s=seconds)(ctx, thing, name)
337
+
338
+ if with_nth:
339
+
340
+ @step(f'I immediately {action} the "{{nth:nth}}" {thing} "{{name}}"')
341
+ def immediately_action_the_nth(ctx, nth, name):
342
+ base_action_the(ctx, thing, name, index=nth)
343
+
344
+ @step(f'I {action} the "{{nth:nth}}" {thing} "{{name}}"')
345
+ def action_the_nth(ctx, nth, name):
346
+ retry(
347
+ base_action_the,
348
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
349
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
350
+ )(ctx, thing, name, index=nth)
351
+
352
+ @step(
353
+ f'I immediately {action} the "{{nth:nth}}" {thing} "{{name}}" if it exists'
354
+ )
355
+ def immediately_action_the_nth_if_it_exists(ctx, nth, name):
356
+ base_action_the(ctx, thing, name, index=nth, must_exist=False)
357
+
358
+ @step(f'I {action} the "{{nth:nth}}" {thing} "{{name}}" if it exists')
359
+ def action_the_nth_if_it_exists(ctx, nth, name):
360
+ retry(
361
+ base_action_the,
362
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
363
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
364
+ )(ctx, thing, name, index=nth, must_exist=False)
365
+
366
+ @step(f'I wait to {action} the "{{nth:nth}}" {thing} "{{name}}"')
367
+ def wait_to_action_the_nth(ctx, nth, name):
368
+ retry(base_action_the)(ctx, thing, name, index=nth)
369
+
370
+ @step(
371
+ f'I wait up to "{{seconds}}" seconds to {action} the "{{nth:nth}}" {thing} "{{name}}"'
372
+ )
373
+ def wait_up_to_action_the_nth(ctx, seconds, nth, name):
374
+ seconds = float(seconds)
375
+ retry(base_action_the, wait_up_to_s=seconds)(
376
+ ctx, thing, name, index=nth
377
+ )
378
+
379
+
380
+ def define_ensure_state_on_thing_with_name_steps(
381
+ thing, state, find_func, state_func, action_func, with_nth=False
382
+ ):
383
+ """
384
+ defines steps with with the following signatures:
385
+
386
+ I ensure the {thing} "{name}" is {state}
387
+ I wait to ensure the {thing} "{name}" is {state}
388
+ I wait up to "{seconds}" seconds to ensure the {thing} "{name}" is {state}
389
+
390
+ when with_nth=True we also define:
391
+
392
+ I ensure the "{nth}" {thing} "{name}" is {state}
393
+ I wait to ensure the "{nth}" {thing} "{name}" is {state}
394
+ I wait up to "{seconds}" seconds to ensure the "{nth}" {thing} "{name}" is {state}
395
+
396
+ parameters:
397
+ thing(string): name of the thing we're creating the steps for such
398
+ as button, dialog, etc.
399
+ state(stirng): the name of the state we want to ensure, such as:
400
+ filled, checked, empty, not empty, etc.
401
+ find_func(function): function that returns the desired element:
402
+
403
+ def find_func(ctx, name, index=):
404
+ '''
405
+ ctx(object): behave context object
406
+ name(string): name of the thing to find
407
+ index(int): when there are multiple elements
408
+ with the same name and you've
409
+ specified with_nth=True
410
+ '''
411
+ state_func(function): function that returns True if the element is in
412
+ the desired state and False otherwise:
413
+
414
+ def state_func(element):
415
+ '''
416
+ element(object): the element found
417
+ '''
418
+ action_func(function): action that will set the desired state on the
419
+ element if state_func returns False:
420
+ def action_func(ctx, element):
421
+ '''
422
+ ctx(object): behave context object
423
+ element(object): the element found
424
+ '''
425
+ with_nth(bool): when set to True we'll define the expanded set of
426
+ "nth" steps. default: False
427
+ """
428
+
429
+ # undecorated def for reference below
430
+ def base_ensure_the(ctx, thing, name, index=0):
431
+ prefix = nth_to_ordinal(index)
432
+ element = find_func(ctx, name, index=index)
433
+
434
+ if element is None:
435
+ raise RuntimeError(f'unable to find the {prefix}{thing} "{name}"')
436
+
437
+ if not state_func(element):
438
+ action_func(ctx, element)
439
+
440
+ @step(f'I immediately ensure the {thing} "{{name}}" is {state}')
441
+ def immediately_ensure_the(ctx, name, state):
442
+ base_ensure_the(ctx, thing, name)
443
+
444
+ @step(f'I ensure the {thing} "{{name}}" is {state}')
445
+ def ensure_the(ctx, name):
446
+ retry(
447
+ base_ensure_the,
448
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
449
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
450
+ )(ctx, thing, name)
451
+
452
+ @step(f'I wait to ensure the {thing} "{{name}}" is {state}')
453
+ def wait_to_ensure_the(ctx, name):
454
+ retry(base_ensure_the)(ctx, thing, name)
455
+
456
+ @step(
457
+ f'I wait up to "{{seconds}}" seconds to ensure the {thing} "{{name}}" is {state}'
458
+ )
459
+ def wait_up_to_seconds_to_ensure_the(ctx, seconds, name):
460
+ seconds = float(seconds)
461
+ retry(base_ensure_the, wait_up_to_s=seconds)(ctx, thing, name)
462
+
463
+ if with_nth:
464
+
465
+ @step(
466
+ f'I immediately ensure the "{{nth:nth}}" {thing} "{{name}}" is {state}'
467
+ )
468
+ def immediately_ensure_the_nth(ctx, nth, name):
469
+ base_ensure_the(ctx, thing, name, index=nth)
470
+
471
+ @step(f'I ensure the "{{nth:nth}}" {thing} "{{name}}"')
472
+ def ensure_the_nth(ctx, nth, name):
473
+ retry(
474
+ base_ensure_the,
475
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
476
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
477
+ )(ctx, thing, name, index=nth)
478
+
479
+ @step(
480
+ f'I wait to ensure the "{{nth:nth}}" {thing} "{{name}}" is {state}'
481
+ )
482
+ def wait_to_ensure_the_nth(ctx, nth, name):
483
+ retry(base_ensure_the)(ctx, thing, name, index=nth)
484
+
485
+ @step(
486
+ f'I wait up to "{{seconds}}" seconds to ensure the "{{nth:nth}}" {thing} "{{name}}" is {state}'
487
+ )
488
+ def wait_up_to_ensure_the_nth(ctx, seconds, nth, name):
489
+ seconds = float(seconds)
490
+ retry(base_ensure_the, wait_up_to_s=seconds)(
491
+ ctx, thing, name, index=nth
492
+ )
493
+
494
+
495
+ def define_thing_with_name_in_state_steps(
496
+ thing, state, find_func, is_in_state_func, with_nth=False
497
+ ):
498
+ """
499
+ defines steps with with the following signatures:
500
+
501
+ I should immediately see the {thing} "{name}" is {state_name}
502
+ I should see the {thing} "{name}" is {state_name}
503
+ I wait to see the {thing} "{name}" is {state_name}
504
+ I wait up to "{seconds}" seconds to see the {thing} "{name}" is {state_name}
505
+
506
+ when with_nth=True we also define:
507
+
508
+ I should immediately see the "{nth}" {thing} "{name}" is {state_name}
509
+ I should see the "{nth}" {thing} "{name}" is {state_name}
510
+ I wait to see the "{nth}" {thing} "{name}" is {state_name}
511
+ I wait up to "{seconds}" seconds to see the "{nth}" {thing} "{name}" is {state_name}
512
+
513
+ parameters:
514
+ thing(string): name of the thing we're creating the steps for such
515
+ as button, dialog, etc.
516
+ state(stirng): the name of the state being verified, such as:
517
+ selected, checked, disabled, etc.
518
+ find_func(function): function that returns the desired element:
519
+
520
+ def find_func(ctx, name, index=):
521
+ '''
522
+ ctx(object): behave context object
523
+ name(string): name of the thing to find
524
+ index(int): when there are multiple elements
525
+ with the same name and you've
526
+ specified with_nth=True
527
+ '''
528
+ is_int_state_func(function): function that verifies the element is in
529
+ the desired state:
530
+
531
+ def is_in_state_func(element):
532
+ '''
533
+ element(object): the element found
534
+ returns(bool): returns True if in
535
+ expected state
536
+ '''
537
+ with_nth(bool): when set to True we'll define the expanded set of
538
+ "nth" steps. default: False
539
+
540
+ """
541
+
542
+ # undecorated def for reference below
543
+ def base_should_see_the_in_state(ctx, thing, name, index=0):
544
+ prefix = nth_to_ordinal(index)
545
+ element = find_func(ctx, name, index=index)
546
+
547
+ if element is None:
548
+ raise RuntimeError(f'unable to find the {prefix}{thing} "{name}"')
549
+
550
+ if not is_in_state_func(element):
551
+ raise RuntimeError(f'{thing} "{name}" is not {state}')
552
+ logger.debug(f'{thing} {name} was in desired state "{state}"')
553
+
554
+ @step(f'I should immediately see the {thing} "{{name}}" is {state}')
555
+ def should_immediately_see_the_in_state(ctx, name, index=0):
556
+ base_should_see_the_in_state(ctx, thing, name, index=0)
557
+
558
+ @step(f'I should see the {thing} "{{name}}" is {state}')
559
+ def should_see_the_in_state(ctx, name):
560
+ retry(
561
+ base_should_see_the_in_state,
562
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
563
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
564
+ )(ctx, thing, name)
565
+
566
+ @step(f'I wait to see the {thing} "{{name}}" is {state}')
567
+ def wait_to_see_the_in_state(ctx, name):
568
+ retry(base_should_see_the_in_state)(ctx, thing, name)
569
+
570
+ @step(
571
+ f'I wait up to "{{seconds}}" seconds to see the {thing} "{{name}}" is {state}'
572
+ )
573
+ def wait_up_to_seconds_to_see_the_in_state(ctx, seconds, name):
574
+ seconds = float(seconds)
575
+ retry(base_should_see_the_in_state, wait_up_to_s=seconds)(
576
+ ctx, thing, name
577
+ )
578
+
579
+ if with_nth:
580
+
581
+ @step(
582
+ f'I should immediately see the "{{nth:nth}}" {thing} "{{name}}" is {state}'
583
+ )
584
+ def base_should_see_the_nth_in_state(ctx, nth, name):
585
+ base_should_see_the_in_state(ctx, thing, name, index=nth)
586
+
587
+ @step(f'I should see the "{{nth:nth}}" {thing} "{{name}}" is {state}')
588
+ def should_see_the_nth_in_state(ctx, nth, name):
589
+ retry(
590
+ base_should_see_the_in_state,
591
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
592
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
593
+ )(ctx, thing, name, index=nth)
594
+
595
+ @step(f'I wait to see the "{{nth:nth}}" {thing} "{{name}}" is {state}')
596
+ def wait_to_see_the_nth_in_state(ctx, nth, name):
597
+ retry(base_should_see_the_in_state)(ctx, thing, name, index=nth)
598
+
599
+ @step(
600
+ f'I wait up to "{{seconds}}" seconds to see the "{{nth:nth}}" {thing} "{{name}}" is {state}'
601
+ )
602
+ def wait_up_to_seconds_to_see_the_nth_in_state(
603
+ ctx, seconds, nth, name
604
+ ):
605
+ seconds = float(seconds)
606
+ retry(base_should_see_the_in_state, wait_up_to_s=seconds)(
607
+ ctx, thing, name, index=nth
608
+ )
609
+
610
+
611
+ def define_run_steps_if_I_can_see_element_with_name_steps(thing, find_func):
612
+ """
613
+ defines steps with with the following signatures:
614
+
615
+ I run the following steps if I can immediately see the {thing} "{name}"
616
+ I run the following steps if I can see the {thing} "{name}"
617
+
618
+ I run the following steps if I can immediately not see the {thing} "{name}"
619
+ I run the following steps if I can not see the {thing} "{name}"
620
+
621
+ parameters:
622
+ thing(string): name of the thing we're creating the steps for such
623
+ as button, dialog, etc.
624
+ find_func(function): function that returns the element that matches the
625
+ name provided and is visible
626
+
627
+ def find_func(ctx, name):
628
+ '''
629
+ ctx(object): behave context object
630
+ name(string): name of the thing to find
631
+ '''
632
+ """
633
+
634
+ # undecorated def for reference below
635
+ def base_run_if_visibile(ctx, name):
636
+ element = find_func(ctx, name)
637
+
638
+ if element is not None:
639
+ run_steps(ctx, ctx.text)
640
+
641
+ @step(
642
+ f'I run the following steps if I can immediately see the {thing} "{{name}}"'
643
+ )
644
+ def run_if_immediately_visibile(ctx, name):
645
+ base_run_if_visibile(ctx, name)
646
+
647
+ @step(f'I run the following steps if I can see the {thing} "{{name}}"')
648
+ def run_if_visibile(ctx, name):
649
+ retry(
650
+ base_run_if_visibile,
651
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
652
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
653
+ )(ctx, name)
654
+
655
+ def base_run_if_not_visibile(ctx, name):
656
+ element = find_func(ctx, name)
657
+
658
+ if element is None:
659
+ run_steps(ctx, ctx.text)
660
+
661
+ @step(
662
+ f'I immediately run the following steps if I can not see the {thing} "{{name}}"'
663
+ )
664
+ def immediately_run_if_not_visibile(ctx, name):
665
+ base_run_if_not_visibile(ctx, name)
666
+
667
+ @step(f'I run the following steps if I can not see the {thing} "{{name}}"')
668
+ def run_if_not_visibile(ctx, name):
669
+ retry(
670
+ base_run_if_not_visibile,
671
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
672
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
673
+ )(ctx, name)
674
+
675
+
676
+ def define_two_thing_interaction_steps(
677
+ action: str,
678
+ action_func,
679
+ thing_1,
680
+ thing_1_find_func,
681
+ preposition: str,
682
+ thing_2,
683
+ thing_2_find_func,
684
+ with_nth=False,
685
+ ):
686
+ """
687
+ defines steps with with the following signatures:
688
+ I {action} the {thing_1} "{name_1}" {preposition} the {thing_2} "{name_2}"
689
+ I wait to {action} the {thing_1} "{name_1}" {preposition} the {thing_2} "{name_2}"
690
+ I wait up to "{seconds}" seconds to {action} the {thing_1} "{name_1}" {preposition} the {thing_2} "{name_2}"
691
+ ...
692
+ I {action} the {thing_1} "{name_1}" {preposition} the {thing_2} "{name_2}" if they both exist
693
+
694
+
695
+ when with_nth=True we also define:
696
+
697
+ I {action} the "{nth_1}" {thing_1} "{name_1}" {preposition} the "{nth_2}" {thing_2} "{name_2}"
698
+ I wait to {action} the "{nth_1}" {thing_1} "{name_1}" {preposition} the "{nth_2}" {thing_2} "{name_2}"
699
+ I wait up to "{seconds}" seconds to {action} the "{nth_1}" {thing_1} "{name_1}" {preposition} the "{nth_2}" {thing_2} "{name_2}"
700
+ ...
701
+ I {action} the "{nth_1}" {thing_1} "{name_1}" {preposition} the "{nth_2}" {thing_2} "{name_2}" if they both exist
702
+
703
+ parameters:
704
+ action(string): the name of the action being performed, such as:
705
+ click, disable, etc.
706
+ action_func(function): function that performs the desired action:
707
+
708
+ def action_func(ctx, element, ):
709
+ '''
710
+ ctx(object): behave context object
711
+ element(object): the element found
712
+ '''
713
+ thing_1(string): name of the thing we're creating the steps for such
714
+ as button, dialog, etc.
715
+ thing_1_find_func(function): function that returns the desired element:
716
+
717
+ def thing_1_find_func(ctx, name_1, index_1=):
718
+ '''
719
+ ctx(object): behave context object
720
+ name_1(string):name of the thing to find
721
+ index_1(int): when there are multiple elements
722
+ with the same name and you've
723
+ specified with_nth=True
724
+ '''
725
+ preposition(string): preposition to help with readability as there are
726
+ many different prepositions that would be valid for
727
+ a desired action
728
+ thing_2(string): name of the thing that is being interacted with
729
+ from the defined action
730
+ thing_2_find_func(function): function that returns the interacted element:
731
+
732
+ def thing_2_find_func(ctx, name_2, index_2=):
733
+ '''
734
+ ctx(object): behave context object
735
+ name_2(string): name of the thing to find
736
+ index_1(int): when there are multiple elements
737
+ with the same name and you've
738
+ specified with_nth=True
739
+ '''
740
+ with_nth(bool): when set to True we'll define the expanded set of
741
+ "nth" steps. default: False
742
+ """
743
+
744
+ # undecorated def for reference below
745
+ def base_action_the(
746
+ ctx,
747
+ thing_1,
748
+ name_1,
749
+ thing_2,
750
+ name_2,
751
+ index_1=0,
752
+ index_2=0,
753
+ ):
754
+ prefix_1 = nth_to_ordinal(index_1)
755
+ prefix_2 = nth_to_ordinal(index_2)
756
+
757
+ element_1 = thing_1_find_func(ctx, name_1, index_1)
758
+ element_2 = thing_2_find_func(ctx, name_2, index_2)
759
+
760
+ if element_1 is None or element_2 is None:
761
+ error_message = []
762
+ if element_1 is None:
763
+ error_message.append(
764
+ f'Unable to find the {prefix_1}{thing_1} "{name_1}"'
765
+ )
766
+ if element_2 is None:
767
+ error_message.append(
768
+ f'Unable to find the {prefix_2}{thing_2} "{name_2}"'
769
+ )
770
+
771
+ raise RuntimeError(", ".join(error_message))
772
+
773
+ else:
774
+ action_func(ctx, element_1, element_2)
775
+ logger.debug(
776
+ f'Successfully executed {action} {prefix_1}{thing_1} "{name_1}" {preposition} {prefix_2}{thing_2} "{name_2}"'
777
+ )
778
+
779
+ @step(
780
+ f'I immediately {action} the {thing_1} "{{name_1}}" {preposition} the {thing_2} "{{name_2}}"'
781
+ )
782
+ def immediately_action_the(ctx, name_1, name_2):
783
+ base_action_the(ctx, thing_1, name_1, thing_2, name_2)
784
+
785
+ @step(
786
+ f'I {action} the {thing_1} "{{name_1}}" {preposition} the {thing_2} "{{name_2}}"'
787
+ )
788
+ def action_the(ctx, name_1, name_2):
789
+ retry(
790
+ base_action_the,
791
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
792
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
793
+ )(ctx, thing_1, name_1, thing_2, name_2)
794
+
795
+ @step(
796
+ f'I wait to {action} the {thing_1} "{{name_1}}" {preposition} the {thing_2} "{{name_2}}"'
797
+ )
798
+ def wait_to_action_the(ctx, name_1, name_2):
799
+ retry(base_action_the)(ctx, thing_1, name_1, thing_2, name_2)
800
+
801
+ @step(
802
+ f'I wait up to "{{seconds}}" seconds to {action} the {thing_1} "{{name_1}}" {preposition} the {thing_2} "{{name_2}}"'
803
+ )
804
+ def wait_up_to_seconds_to_action_the(ctx, seconds, name_1, name_2):
805
+ seconds = float(seconds)
806
+ retry(base_action_the, wait_up_to_s=seconds)(
807
+ ctx, thing_1, name_1, thing_2, name_2
808
+ )
809
+
810
+ if with_nth:
811
+
812
+ @step(
813
+ f'I immediately {action} the "{{nth_1:nth}}" {thing_1} "{{name_1}}" {preposition} the "{{nth_2:nth}}" {thing_2} "{{name_2}}"'
814
+ )
815
+ def immediately_action_the_nth_i_nth(
816
+ ctx, nth_1, name_1, nth_2, name_2
817
+ ):
818
+ base_action_the(
819
+ ctx,
820
+ thing_1,
821
+ name_1,
822
+ thing_2,
823
+ name_2,
824
+ index_1=nth_1,
825
+ index_2=nth_2,
826
+ )
827
+
828
+ @step(
829
+ f'I {action} the "{{nth_1:nth}}" {thing_1} "{{name_1}}" {preposition} the "{{nth_2:nth}}" {thing_2} "{{name_2}}"'
830
+ )
831
+ def action_the_nth_i_nth(ctx, nth_1, name_1, nth_2, name_2):
832
+ retry(
833
+ base_action_the,
834
+ retry_after_s=float(CONFIG["CUCU_SHORT_UI_RETRY_AFTER_S"]),
835
+ wait_up_to_s=float(CONFIG["CUCU_SHORT_UI_WAIT_TIMEOUT_S"]),
836
+ )(
837
+ ctx,
838
+ thing_1,
839
+ name_1,
840
+ thing_2,
841
+ name_2,
842
+ index_1=nth_1,
843
+ index_2=nth_2,
844
+ )
845
+
846
+ @step(
847
+ f'I wait to {action} the "{{nth_1:nth}}" {thing_1} "{{name_1}}" {preposition} the "{{nth_2:nth}}" {thing_2} "{{name_2}}"'
848
+ )
849
+ def wait_to_action_the_nth_ith(ctx, nth_1, name_1, nth_2, name_2):
850
+ retry(base_action_the)(
851
+ ctx,
852
+ thing_1,
853
+ name_1,
854
+ thing_2,
855
+ name_2,
856
+ index_1=nth_1,
857
+ index_2=nth_2,
858
+ )
859
+
860
+ @step(
861
+ f'I wait up to "{{seconds}}" seconds to {action} the "{{nth_1:nth}}" {thing_1} "{{name_1}}" {preposition} the "{{nth_2:nth}}" {thing_2} "{{name_2}}"'
862
+ )
863
+ def wait_up_to_action_the_nth_i_nth(
864
+ ctx, seconds, nth_1, name_1, nth_2, name_2
865
+ ):
866
+ seconds = float(seconds)
867
+ retry(base_action_the, wait_up_to_s=seconds)(
868
+ ctx,
869
+ thing_1,
870
+ name_1,
871
+ thing_2,
872
+ name_2,
873
+ index_1=nth_1,
874
+ index_2=nth_2,
875
+ )