robotframework-testdoc 0.1.2__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 robotframework-testdoc might be problematic. Click here for more details.
- robotframework_testdoc-0.1.2.dist-info/METADATA +148 -0
- robotframework_testdoc-0.1.2.dist-info/RECORD +24 -0
- robotframework_testdoc-0.1.2.dist-info/WHEEL +5 -0
- robotframework_testdoc-0.1.2.dist-info/entry_points.txt +2 -0
- robotframework_testdoc-0.1.2.dist-info/licenses/LICENSE +201 -0
- robotframework_testdoc-0.1.2.dist-info/top_level.txt +1 -0
- testdoc/__init__.py +0 -0
- testdoc/__main__.py +3 -0
- testdoc/cli.py +91 -0
- testdoc/helper/cliargs.py +43 -0
- testdoc/helper/datetimeconverter.py +5 -0
- testdoc/helper/logger.py +9 -0
- testdoc/helper/pathconverter.py +50 -0
- testdoc/html/images/robotframework.svg +1 -0
- testdoc/html/templates/jinja_template_01.html +334 -0
- testdoc/html/themes/__init__.py +0 -0
- testdoc/html/themes/theme_config.py +27 -0
- testdoc/html/themes/themes.py +44 -0
- testdoc/html_rendering/render.py +31 -0
- testdoc/parser/modifier/sourceprefixmodifier.py +78 -0
- testdoc/parser/modifier/suitefilemodifier.py +115 -0
- testdoc/parser/testcaseparser.py +31 -0
- testdoc/parser/testsuiteparser.py +68 -0
- testdoc/testdoc.py +19 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
<!-- Jinja Makro fΓΌr rekursive Suite-Anzeige -->
|
|
2
|
+
{% macro render_suite(suite, index, first) %}
|
|
3
|
+
<div class="accordion-item">
|
|
4
|
+
<h2 class="accordion-header" id="heading{{ index }}">
|
|
5
|
+
|
|
6
|
+
{% if suite.is_folder %}
|
|
7
|
+
<button class="accordion-button {% if not first %}collapsed{% endif %}" type="button"
|
|
8
|
+
data-bs-toggle="collapse"
|
|
9
|
+
data-bs-target="#collapse{{ index }}"
|
|
10
|
+
aria-expanded="{% if first %}true{% else %}false{% endif %}"
|
|
11
|
+
aria-controls="collapse{{ index }}">
|
|
12
|
+
<span class="custom-chevron">
|
|
13
|
+
β―
|
|
14
|
+
</span>
|
|
15
|
+
π Suite Directory: <strong>{{ suite.name }}</strong>
|
|
16
|
+
</button>
|
|
17
|
+
{% else %}
|
|
18
|
+
<button class="accordion-button {% if not first %}collapsed{% endif %}" type="button"
|
|
19
|
+
data-bs-toggle="collapse"
|
|
20
|
+
data-bs-target="#collapse{{ index }}"
|
|
21
|
+
aria-expanded="{% if first %}true{% else %}false{% endif %}"
|
|
22
|
+
aria-controls="collapse{{ index }}"
|
|
23
|
+
style="border: 2px solid {{ colors.robot_icon }};">
|
|
24
|
+
<span class="custom-chevron">
|
|
25
|
+
β―
|
|
26
|
+
</span>
|
|
27
|
+
<svg class="svg-icon" width="20" height="20" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
28
|
+
<title>Robot Framework</title>
|
|
29
|
+
<path d="M4.9565 10.2246c0-1.8766 1.5257-3.4023 3.4-3.4023 1.8766 0 3.4024 1.5257 3.4024 3.4023 0 .6838-.5526 1.2364-1.2341 1.2364-.6818 0-1.2344-.5526-1.2344-1.2364 0-.513-.4185-.9296-.9338-.9296-.5129 0-.9317.4165-.9317.9296 0 .6838-.5523 1.2364-1.234 1.2364-.6818 0-1.2344-.5526-1.2344-1.2364m14.0868 5.717c0 .6842-.5524 1.2363-1.2341 1.2363H6.3575c-.6818 0-1.2344-.552-1.2344-1.2363 0-.6837.5526-1.2363 1.2344-1.2363h11.4517c.6817 0 1.234 5526 1.234 1.2363m-5.351-5.0244c-.3814-.5657-.2323-1.3328.3334-1.7143l2.8628-1.9334c.5613-.3902 1.3329-.2324 1.7144.3289.3815.5654.2323 1.3329-.3334 1.7144l-2.8628 1.9333c-.5442.3831-1.3348.2379-1.7144-.3289zm7.8393 7.6018a.8815.8815 0 0 1-.258.6227l-2.1277 2.1277a.8822.8822 0 0 1-.623.258H5.4772a.8822.8822 0 0 1-.623-.258l-2.1277-2.1277a.8815.8815 0 0 1-.258-.6227V5.4818a.8797.8797 0 0 1 .258-.6228l2.1277-2.1282a.8816.8816 0 0 1 .623-.2578h13.0456a.8816.8816 0 0 1 .623.2578l2.1277 2.1282a.8797.8797 0 0 1 .258.6228V18.519zm1.811-15.0835L20.5644.6577A2.2454 2.2454 0 0 0 18.9775 0H5.0207A2.2445 2.2445 0 0 0 3.433.658L.657 3.4359A2.2449 2.2449 0 0 0 0 5.0228v13.9547c0 .5953.2366 1.1667.6575 1.5872l2.778 2.7779c.421.421.9918.6573 1.5871.6573h13.9548a2.2448 2.2448 0 0 0 1.5872-.6573l2.7779-2.7779A2.2436 2.2436 0 0 0 24 18.9775V5.023a2.2451 2.2451 0 0 0-.6575-1.5875z"/>
|
|
30
|
+
</svg>
|
|
31
|
+
Suite File: <strong>{{ suite.name }}.robot</strong>
|
|
32
|
+
</button>
|
|
33
|
+
{% endif %}
|
|
34
|
+
</h2>
|
|
35
|
+
<div id="collapse{{ index }}" class="accordion-collapse collapse"
|
|
36
|
+
aria-labelledby="heading{{ index }}">
|
|
37
|
+
<div class="accordion-body">
|
|
38
|
+
<div class="alert alert-secondary small-text text-start p-2 mb-3" role="alert">
|
|
39
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
40
|
+
{% if suite.is_folder %}
|
|
41
|
+
{% if suite.doc is not none %}
|
|
42
|
+
<tr>
|
|
43
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Docs:</td>
|
|
44
|
+
<td style="text-align: left;">{{ suite.doc }}</td>
|
|
45
|
+
</tr>
|
|
46
|
+
{% endif %}
|
|
47
|
+
{% if suite.metadata is not none %}
|
|
48
|
+
<tr>
|
|
49
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">βοΈ Metadata:</td>
|
|
50
|
+
<td style="text-align: left;">{{ suite.metadata }}</td>
|
|
51
|
+
</tr>
|
|
52
|
+
{% endif %}
|
|
53
|
+
<tr>
|
|
54
|
+
<td style="width: 10%; font-weight: bold;">π Number of Tests:</td>
|
|
55
|
+
<td style="text-align: left;">{{ suite.total_tests }}</td>
|
|
56
|
+
</tr>
|
|
57
|
+
{% else %}
|
|
58
|
+
{% if suite.doc is not none %}
|
|
59
|
+
<tr>
|
|
60
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Docs:</td>
|
|
61
|
+
<td style="text-align: left;">{{ suite.doc }}</td>
|
|
62
|
+
</tr>
|
|
63
|
+
{% endif %}
|
|
64
|
+
{% if suite.source is not none %}
|
|
65
|
+
<tr>
|
|
66
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Source:</td>
|
|
67
|
+
<td style="text-align: left;">
|
|
68
|
+
<a href="{{ suite.source }}" target="_blank">{{ suite.source }}</a>
|
|
69
|
+
</td>
|
|
70
|
+
</tr>
|
|
71
|
+
{% endif %}
|
|
72
|
+
<tr>
|
|
73
|
+
<td style="width: 10%; font-weight: bold;">π Number of Tests:</td>
|
|
74
|
+
<td style="text-align: left;">{{ suite.num_tests }}</td>
|
|
75
|
+
</tr>
|
|
76
|
+
{% endif %}
|
|
77
|
+
</table>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{% if suite.tests %}
|
|
81
|
+
<div class="accordion mt-3" id="testAccordion{{ index }}">
|
|
82
|
+
{% for test in suite.tests %}
|
|
83
|
+
<div class="accordion-item">
|
|
84
|
+
<h2 class="accordion-header" id="headingTest{{ index }}-{{ loop.index }}">
|
|
85
|
+
<button class="accordion-button collapsed" type="button"
|
|
86
|
+
data-bs-toggle="collapse"
|
|
87
|
+
data-bs-target="#collapseTest{{ index }}-{{ loop.index }}"
|
|
88
|
+
aria-expanded="false"
|
|
89
|
+
aria-controls="collapseTest{{ index }}-{{ loop.index }}">
|
|
90
|
+
<span class="custom-chevron">
|
|
91
|
+
β―
|
|
92
|
+
</span>
|
|
93
|
+
<svg class="svg-icon" width="20" height="20" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
94
|
+
<title>Robot Framework</title>
|
|
95
|
+
<path d="M4.9565 10.2246c0-1.8766 1.5257-3.4023 3.4-3.4023 1.8766 0 3.4024 1.5257 3.4024 3.4023 0 .6838-.5526 1.2364-1.2341 1.2364-.6818 0-1.2344-.5526-1.2344-1.2364 0-.513-.4185-.9296-.9338-.9296-.5129 0-.9317.4165-.9317.9296 0 .6838-.5523 1.2364-1.234 1.2364-.6818 0-1.2344-.5526-1.2344-1.2364m14.0868 5.717c0 .6842-.5524 1.2363-1.2341 1.2363H6.3575c-.6818 0-1.2344-.552-1.2344-1.2363 0-.6837.5526-1.2363 1.2344-1.2363h11.4517c.6817 0 1.234 5526 1.234 1.2363m-5.351-5.0244c-.3814-.5657-.2323-1.3328.3334-1.7143l2.8628-1.9334c.5613-.3902 1.3329-.2324 1.7144.3289.3815.5654.2323 1.3329-.3334 1.7144l-2.8628 1.9333c-.5442.3831-1.3348.2379-1.7144-.3289zm7.8393 7.6018a.8815.8815 0 0 1-.258.6227l-2.1277 2.1277a.8822.8822 0 0 1-.623.258H5.4772a.8822.8822 0 0 1-.623-.258l-2.1277-2.1277a.8815.8815 0 0 1-.258-.6227V5.4818a.8797.8797 0 0 1 .258-.6228l2.1277-2.1282a.8816.8816 0 0 1 .623-.2578h13.0456a.8816.8816 0 0 1 .623.2578l2.1277 2.1282a.8797.8797 0 0 1 .258.6228V18.519zm1.811-15.0835L20.5644.6577A2.2454 2.2454 0 0 0 18.9775 0H5.0207A2.2445 2.2445 0 0 0 3.433.658L.657 3.4359A2.2449 2.2449 0 0 0 0 5.0228v13.9547c0 .5953.2366 1.1667.6575 1.5872l2.778 2.7779c.421.421.9918.6573 1.5871.6573h13.9548a2.2448 2.2448 0 0 0 1.5872-.6573l2.7779-2.7779A2.2436 2.2436 0 0 0 24 18.9775V5.023a2.2451 2.2451 0 0 0-.6575-1.5875z"/>
|
|
96
|
+
</svg>
|
|
97
|
+
Test Case: <strong>{{ test.name }}</strong>
|
|
98
|
+
</button>
|
|
99
|
+
</h2>
|
|
100
|
+
<div id="collapseTest{{ index }}-{{ loop.index }}" class="accordion-collapse collapse"
|
|
101
|
+
aria-labelledby="headingTest{{ index }}-{{ loop.index }}">
|
|
102
|
+
<div class="accordion-body">
|
|
103
|
+
<div class="alert alert-light text-start p-2 mb-3" role="alert">
|
|
104
|
+
{% set has_info = test.doc is not none or test.source is not none or test.tags is not none or test.keywords is not none %}
|
|
105
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
106
|
+
{% if test.doc is not none %}
|
|
107
|
+
<tr>
|
|
108
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Docs:</td>
|
|
109
|
+
<td style="text-align: left;">{{ test.doc }}</td>
|
|
110
|
+
</tr>
|
|
111
|
+
{% endif %}
|
|
112
|
+
{% if test.source is not none %}
|
|
113
|
+
<tr>
|
|
114
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Source:</td>
|
|
115
|
+
<td style="text-align: left;">
|
|
116
|
+
<a href="{{ test.source }}" target="_blank">{{ test.source }}</a>
|
|
117
|
+
</td>
|
|
118
|
+
</tr>
|
|
119
|
+
{% endif %}
|
|
120
|
+
{% if test.tags is not none %}
|
|
121
|
+
<tr>
|
|
122
|
+
<td style="width: 10%; font-weight: bold;">π· Tags:</td>
|
|
123
|
+
<td style="text-align: left;">
|
|
124
|
+
{% if test.tags %}
|
|
125
|
+
{{ test.tags | join(', ') }}
|
|
126
|
+
{% endif %}
|
|
127
|
+
</td>
|
|
128
|
+
</tr>
|
|
129
|
+
{% endif %}
|
|
130
|
+
{% if test.keywords is not none %}
|
|
131
|
+
<tr>
|
|
132
|
+
<td style="width: 10%; font-weight: bold; vertical-align: top;">π Keywords:</td>
|
|
133
|
+
<td style="text-align: left;">
|
|
134
|
+
{% if test.keywords %}
|
|
135
|
+
- {{ test.keywords | join('<br>- ') }}
|
|
136
|
+
{% endif %}
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
{% endif %}
|
|
140
|
+
{% if not has_info %}
|
|
141
|
+
<tr>
|
|
142
|
+
<td class="info-msg" style="width: 10%; vertical-align: top; text-align: center;">
|
|
143
|
+
No Details Available!
|
|
144
|
+
</td>
|
|
145
|
+
</tr>
|
|
146
|
+
{% endif %}
|
|
147
|
+
</table>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
{% endfor %}
|
|
153
|
+
</div>
|
|
154
|
+
{% endif %}
|
|
155
|
+
|
|
156
|
+
{% if suite.sub_suites %}
|
|
157
|
+
<div class="accordion mt-3">
|
|
158
|
+
{% for sub_suite in suite.sub_suites %}
|
|
159
|
+
{{ render_suite(sub_suite, index|string + loop.index|string) }}
|
|
160
|
+
{% endfor %}
|
|
161
|
+
</div>
|
|
162
|
+
{% endif %}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
{% endmacro %}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
<!-- Templates - Main Part -->
|
|
172
|
+
<!DOCTYPE html>
|
|
173
|
+
<html lang="de">
|
|
174
|
+
<head>
|
|
175
|
+
<meta charset="UTF-8">
|
|
176
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
177
|
+
<title>Robot Framework - Test Documentation</title>
|
|
178
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
179
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
180
|
+
|
|
181
|
+
<!-- In-File CSS Style Sheet -->
|
|
182
|
+
<style>
|
|
183
|
+
/* Hintergrundfarbe der Seite */
|
|
184
|
+
html, body, .container-fluid {
|
|
185
|
+
background-color: {{ colors.background }};
|
|
186
|
+
color: {{ colors.text_color }}
|
|
187
|
+
font-size: 14px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
a {
|
|
191
|
+
color: {{ colors.text_color }}
|
|
192
|
+
}
|
|
193
|
+
a:hover {
|
|
194
|
+
color: {{ colors.robot_icon }}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.custom-line {
|
|
198
|
+
background-color: {{ colors.robot_icon }}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#testSuiteAccordion {
|
|
202
|
+
background-color: {{ colors.background }} !important;
|
|
203
|
+
max-width: 98%;
|
|
204
|
+
margin: 0 auto;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.svg-icon {
|
|
208
|
+
fill: {{ colors.robot_icon }};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Style fΓΌr das Accordion */
|
|
212
|
+
.accordion-item {
|
|
213
|
+
background-color: {{ colors.inner_color }} !important;
|
|
214
|
+
border-radius: 6px;
|
|
215
|
+
margin: 1px;
|
|
216
|
+
border: 1px solid {{ colors.border_color }};
|
|
217
|
+
font-size: 14px;
|
|
218
|
+
box-sizing: border-box;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.accordion-header {
|
|
222
|
+
font-size: 14px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.accordion-button {
|
|
226
|
+
background-color: {{ colors.inner_color }};
|
|
227
|
+
color: {{ colors.text_color }};
|
|
228
|
+
font-size: 14px;
|
|
229
|
+
height: 35px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.accordion-button:hover {
|
|
233
|
+
background-color: {{ colors.button_hover_color }} !important;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.accordion-button:not(.collapsed),
|
|
237
|
+
.accordion-button:focus,
|
|
238
|
+
.accordion-button:active {
|
|
239
|
+
background-color: {{ colors.button_active_color }} !important;
|
|
240
|
+
box-shadow: none !important;
|
|
241
|
+
color: {{ colors.text_color }} !important;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.custom-chevron {
|
|
245
|
+
color: {{ colors.text_color }};
|
|
246
|
+
margin-right: 8px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.accordion-button.collapsed .custom-chevron {
|
|
250
|
+
transform: rotate(0deg);
|
|
251
|
+
transition: transform 0.3s ease;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.accordion-button .custom-chevron {
|
|
255
|
+
transform: rotate(90deg);
|
|
256
|
+
transition: transform 0.3s ease;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.accordion-button::after {
|
|
260
|
+
background-image: none !important;
|
|
261
|
+
content: none !important;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.accordion-body {
|
|
265
|
+
background-color: {{ colors.inner_color }};
|
|
266
|
+
padding: 5px;
|
|
267
|
+
font-size: 14px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* Liste der TestfΓ€lle */
|
|
271
|
+
.list-group-item {
|
|
272
|
+
background-color: #fff;
|
|
273
|
+
border: 1px solid {{ colors.border_color }};
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
font-size: 14px;
|
|
277
|
+
padding: 6px 10px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Testfall Hover Effekt */
|
|
281
|
+
.list-group-item:hover {
|
|
282
|
+
background-color: {{ colors.inner_color }};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* Tabelle fΓΌr Test Suite Informationen */
|
|
286
|
+
.alert {
|
|
287
|
+
font-size: 14px;
|
|
288
|
+
padding: 6px 10px;
|
|
289
|
+
background-color: {{ colors.inner_color }};
|
|
290
|
+
border: 1px solid {{ colors.border_color }};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
table {
|
|
294
|
+
height: 10px;
|
|
295
|
+
font-size: 13px;
|
|
296
|
+
color: {{ colors.text_color }};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
td {
|
|
300
|
+
padding: 1px 1px;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* TitelgrΓΆΓe */
|
|
304
|
+
h1 {
|
|
305
|
+
color: {{ colors.title_color }};
|
|
306
|
+
font-size: 30px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.info-msg {
|
|
310
|
+
color: {{ colors.title_color }};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.generated_at {
|
|
314
|
+
color: {{ colors.title_color }}
|
|
315
|
+
}
|
|
316
|
+
</style>
|
|
317
|
+
</head>
|
|
318
|
+
<body class="d-flex flex-column min-vh-100">
|
|
319
|
+
<div class="container-fluid mt-5 flex-grow-1">
|
|
320
|
+
<h1 class="text-center">{{ title }}</h1>
|
|
321
|
+
|
|
322
|
+
<div class="accordion mt-4" id="testSuiteAccordion">
|
|
323
|
+
{% for suite in suites %}
|
|
324
|
+
{{ render_suite(suite, loop.index, loop.first) }}
|
|
325
|
+
{% endfor %}
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<!-- Footer -->
|
|
330
|
+
<p class="text-center generated_at py-1 border-top">
|
|
331
|
+
Generated at: {{ generated_at }}<br>robotframework-testdoc 2.0 by Marvin Klerx
|
|
332
|
+
</p>
|
|
333
|
+
</body>
|
|
334
|
+
</html>
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ...helper.cliargs import CommandLineArguments
|
|
2
|
+
from .themes import DEFAULT_THEME, ROBOT_THEME, DARK_THEME, BLUE_THEME
|
|
3
|
+
|
|
4
|
+
class ThemeConfig():
|
|
5
|
+
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.args = CommandLineArguments().data
|
|
8
|
+
|
|
9
|
+
def theme(self):
|
|
10
|
+
_theme = self.args.colors
|
|
11
|
+
if _theme:
|
|
12
|
+
if "default" in _theme:
|
|
13
|
+
return self._get_predefined_theme(_theme.get("default"))
|
|
14
|
+
return _theme
|
|
15
|
+
return DARK_THEME
|
|
16
|
+
|
|
17
|
+
def _get_predefined_theme(self, theme: str):
|
|
18
|
+
theme = theme.strip()
|
|
19
|
+
if theme == "default" or theme == 0:
|
|
20
|
+
return DEFAULT_THEME
|
|
21
|
+
if theme == "dark" or theme == 1:
|
|
22
|
+
return DARK_THEME
|
|
23
|
+
if theme == "robot" or theme == 2:
|
|
24
|
+
return ROBOT_THEME
|
|
25
|
+
if theme == "blue" or theme == 3:
|
|
26
|
+
return BLUE_THEME
|
|
27
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# default
|
|
2
|
+
DEFAULT_THEME = {
|
|
3
|
+
"background": "#f8f9fa",
|
|
4
|
+
"inner_color": "#f8f9fa",
|
|
5
|
+
"button_active_color": "#f8f9fa",
|
|
6
|
+
"button_hover_color": "#C2C2C2",
|
|
7
|
+
"border_color": "#353535",
|
|
8
|
+
"text_color": "#353535",
|
|
9
|
+
"title_color": "#343a40",
|
|
10
|
+
"robot_icon": "#00c0b5",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ROBOT_THEME = {
|
|
14
|
+
"background": "#f8f9fa",
|
|
15
|
+
"inner_color": "#f8f9fa",
|
|
16
|
+
"button_active_color": "#C2C2C2",
|
|
17
|
+
"button_hover_color": "#C2C2C2",
|
|
18
|
+
"border_color": "black",
|
|
19
|
+
"text_color": "black",
|
|
20
|
+
"title_color": "black",
|
|
21
|
+
"robot_icon": "#00c0b5",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
DARK_THEME = {
|
|
25
|
+
"background": "#303030",
|
|
26
|
+
"inner_color": "#303030",
|
|
27
|
+
"button_active_color": "#5F5F5F",
|
|
28
|
+
"button_hover_color": "#5F5F5F",
|
|
29
|
+
"border_color": "white",
|
|
30
|
+
"text_color": "white",
|
|
31
|
+
"title_color": "#00c0b5",
|
|
32
|
+
"robot_icon": "#00c0b5",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
BLUE_THEME = {
|
|
36
|
+
"background": "#000028",
|
|
37
|
+
"inner_color": "#000028",
|
|
38
|
+
"button_active_color": "#193966",
|
|
39
|
+
"button_hover_color": "#193966",
|
|
40
|
+
"border_color": "#CCCCCC",
|
|
41
|
+
"text_color": "#CCCCCC",
|
|
42
|
+
"title_color": "#00ffb9",
|
|
43
|
+
"robot_icon": "#00ffb9",
|
|
44
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from jinja2 import Environment, FileSystemLoader
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from ..html.themes.theme_config import ThemeConfig
|
|
5
|
+
from ..helper.cliargs import CommandLineArguments
|
|
6
|
+
from ..helper.datetimeconverter import DateTimeConverter
|
|
7
|
+
from ..helper.logger import Logger
|
|
8
|
+
|
|
9
|
+
class TestDocHtmlRendering():
|
|
10
|
+
|
|
11
|
+
TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "html", "templates")
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.args = CommandLineArguments().data
|
|
15
|
+
|
|
16
|
+
def render_testdoc(self,
|
|
17
|
+
suites,
|
|
18
|
+
output_file
|
|
19
|
+
):
|
|
20
|
+
env = Environment(loader=FileSystemLoader(self.TEMPLATE_DIR))
|
|
21
|
+
template = env.get_template("jinja_template_01.html")
|
|
22
|
+
|
|
23
|
+
rendered_html = template.render(
|
|
24
|
+
suites=suites,
|
|
25
|
+
generated_at=DateTimeConverter().get_generated_datetime(),
|
|
26
|
+
title=self.args.title,
|
|
27
|
+
colors=ThemeConfig().theme()
|
|
28
|
+
)
|
|
29
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
30
|
+
f.write(rendered_html)
|
|
31
|
+
Logger().LogKeyValue("Generated Test Documentation File: ", output_file)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from robot.api import TestSuite
|
|
4
|
+
|
|
5
|
+
from ...helper.cliargs import CommandLineArguments
|
|
6
|
+
from ...helper.logger import Logger
|
|
7
|
+
|
|
8
|
+
class SourcePrefixModifier():
|
|
9
|
+
|
|
10
|
+
GITLAB_CONNECTOR = "-/blob/main/"
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.args = CommandLineArguments().data
|
|
14
|
+
|
|
15
|
+
def _modify(self, suite: TestSuite, prefix: str):
|
|
16
|
+
prefix_type, prefix = self._prefix_validation(prefix)
|
|
17
|
+
if "gitlab" in prefix_type:
|
|
18
|
+
SourcePrefixGitLab()._apply_gitlab_source_to_suite(suite, prefix)
|
|
19
|
+
else:
|
|
20
|
+
raise ValueError(f"No matching source-prefix modifier found for: {prefix_type} with prefix: {prefix}")
|
|
21
|
+
|
|
22
|
+
def _prefix_validation(self, prefix: str) -> list:
|
|
23
|
+
if "::" not in prefix:
|
|
24
|
+
raise ValueError("Missing source-prefix type - expected type in format like 'gitlab::source-prefix!'")
|
|
25
|
+
prefix = self.args.sourceprefix.split("::")
|
|
26
|
+
return prefix[0], prefix[1]
|
|
27
|
+
|
|
28
|
+
def modify_source_prefix(self, suite_object: TestSuite) -> TestSuite:
|
|
29
|
+
Logger().LogKeyValue("Using Prefix for Source: ", self.args.sourceprefix, "yellow") if self.args.verbose_mode else None
|
|
30
|
+
for suite in suite_object:
|
|
31
|
+
self._modify(suite, self.args.sourceprefix)
|
|
32
|
+
return suite_object
|
|
33
|
+
|
|
34
|
+
class SourcePrefixGitLab():
|
|
35
|
+
"""
|
|
36
|
+
Source Prefix Modifier for "GitLab" Projects.
|
|
37
|
+
Expected CMD Line Arg: "gitlab::prefix"
|
|
38
|
+
"""
|
|
39
|
+
def _get_git_root(self, path):
|
|
40
|
+
current = os.path.abspath(path)
|
|
41
|
+
while current != os.path.dirname(current):
|
|
42
|
+
if os.path.isdir(os.path.join(current, ".git")):
|
|
43
|
+
return current
|
|
44
|
+
current = os.path.dirname(current)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def _get_git_branch(self, git_root):
|
|
48
|
+
head_file = os.path.join(git_root, ".git", "HEAD")
|
|
49
|
+
if not os.path.isfile(head_file):
|
|
50
|
+
return "main"
|
|
51
|
+
with open(head_file, "r") as f:
|
|
52
|
+
content = f.read().strip()
|
|
53
|
+
if content.startswith("ref:"):
|
|
54
|
+
return content.split("/")[-1]
|
|
55
|
+
return "main"
|
|
56
|
+
|
|
57
|
+
def _convert_to_gitlab_url(self, file_path, prefix):
|
|
58
|
+
git_root = self._get_git_root(file_path)
|
|
59
|
+
git_branch = self._get_git_branch(git_root)
|
|
60
|
+
if not git_root:
|
|
61
|
+
return "GitLink error"
|
|
62
|
+
rel_path = os.path.relpath(file_path, git_root).replace(os.sep, "/")
|
|
63
|
+
return prefix.rstrip("/") + "/-/blob/" + git_branch + "/" + rel_path
|
|
64
|
+
|
|
65
|
+
def _apply_gitlab_source_to_suite(self, suite_dict, prefix):
|
|
66
|
+
try:
|
|
67
|
+
suite_dict["source"] = self._convert_to_gitlab_url(suite_dict["source"], prefix)
|
|
68
|
+
except:
|
|
69
|
+
suite_dict["source"] = "GitLink error"
|
|
70
|
+
|
|
71
|
+
for test in suite_dict.get("tests", []):
|
|
72
|
+
try:
|
|
73
|
+
test["source"] = self._convert_to_gitlab_url(test["source"], prefix)
|
|
74
|
+
except:
|
|
75
|
+
test["source"] = "GitLink error"
|
|
76
|
+
|
|
77
|
+
for sub_suite in suite_dict.get("sub_suites", []):
|
|
78
|
+
self._apply_gitlab_source_to_suite(sub_suite, prefix)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from robot.api import TestSuite
|
|
2
|
+
|
|
3
|
+
from ...helper.cliargs import CommandLineArguments
|
|
4
|
+
from ...helper.logger import Logger
|
|
5
|
+
from .sourceprefixmodifier import SourcePrefixModifier
|
|
6
|
+
|
|
7
|
+
class SuiteFileModifier():
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.args = CommandLineArguments().data
|
|
11
|
+
self.suite = None
|
|
12
|
+
|
|
13
|
+
#############################################################################################################################
|
|
14
|
+
|
|
15
|
+
def run(self, suite_object: TestSuite = None):
|
|
16
|
+
if not suite_object:
|
|
17
|
+
raise KeyError(f"[{self.__class__}] - Error - Suite Object must not be None!")
|
|
18
|
+
self.suite = suite_object
|
|
19
|
+
|
|
20
|
+
# Modify generic params / hide some params
|
|
21
|
+
self._modify_root_suite_name()
|
|
22
|
+
self._modify_root_suite_doc()
|
|
23
|
+
self._modify_root_suite_metadata()
|
|
24
|
+
self._modify_tags()
|
|
25
|
+
self._modify_test_doc()
|
|
26
|
+
self._modify_suite_doc()
|
|
27
|
+
self._modify_keywords()
|
|
28
|
+
self._modify_source()
|
|
29
|
+
return self.suite
|
|
30
|
+
|
|
31
|
+
#############################################################################################################################
|
|
32
|
+
|
|
33
|
+
def _modify_root_suite_name(self):
|
|
34
|
+
if not self.args.name:
|
|
35
|
+
return
|
|
36
|
+
Logger().LogKeyValue("Modified Name of Root Suite: ", self.args.name, "yellow") if self.args.verbose_mode else None
|
|
37
|
+
self.suite[0]["name"] = self.args.name
|
|
38
|
+
|
|
39
|
+
#############################################################################################################################
|
|
40
|
+
|
|
41
|
+
def _modify_root_suite_doc(self):
|
|
42
|
+
if not self.args.doc:
|
|
43
|
+
return
|
|
44
|
+
Logger().LogKeyValue("Modified Doc of Root Suite: ", self.args.name, "yellow") if self.args.verbose_mode else None
|
|
45
|
+
self.suite[0]["doc"] = self.args.doc
|
|
46
|
+
|
|
47
|
+
#############################################################################################################################
|
|
48
|
+
|
|
49
|
+
def _modify_root_suite_metadata(self):
|
|
50
|
+
if not self.args.metadata:
|
|
51
|
+
return
|
|
52
|
+
Logger().LogKeyValue("Modified Metadata of Root Suite: ", self.args.metadata, "yellow") if self.args.verbose_mode else None
|
|
53
|
+
formatted_metadata = "<br>".join([f"{k}: {v}" for k, v in self.args.metadata.items()])
|
|
54
|
+
self.suite[0]["metadata"] = formatted_metadata
|
|
55
|
+
|
|
56
|
+
#############################################################################################################################
|
|
57
|
+
|
|
58
|
+
def _modify_tags(self):
|
|
59
|
+
if not self.args.hide_tags:
|
|
60
|
+
return
|
|
61
|
+
Logger().LogKeyValue("Removed Info from Test Documentation: ", "Tags", "red") if self.args.verbose_mode else None
|
|
62
|
+
self._remove_suite_object_parameter(self.suite, "tags", "test")
|
|
63
|
+
|
|
64
|
+
#############################################################################################################################
|
|
65
|
+
|
|
66
|
+
def _modify_test_doc(self):
|
|
67
|
+
if not self.args.hide_test_doc:
|
|
68
|
+
return
|
|
69
|
+
Logger().LogKeyValue("Removed Info from Test Documentation: ", "Test Doc", "red") if self.args.verbose_mode else None
|
|
70
|
+
self._remove_suite_object_parameter(self.suite, "doc", "test")
|
|
71
|
+
|
|
72
|
+
#############################################################################################################################
|
|
73
|
+
|
|
74
|
+
def _modify_suite_doc(self):
|
|
75
|
+
if not self.args.hide_suite_doc:
|
|
76
|
+
return
|
|
77
|
+
Logger().LogKeyValue("Removed Info from Test Documentation: ", "Suite Doc", "red") if self.args.verbose_mode else None
|
|
78
|
+
self._remove_suite_object_parameter(self.suite, "doc", "suite")
|
|
79
|
+
|
|
80
|
+
#############################################################################################################################
|
|
81
|
+
|
|
82
|
+
def _modify_keywords(self):
|
|
83
|
+
if not self.args.hide_keywords:
|
|
84
|
+
return
|
|
85
|
+
Logger().LogKeyValue("Removed Info from Test Documentation: ", "Keywod Calls", "red") if self.args.verbose_mode else None
|
|
86
|
+
self._remove_suite_object_parameter(self.suite, "keywords", "test")
|
|
87
|
+
|
|
88
|
+
#############################################################################################################################
|
|
89
|
+
|
|
90
|
+
def _modify_source(self):
|
|
91
|
+
if self.args.hide_source:
|
|
92
|
+
Logger().LogKeyValue("Removed Info from Test Documentation: ", "Test Suite / Case Source", "red") if self.args.verbose_mode else None
|
|
93
|
+
self._remove_suite_object_parameter(self.suite, "source", "both")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
# Modify the source path for the test documentation
|
|
97
|
+
if self.args.sourceprefix:
|
|
98
|
+
self.suite = SourcePrefixModifier().modify_source_prefix(self.suite)
|
|
99
|
+
|
|
100
|
+
#############################################################################################################################
|
|
101
|
+
#############################################################################################################################
|
|
102
|
+
#############################################################################################################################
|
|
103
|
+
|
|
104
|
+
def _remove_suite_object_parameter(self, suites: list, field: str, target: str = "test"):
|
|
105
|
+
"""Remove a specific key from the test suite or test case object"""
|
|
106
|
+
for suite in suites:
|
|
107
|
+
if target in ("suite", "both"):
|
|
108
|
+
suite[field] = None
|
|
109
|
+
if target in ("test", "both"):
|
|
110
|
+
for test in suite.get("tests", []):
|
|
111
|
+
test[field] = None
|
|
112
|
+
if "sub_suites" in suite:
|
|
113
|
+
self._remove_suite_object_parameter(suite["sub_suites"], field, target)
|
|
114
|
+
|
|
115
|
+
#############################################################################################################################
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from robot.api import TestSuite
|
|
2
|
+
from ..helper.cliargs import CommandLineArguments
|
|
3
|
+
|
|
4
|
+
class TestCaseParser():
|
|
5
|
+
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.args = CommandLineArguments().data
|
|
8
|
+
|
|
9
|
+
def parse_test(self,
|
|
10
|
+
suite: TestSuite,
|
|
11
|
+
suite_info: dict
|
|
12
|
+
) -> dict:
|
|
13
|
+
|
|
14
|
+
for test in suite.tests:
|
|
15
|
+
test_info = {
|
|
16
|
+
"name": test.name,
|
|
17
|
+
"doc": "<br>".join(line.replace("\\n","") for line in test.doc.splitlines()
|
|
18
|
+
if line.strip()) if test.doc else "No Test Case Documentation Available",
|
|
19
|
+
"tags": test.tags if test.tags else "No Tags Configured",
|
|
20
|
+
"source": str(test.source),
|
|
21
|
+
"keywords": [kw.name for kw in test.body if hasattr(kw, 'name')] or "No Keyword Calls in Test"
|
|
22
|
+
}
|
|
23
|
+
suite_info["tests"].append(test_info)
|
|
24
|
+
return suite_info
|
|
25
|
+
|
|
26
|
+
def consider_tags(self, suite: TestSuite) -> TestSuite:
|
|
27
|
+
if len(self.args.include) > 0:
|
|
28
|
+
suite.configure(include_tags=self.args.include)
|
|
29
|
+
if len(self.args.exclude) > 0:
|
|
30
|
+
suite.configure(exclude_tags=self.args.exclude)
|
|
31
|
+
return suite
|