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,109 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>{{ title }}</title>
|
|
5
|
+
<meta charset="utf-8"/>
|
|
6
|
+
<link rel="shortcut icon" href="{{ dir_depth }}favicon.png">
|
|
7
|
+
<link href="{{ dir_depth }}external/bootstrap.min.css" rel="stylesheet"></link>
|
|
8
|
+
|
|
9
|
+
<script src="{{ dir_depth }}external/jquery-3.5.1.min.js"></script>
|
|
10
|
+
<script src="{{ dir_depth }}external/popper.min.js"></script>
|
|
11
|
+
<script src="{{ dir_depth }}external/bootstrap.min.js"></script>
|
|
12
|
+
|
|
13
|
+
<!-- fancy tables with search and sorting -->
|
|
14
|
+
<link href="{{ dir_depth }}external/dataTables.bootstrap.min.css" rel="stylesheet"></link>
|
|
15
|
+
<script src="{{ dir_depth }}external/jquery.dataTables.min.js" language="javascript"></script>
|
|
16
|
+
<script src="{{ dir_depth}}external/dataTables.bootstrap.min.js" language="javascript"></script>
|
|
17
|
+
<style>
|
|
18
|
+
.remove-table-hover tr:hover{
|
|
19
|
+
background-color: unset !important;
|
|
20
|
+
}
|
|
21
|
+
@link-color: @brand-primary;
|
|
22
|
+
@link-hover-color: darken(@link-color, 15%);
|
|
23
|
+
|
|
24
|
+
a {
|
|
25
|
+
color: @link-color;
|
|
26
|
+
text-decoration: none;
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
color: @link-hover-color;
|
|
30
|
+
text-decoration: underline;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.status-passed {
|
|
35
|
+
display: inline;
|
|
36
|
+
color: green;
|
|
37
|
+
}
|
|
38
|
+
.status-failed {
|
|
39
|
+
display: inline;
|
|
40
|
+
color: red;
|
|
41
|
+
}
|
|
42
|
+
.status-errored {
|
|
43
|
+
display: inline;
|
|
44
|
+
color: red;
|
|
45
|
+
}
|
|
46
|
+
.status-skipped {
|
|
47
|
+
display: inline;
|
|
48
|
+
color: blue;
|
|
49
|
+
}
|
|
50
|
+
.status-untested {
|
|
51
|
+
display: inline;
|
|
52
|
+
color: gray;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
55
|
+
<script>
|
|
56
|
+
function setupReportTables(defaultOrder, columns) {
|
|
57
|
+
$(document).ready(function () {
|
|
58
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
59
|
+
var order = defaultOrder;
|
|
60
|
+
|
|
61
|
+
if (urlParams.get('tableOrder')) {
|
|
62
|
+
const parts = urlParams.get('tableOrder').split(':');
|
|
63
|
+
order = [[parseInt(parts[0]), parts[1]]];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
$('.datatable').each(function(){
|
|
67
|
+
const table = $(this).DataTable({
|
|
68
|
+
info: false,
|
|
69
|
+
paging: false,
|
|
70
|
+
order: order,
|
|
71
|
+
columns: columns,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (urlParams.get('search')) {
|
|
75
|
+
const input = urlParams.get('search');
|
|
76
|
+
table.search(input).draw();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// catch the various sorting changes and put it int he current URL
|
|
80
|
+
$(this).on('order.dt', function() {
|
|
81
|
+
const params = new URLSearchParams(window.location.search)
|
|
82
|
+
var currentOrder = table.order();
|
|
83
|
+
params.set('tableOrder', currentOrder[0][0] + ':' + currentOrder[0][1]);
|
|
84
|
+
|
|
85
|
+
var url = window.location.pathname + '?' + params.toString() + window.location.hash;
|
|
86
|
+
history.pushState(null, "", url);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// catch the search filtering and append that to the URl aswell
|
|
90
|
+
$(this).on('search.dt', function() {
|
|
91
|
+
const params = new URLSearchParams(window.location.search)
|
|
92
|
+
const search = table.search();
|
|
93
|
+
params.set('search', search);
|
|
94
|
+
|
|
95
|
+
var url = window.location.pathname + '?' + params.toString() + window.location.hash;
|
|
96
|
+
history.pushState(null, "", url);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
</script>
|
|
102
|
+
</head>
|
|
103
|
+
<body>
|
|
104
|
+
<div class="container">
|
|
105
|
+
{% block nav %}{% endblock %}
|
|
106
|
+
{% block content %}{% endblock %}
|
|
107
|
+
</div>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
{% extends "layout.html" %}
|
|
2
|
+
{% block nav %}
|
|
3
|
+
<nav class="navbar navbar-expand-sm sticky-top navbar-light bg-light">
|
|
4
|
+
<div class="container-fluid">
|
|
5
|
+
<a class="navbar-brand" href="#">Scenario HTML Test Report</a>
|
|
6
|
+
|
|
7
|
+
<div class="collapse navbar-collapse">
|
|
8
|
+
<ul class="navbar-nav mr-auto mt-2 mt-lg-0">
|
|
9
|
+
<li class="nav-item active">
|
|
10
|
+
<a class="nav-link" href="../../flat.html" title="go to Flat report">Flat</a>
|
|
11
|
+
</li>
|
|
12
|
+
<li class="nav-item active">
|
|
13
|
+
<a class="nav-link" href="../../index.html" title="go to Index report">Index</a>
|
|
14
|
+
</li>
|
|
15
|
+
<li class="nav-item active">
|
|
16
|
+
<a class="nav-link" href="../../{{ feature['name'] }}.html" title="go to feature report">Feature</a>
|
|
17
|
+
</li>
|
|
18
|
+
<li class="nav-item dropdown">
|
|
19
|
+
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="various log files">
|
|
20
|
+
Logs
|
|
21
|
+
</a>
|
|
22
|
+
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
|
23
|
+
{% for log_file in scenario['logs'] %}
|
|
24
|
+
<a class="dropdown-item" href="{{ log_file['filepath'] }}">{{ log_file['name'] }}</a>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</div>
|
|
27
|
+
</li>
|
|
28
|
+
<li class="nav-item active">
|
|
29
|
+
<button class="btn btn-md btn-light" title="show images" type="button" data-toggle="collapse" data-target=".multi-collapse" aria-expanded="false" aria-controls=".multi-collapse">🖼</button>
|
|
30
|
+
</li>
|
|
31
|
+
<li class="nav-item active">
|
|
32
|
+
<button class="btn btn-md btn-light" title="scroll to top" onclick="window.scrollTo({ top: 0, behavior: 'smooth' });" class="nav-link">⬆️</button>
|
|
33
|
+
</li>
|
|
34
|
+
<li class="nav-item active">
|
|
35
|
+
<button class="btn btn-md btn-light" title="scroll to bottom" onclick="window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });" class="nav-link">⬇️ </button>
|
|
36
|
+
</li>
|
|
37
|
+
<li class="align-middle">
|
|
38
|
+
<div style="padding: 8px">Started {{ scenario["started_at"] }} for {{ scenario['duration'] }}</div>
|
|
39
|
+
</li>
|
|
40
|
+
</ul>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</nav>
|
|
44
|
+
{% endblock %}
|
|
45
|
+
{% block content %}
|
|
46
|
+
<style>
|
|
47
|
+
.anchor {
|
|
48
|
+
display: inline;
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
font-size: 8px;
|
|
52
|
+
font-style: bold;
|
|
53
|
+
text-decoration: none;
|
|
54
|
+
visibility: hidden;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.anchor-toggle:hover > .anchor {
|
|
58
|
+
display: inline;
|
|
59
|
+
visibility: visible;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
62
|
+
<table class="table table-borderless container row">
|
|
63
|
+
|
|
64
|
+
<tr class="row">
|
|
65
|
+
<td style="min-width: 0;" class="col-10 text-truncate">
|
|
66
|
+
<span style="display: inline; color: maroon;">Feature: </span><span title="{{ escape(feature['name']) }}">{{ escape(feature['name']) }}</span><br/>
|
|
67
|
+
{% if feature['tags'] %}
|
|
68
|
+
<span style="display: inline; color: darkslateblue">{{ feature['tags'] }}</span><br/>
|
|
69
|
+
{% endif %}
|
|
70
|
+
<span style="display: inline; color: maroon;">Scenario:</span> <span title="{{ escape(scenario['name']) }}">{{ escape(scenario['name']) }}</span><br/>
|
|
71
|
+
<span style="display: inline; color: darkslateblue">{{ scenario['tags'] }}</span>
|
|
72
|
+
{% if scenario['sub_headers'] %}
|
|
73
|
+
<br/>
|
|
74
|
+
{{ scenario['sub_headers'] }}
|
|
75
|
+
{% endif %}
|
|
76
|
+
</td>
|
|
77
|
+
<td style="text-align: right; margin-top: auto;" class="col-2">
|
|
78
|
+
<pre style="display: inline; color: gray;">Offset and Duration (s)</pre>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
{% for step in steps %}
|
|
82
|
+
{% set step_name = step['keyword'] + ' ' + escape(step['name']) %}
|
|
83
|
+
{% if step['substep'] %}
|
|
84
|
+
{% set step_prefix = " ⤷" %}
|
|
85
|
+
{% else %}
|
|
86
|
+
{% set step_prefix = "" %}
|
|
87
|
+
{% endif %}
|
|
88
|
+
{% if step['result'] is defined %}
|
|
89
|
+
{% set step_status = step['result']['status'] %}
|
|
90
|
+
{% if step['result']['status'] == 'failed' or step['result']['status'] == 'passed' %}
|
|
91
|
+
{% set step_timing = "{} for {:.3f}s".format(step["result"]["time_offset"].strftime("%H:%M:%S"), step["result"]["duration"]) %}
|
|
92
|
+
{% set step_start = step["result"]["timestamp"] %}
|
|
93
|
+
{% endif %}
|
|
94
|
+
{% else %}
|
|
95
|
+
{% set step_status = 'untested' %}
|
|
96
|
+
{% set step_timing = "" %}
|
|
97
|
+
{% set step_start = "" %}
|
|
98
|
+
{% endif %}
|
|
99
|
+
{% set step_keyword = step_prefix + step['keyword'].rjust(6, ' ') %}
|
|
100
|
+
|
|
101
|
+
{% if step['heading_level'] %}
|
|
102
|
+
<tr class="row">
|
|
103
|
+
<td style="min-width: 0;" class="anchor-toggle text-truncate col-12" colspan="2" data-toggle="collapse" href="#collapsable-row-{{ loop.index }}" role="button" aria-expanded="false" aria-controls="collapsable-row-{{ loop.index }}">
|
|
104
|
+
<a class="anchor" id="step_{{ loop.index}}" href="#step_{{ loop.index }}">🔗</a>
|
|
105
|
+
<{{ step['heading_level'] }} style="display: contents;" title="{{ escape(step['name']) }}"> {{ escape(step['name']) }}</{{ step['heading_level'] }}>
|
|
106
|
+
</td>
|
|
107
|
+
</tr>
|
|
108
|
+
|
|
109
|
+
{% else %}
|
|
110
|
+
<tr class="row">
|
|
111
|
+
<td style="min-width: 0;" class="anchor-toggle text-truncate col-10" data-toggle="collapse" href="#collapsable-row-{{ loop.index }}" role="button" aria-expanded="false" aria-controls="collapsable-row-{{ loop.index }}">
|
|
112
|
+
<a class="anchor" id="step_{{ loop.index}}" href="#step_{{ loop.index }}">🔗</a>
|
|
113
|
+
<pre class="status-{{ step_status }}">{{ step_keyword }}</pre><span style="display: contents;" title="{{ escape(step['name']) }}"> {{ escape(step['name']) }}</span>
|
|
114
|
+
</td>
|
|
115
|
+
<td style="min-width: 0; text-align: right;" class="col-2"><pre style="display: inline; color: gray;" title="{{ step_start }}">{{ step_timing }}</pre></td>
|
|
116
|
+
</tr>
|
|
117
|
+
{% endif %}
|
|
118
|
+
|
|
119
|
+
{% if step['text'] or step['table'] %}
|
|
120
|
+
<tr class="row"><td style="min-width: 0;" class="col-12" colspan="2">
|
|
121
|
+
{% if step['text'] %}
|
|
122
|
+
<pre style="margin: 0;">{{ escape(step['text']) }}</pre>
|
|
123
|
+
{% endif %}
|
|
124
|
+
{% if step['table'] %}
|
|
125
|
+
<pre style="margin: 0;">{{ escape(step['table']) }}</pre>
|
|
126
|
+
{% endif %}
|
|
127
|
+
</td></tr>
|
|
128
|
+
{% endif %}
|
|
129
|
+
|
|
130
|
+
{% if step['result'] is defined %}
|
|
131
|
+
<tr class="row"><td style="min-width: 0;" class="col-12 collapse multi-collapse" id="collapsable-row-{{ loop.index }}" colspan="2">
|
|
132
|
+
|
|
133
|
+
{% if step['result']['stdout'] %}
|
|
134
|
+
<pre style="color: darkgray; margin: 0;">{{ escape("\n".join(step['result']['stdout'])) }}</pre>
|
|
135
|
+
{% endif %}
|
|
136
|
+
|
|
137
|
+
{% if step['images'] is defined %}
|
|
138
|
+
{% if step['result']['stdout'] %}
|
|
139
|
+
<br/>
|
|
140
|
+
{% endif %}
|
|
141
|
+
{% for image in step['images'] %}
|
|
142
|
+
<img class="mx-auto d-block img-fluid shadow bg-white rounded" style="margin-bottom:15px" alt='{{ image["label"] }}' title='{{ image["label"] }}' src='{{ image["src"] }}'></img>
|
|
143
|
+
{% endfor %}
|
|
144
|
+
{% endif %}
|
|
145
|
+
|
|
146
|
+
{% if step['result']['error_message'] is defined %}
|
|
147
|
+
{% if step['image'] is defined %}
|
|
148
|
+
<br/>
|
|
149
|
+
{% endif %}
|
|
150
|
+
<pre style="color: gray; margin: 0">{{ escape("\n".join(step['result']['error_message'])) }}</pre>
|
|
151
|
+
{% endif %}
|
|
152
|
+
</td></tr>
|
|
153
|
+
{% endif %}
|
|
154
|
+
|
|
155
|
+
{% endfor %}
|
|
156
|
+
</table>
|
|
157
|
+
<script>
|
|
158
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
159
|
+
var expandables = [];
|
|
160
|
+
var expandable_ids = urlParams.get('expand');
|
|
161
|
+
if (expandable_ids === 'all') {
|
|
162
|
+
expand = $('.collapse');
|
|
163
|
+
} else if (expandable_ids) {
|
|
164
|
+
var ids = expandable_ids.split(',');
|
|
165
|
+
for (var index = 0; index < ids.length; index++) {
|
|
166
|
+
expandables.push(document.getElementById(ids[index]));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (var index=0; index < expandables.length; index++) {
|
|
170
|
+
$(expandables[index]).removeClass('hide')
|
|
171
|
+
$(expandables[index]).addClass('show')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// necessary to make sure we jump to the anchored step as the page loads
|
|
175
|
+
// and then we take the URL parameter and figure out which rows to
|
|
176
|
+
// expand and only then would it make sense to adjust for any anchors
|
|
177
|
+
// mentioned in the URL
|
|
178
|
+
$('document').ready(function() {
|
|
179
|
+
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
|
180
|
+
var observer = new MutationObserver(function(mutations, observer) {
|
|
181
|
+
const params = new URLSearchParams(window.location.search)
|
|
182
|
+
|
|
183
|
+
// update any open/closed rows
|
|
184
|
+
var expanded = $('.collapse.show');
|
|
185
|
+
var expanded_ids = [];
|
|
186
|
+
for (var index=0; index < expanded.length; index++) {
|
|
187
|
+
expanded_ids.push(expanded[index].getAttribute('id'))
|
|
188
|
+
}
|
|
189
|
+
params.set('expand', expanded_ids.join(','));
|
|
190
|
+
|
|
191
|
+
var url = window.location.pathname + '?' + params.toString() + window.location.hash;
|
|
192
|
+
history.pushState(null, "", url);
|
|
193
|
+
});
|
|
194
|
+
observer.observe(document, {
|
|
195
|
+
subtree: true,
|
|
196
|
+
attributes: true
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
</script>
|
|
200
|
+
{% endblock %}
|
cucu/steps/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
# nopycln: file
|
|
3
|
+
import cucu.hooks
|
|
4
|
+
|
|
5
|
+
import cucu.steps.comment_steps
|
|
6
|
+
import cucu.steps.base_steps
|
|
7
|
+
|
|
8
|
+
import cucu.steps.browser_steps
|
|
9
|
+
import cucu.steps.button_steps
|
|
10
|
+
import cucu.steps.command_steps
|
|
11
|
+
import cucu.steps.checkbox_steps
|
|
12
|
+
import cucu.steps.draggable_steps
|
|
13
|
+
import cucu.steps.dropdown_steps
|
|
14
|
+
import cucu.steps.filesystem_steps
|
|
15
|
+
import cucu.steps.image_steps
|
|
16
|
+
import cucu.steps.input_steps
|
|
17
|
+
import cucu.steps.file_input_steps
|
|
18
|
+
import cucu.steps.link_steps
|
|
19
|
+
import cucu.steps.flow_control_steps
|
|
20
|
+
import cucu.steps.menuitem_steps
|
|
21
|
+
import cucu.steps.platform_steps
|
|
22
|
+
import cucu.steps.radio_steps
|
|
23
|
+
import cucu.steps.tab_steps
|
|
24
|
+
import cucu.steps.table_steps
|
|
25
|
+
import cucu.steps.text_steps
|
|
26
|
+
import cucu.steps.variable_steps
|
|
27
|
+
import cucu.steps.webserver_steps
|
cucu/steps/base_steps.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
import parse
|
|
7
|
+
from behave import register_type
|
|
8
|
+
from behave.model_describe import ModelPrinter
|
|
9
|
+
|
|
10
|
+
from cucu import logger, step
|
|
11
|
+
from cucu.ansi_parser import remove_ansi
|
|
12
|
+
from cucu.config import CONFIG
|
|
13
|
+
|
|
14
|
+
NTH_REGEX = r"(\d+)(nd|th|rd|st)"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@parse.with_pattern(NTH_REGEX)
|
|
18
|
+
def parse_nth(nth):
|
|
19
|
+
matcher = re.match(NTH_REGEX, nth)
|
|
20
|
+
|
|
21
|
+
if matcher is None:
|
|
22
|
+
raise Exception(f"nth expression {nth} is invalid")
|
|
23
|
+
|
|
24
|
+
number, _ = matcher.groups()
|
|
25
|
+
return int(number) - 1
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
register_type(nth=parse_nth)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_disabled(element):
|
|
32
|
+
"""
|
|
33
|
+
internal method to check an element is disabled
|
|
34
|
+
"""
|
|
35
|
+
return (
|
|
36
|
+
element.get_attribute("disabled")
|
|
37
|
+
or element.get_attribute("aria-disabled") == "true"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_not_disabled(element):
|
|
42
|
+
"""
|
|
43
|
+
internal method to check an element is not disabled
|
|
44
|
+
"""
|
|
45
|
+
return not is_disabled(element)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@step("I run a step that fails")
|
|
49
|
+
def this_step_fails(_):
|
|
50
|
+
raise Exception("failing on purpose")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@step('I sleep for "{value}" seconds')
|
|
54
|
+
def sleep(ctx, value):
|
|
55
|
+
time.sleep(int(value))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@step('I echo "{value}"')
|
|
59
|
+
def i_echo(ctx, value):
|
|
60
|
+
print(f"{value}\n")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@step("I echo the following")
|
|
64
|
+
def i_echo_the_following(ctx):
|
|
65
|
+
if ctx.text is not None:
|
|
66
|
+
print(f"{ctx.text}\n")
|
|
67
|
+
|
|
68
|
+
elif ctx.table is not None:
|
|
69
|
+
printer = ModelPrinter(sys.stdout)
|
|
70
|
+
printer.print_table(ctx.table)
|
|
71
|
+
print("")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@step('I log "{message}" at level "{level}"')
|
|
75
|
+
def i_log(_, message, level):
|
|
76
|
+
operator.methodcaller(level.lower(), message)(logger)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@step('I log the following at level "{level}"')
|
|
80
|
+
def i_log_following(ctx, level):
|
|
81
|
+
operator.methodcaller(level.lower(), ctx.text)(logger)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@step(
|
|
85
|
+
'I strip ansi codes from "{value}" and save to the variable "{variable}"'
|
|
86
|
+
)
|
|
87
|
+
def strip_ansi_codes_and_save(ctx, value, variable):
|
|
88
|
+
CONFIG[variable] = remove_ansi(value)
|