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.

@@ -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:&nbsp;<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
+ &nbsp;Suite File:&nbsp;<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
+ &nbsp;Test Case:&nbsp;<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