testit-adapter-robotframework 4.2.8__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.
- testit_adapter_robotframework/TMSLibrary.py +120 -0
- testit_adapter_robotframework/__init__.py +0 -0
- testit_adapter_robotframework/listeners.py +119 -0
- testit_adapter_robotframework/models.py +247 -0
- testit_adapter_robotframework/utils.py +86 -0
- testit_adapter_robotframework-4.2.8.dist-info/METADATA +307 -0
- testit_adapter_robotframework-4.2.8.dist-info/RECORD +9 -0
- testit_adapter_robotframework-4.2.8.dist-info/WHEEL +5 -0
- testit_adapter_robotframework-4.2.8.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
2
|
+
|
|
3
|
+
from testit_python_commons.services import TmsPluginManager
|
|
4
|
+
|
|
5
|
+
from .listeners import AutotestAdapter, TestRunAdapter
|
|
6
|
+
from .models import Option
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def enabled(func):
|
|
10
|
+
def wrapped(self, *args, **kwargs):
|
|
11
|
+
if self.enabled:
|
|
12
|
+
return func(self, *args, **kwargs)
|
|
13
|
+
else:
|
|
14
|
+
raise ImportError("TestIt module should be enabled. Use '-v testit' CLI option")
|
|
15
|
+
|
|
16
|
+
return wrapped
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TMSLibrary:
|
|
20
|
+
"""Library for exporting result to TestIt.
|
|
21
|
+
|
|
22
|
+
= Table of contents =
|
|
23
|
+
|
|
24
|
+
%TOC%
|
|
25
|
+
|
|
26
|
+
= Usage =
|
|
27
|
+
|
|
28
|
+
This library has several keyword, for example `Add Link`, adding links to result of test in TestIt
|
|
29
|
+
|
|
30
|
+
= Examples =
|
|
31
|
+
|
|
32
|
+
| `Add Message` | My message | | |
|
|
33
|
+
| `Add Link` | http://ya.ru | | |
|
|
34
|
+
| `Add Attachments` | image.png | log.txt | video.gif |
|
|
35
|
+
"""
|
|
36
|
+
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
|
|
37
|
+
ROBOT_LIBRARY_VERSION = '1.0'
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
built_in = BuiltIn()
|
|
41
|
+
self.enabled = built_in.get_variable_value("${testit}", None) is not None
|
|
42
|
+
if self.enabled:
|
|
43
|
+
cli_params = ["tmsUrl", "tmsPrivateToken", "tmsProjectId", "tmsConfigurationId", "tmsTestRunId",
|
|
44
|
+
"tmsProxy", "tmsTestRunName", "tmsAdapterMode", "tmsConfigFile", "tmsCertValidation",
|
|
45
|
+
"tmsAutomaticCreationTestCases", "tmsAutomaticUpdationLinksToTestCases", "tmsImportRealtime"]
|
|
46
|
+
option = Option(**{param: built_in.get_variable_value(f'${{{param}}}', None) for param in cli_params})
|
|
47
|
+
self.adapter_manager = TmsPluginManager.get_adapter_manager(option)
|
|
48
|
+
pabot_index = built_in.get_variable_value('${PABOTQUEUEINDEX}', None)
|
|
49
|
+
if pabot_index is not None:
|
|
50
|
+
try:
|
|
51
|
+
from pabot import PabotLib
|
|
52
|
+
pabot = PabotLib()
|
|
53
|
+
if int(pabot_index) == 0:
|
|
54
|
+
test_run_id = self.adapter_manager.get_test_run_id()
|
|
55
|
+
pabot.set_parallel_value_for_key('test_run_id', test_run_id)
|
|
56
|
+
else:
|
|
57
|
+
while True:
|
|
58
|
+
test_run_id = pabot.get_parallel_value_for_key('test_run_id')
|
|
59
|
+
if test_run_id:
|
|
60
|
+
break
|
|
61
|
+
self.adapter_manager.set_test_run_id(test_run_id)
|
|
62
|
+
except RuntimeError:
|
|
63
|
+
raise SystemExit
|
|
64
|
+
else:
|
|
65
|
+
self.adapter_manager.set_test_run_id(self.adapter_manager.get_test_run_id())
|
|
66
|
+
self.ROBOT_LIBRARY_LISTENER = [AutotestAdapter(self.adapter_manager), TestRunAdapter(self.adapter_manager)]
|
|
67
|
+
|
|
68
|
+
@enabled
|
|
69
|
+
def add_link(self, url, type='Defect', title=None, description=None): # noqa: A002,VNE003
|
|
70
|
+
"""
|
|
71
|
+
Adds link to current test.
|
|
72
|
+
|
|
73
|
+
Valid link types are ``Defect``, ``Issue``, ``Related``, ``BlockedBy``, ``Requirement``, ``Repository``.
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
from testit_python_commons.models.link import Link
|
|
77
|
+
|
|
78
|
+
link = Link()\
|
|
79
|
+
.set_url(url)\
|
|
80
|
+
.set_title(title)\
|
|
81
|
+
.set_link_type(type)\
|
|
82
|
+
.set_description(description)
|
|
83
|
+
self.ROBOT_LIBRARY_LISTENER[0].active_test.resultLinks.append(link)
|
|
84
|
+
|
|
85
|
+
@enabled
|
|
86
|
+
def add_links(self, *links):
|
|
87
|
+
"""
|
|
88
|
+
Adds several links to current test.
|
|
89
|
+
|
|
90
|
+
Every link should be a dict with ``url`` key. See `Add Link` keyword for more information.
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
for link in links:
|
|
94
|
+
if isinstance(link, dict):
|
|
95
|
+
self.add_link(**link)
|
|
96
|
+
|
|
97
|
+
@enabled
|
|
98
|
+
def add_attachments(self, *paths):
|
|
99
|
+
"""
|
|
100
|
+
Adds several attachments to current test.
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
attachments = self.adapter_manager.load_attachments(paths)
|
|
104
|
+
self.ROBOT_LIBRARY_LISTENER[0].active_test.attachments.extend(attachments)
|
|
105
|
+
|
|
106
|
+
@enabled
|
|
107
|
+
def add_attachment(self, text, filename=None):
|
|
108
|
+
"""
|
|
109
|
+
Adds attachment to current test
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
attachment = self.adapter_manager.create_attachment(text, filename)
|
|
113
|
+
self.ROBOT_LIBRARY_LISTENER[0].active_test.attachments.extend(attachment)
|
|
114
|
+
|
|
115
|
+
@enabled
|
|
116
|
+
def add_message(self, message):
|
|
117
|
+
"""
|
|
118
|
+
Adds error message to current test
|
|
119
|
+
"""
|
|
120
|
+
self.ROBOT_LIBRARY_LISTENER[0].active_test.message = message
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from robot.api import SuiteVisitor, logger
|
|
4
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
5
|
+
|
|
6
|
+
from .models import Autotest
|
|
7
|
+
from .utils import STEP_STATUSES, STATUS_TYPES, convert_time, convert_executable_test_to_test_result_model, get_hash
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AutotestAdapter:
|
|
11
|
+
ROBOT_LISTENER_API_VERSION = 2
|
|
12
|
+
|
|
13
|
+
# self.adapter_manager.on_block_completed()
|
|
14
|
+
# self.adapter_manager.on_running_started()
|
|
15
|
+
|
|
16
|
+
def __init__(self, adapter_manager):
|
|
17
|
+
self.adapter_manager = adapter_manager
|
|
18
|
+
self.active_test = None
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def get_test_title(attrs):
|
|
22
|
+
title = attrs['type']
|
|
23
|
+
return "\t".join(
|
|
24
|
+
attrs['assign'] + [title if title not in ["SETUP", "TEARDOWN", "KEYWORD"] else "",
|
|
25
|
+
attrs['kwname']] + attrs['args'])
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def parse_arguments(args):
|
|
29
|
+
parameters = {}
|
|
30
|
+
for arg in args:
|
|
31
|
+
variables = re.findall(r'\${[a-zA-Z-_\\ \d]*}', arg)
|
|
32
|
+
for arg_var in variables:
|
|
33
|
+
value = str(BuiltIn().get_variable_value(arg_var))
|
|
34
|
+
if len(value) > 2000:
|
|
35
|
+
value = value[:2000]
|
|
36
|
+
parameters[arg_var] = value
|
|
37
|
+
return parameters if parameters else None
|
|
38
|
+
|
|
39
|
+
def start_test(self, name, attributes):
|
|
40
|
+
self.active_test = Autotest(autoTestName=name)
|
|
41
|
+
self.active_test.add_attributes(attributes)
|
|
42
|
+
BuiltIn().remove_tags("testit*")
|
|
43
|
+
self.adapter_manager.on_running_started()
|
|
44
|
+
|
|
45
|
+
def start_keyword(self, name, attributes):
|
|
46
|
+
if self.active_test:
|
|
47
|
+
title = self.get_test_title(attributes)
|
|
48
|
+
parameters = self.parse_arguments(attributes['args'])
|
|
49
|
+
self.active_test.add_step(attributes['type'], title, attributes['doc'], parameters)
|
|
50
|
+
|
|
51
|
+
def end_keyword(self, name, attributes):
|
|
52
|
+
if self.active_test:
|
|
53
|
+
title = self.get_test_title(attributes)
|
|
54
|
+
start = convert_time(attributes['starttime'])
|
|
55
|
+
end = convert_time(attributes['endtime'])
|
|
56
|
+
duration = attributes['elapsedtime']
|
|
57
|
+
outcome = STEP_STATUSES[attributes['status']]
|
|
58
|
+
self.active_test.add_step_result(title, start, end, duration, outcome, self.active_test.attachments)
|
|
59
|
+
self.active_test.attachments = []
|
|
60
|
+
|
|
61
|
+
def end_test(self, name, attributes):
|
|
62
|
+
if self.active_test:
|
|
63
|
+
self.active_test.outcome = attributes['status']
|
|
64
|
+
self.active_test.status_type = STATUS_TYPES[attributes['status']]
|
|
65
|
+
self.active_test.started_on = convert_time(attributes['starttime'])
|
|
66
|
+
self.active_test.completed_on = convert_time(attributes['endtime'])
|
|
67
|
+
if not self.active_test.message and self.active_test.outcome == 'FAIL':
|
|
68
|
+
for step in (self.active_test.setUpResults + self.active_test.stepResults +
|
|
69
|
+
self.active_test.tearDownResults):
|
|
70
|
+
if step.outcome == 'Failed':
|
|
71
|
+
self.active_test.message = f"Failed on step: '{step.title}'"
|
|
72
|
+
break
|
|
73
|
+
self.active_test.traces = attributes['message']
|
|
74
|
+
self.active_test.duration = attributes['elapsedtime']
|
|
75
|
+
self.adapter_manager.write_test(
|
|
76
|
+
convert_executable_test_to_test_result_model(self.active_test.order()))
|
|
77
|
+
|
|
78
|
+
def close(self):
|
|
79
|
+
self.adapter_manager.on_block_completed()
|
|
80
|
+
self.adapter_manager.write_tests()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestRunAdapter:
|
|
84
|
+
ROBOT_LISTENER_API_VERSION = 3
|
|
85
|
+
|
|
86
|
+
def __init__(self, adapter_manager):
|
|
87
|
+
self.adapter_manager = adapter_manager
|
|
88
|
+
|
|
89
|
+
def start_suite(self, suite, result):
|
|
90
|
+
tests = self.adapter_manager.get_autotests_for_launch()
|
|
91
|
+
if tests is not None:
|
|
92
|
+
selector = ExcludeTests(*tests)
|
|
93
|
+
suite.visit(selector)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ExcludeTests(SuiteVisitor):
|
|
97
|
+
|
|
98
|
+
def __init__(self, *tests):
|
|
99
|
+
self.tests = tests
|
|
100
|
+
if not len(self.tests):
|
|
101
|
+
logger.error('No tests to run!')
|
|
102
|
+
raise SystemExit
|
|
103
|
+
|
|
104
|
+
def start_suite(self, suite):
|
|
105
|
+
suite.tests = [t for t in suite.tests if self._is_included(t)]
|
|
106
|
+
|
|
107
|
+
def _is_included(self, test):
|
|
108
|
+
tags = test.tags
|
|
109
|
+
external_id = get_hash(test.longname)
|
|
110
|
+
for tag in tags:
|
|
111
|
+
if str(tag).lower().startswith('testit.externalid'):
|
|
112
|
+
external_id = tag.split(':', 1)[-1].strip()
|
|
113
|
+
return external_id in self.tests
|
|
114
|
+
|
|
115
|
+
def end_suite(self, suite):
|
|
116
|
+
suite.suites = [s for s in suite.suites if s.test_count > 0]
|
|
117
|
+
|
|
118
|
+
def visit_test(self, test):
|
|
119
|
+
pass
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from attr import Factory, asdict, attrib, s
|
|
5
|
+
|
|
6
|
+
from robot.api import logger
|
|
7
|
+
from testit_python_commons.models.link import Link
|
|
8
|
+
from testit_python_commons.models.link_type import LinkType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from .utils import get_hash
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
LinkTypes = ['Related', 'BlockedBy', 'Defect', 'Issue', 'Requirement', 'Repository']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def link_type_check(self, attribute, value):
|
|
18
|
+
if value.title() not in LinkTypes:
|
|
19
|
+
raise ValueError(f"Incorrect Link type: {value}")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def url_check(self, attribute, value):
|
|
23
|
+
if not bool(re.match(
|
|
24
|
+
r"(https?|ftp)://"
|
|
25
|
+
r"(\w+(-\w+)*\.)?"
|
|
26
|
+
r"((\w+(-\w+)*)\.(\w+))"
|
|
27
|
+
r"(\.\w+)*"
|
|
28
|
+
r"([\w\-._~/]*)*(?<!\.)",
|
|
29
|
+
value)):
|
|
30
|
+
raise ValueError(f"Incorrect URL: {value}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Default:
|
|
34
|
+
|
|
35
|
+
def order(self):
|
|
36
|
+
return asdict(self)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _clean_value(value):
|
|
40
|
+
return str(value).replace("'", "").replace('"', '')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _parse_testit_tag(tag):
|
|
44
|
+
tag = str(tag).strip()
|
|
45
|
+
if not tag.lower().startswith('testit.'):
|
|
46
|
+
return None, None
|
|
47
|
+
|
|
48
|
+
body = tag.split('.', 1)[-1]
|
|
49
|
+
separator = ':' if ':' in body else ('=' if '=' in body else None)
|
|
50
|
+
if not separator:
|
|
51
|
+
logger.error(f"[TestIt] Wrong tag format: {tag}")
|
|
52
|
+
return None, None
|
|
53
|
+
|
|
54
|
+
attr, value = body.split(separator, 1)
|
|
55
|
+
return attr.strip().lower(), value.strip()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@s
|
|
59
|
+
class StepResult(Default):
|
|
60
|
+
title = attrib(default='')
|
|
61
|
+
description = attrib(default='')
|
|
62
|
+
started_on = attrib(default=None)
|
|
63
|
+
completed_on = attrib(default=None)
|
|
64
|
+
duration = attrib(default=None)
|
|
65
|
+
outcome = attrib(default=None)
|
|
66
|
+
step_results = attrib(default=Factory(list))
|
|
67
|
+
attachments = attrib(default=Factory(list))
|
|
68
|
+
parameters = attrib(default=Factory(dict))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@s
|
|
72
|
+
class Step(Default):
|
|
73
|
+
title = attrib()
|
|
74
|
+
description = attrib()
|
|
75
|
+
steps = attrib(default=Factory(list))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@s
|
|
79
|
+
class Label:
|
|
80
|
+
name = attrib()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@s(kw_only=True)
|
|
84
|
+
class Autotest(Default):
|
|
85
|
+
externalID = attrib(default=None) # noqa: N815
|
|
86
|
+
autoTestName = attrib() # noqa: N815
|
|
87
|
+
steps = attrib(default=Factory(list))
|
|
88
|
+
stepResults = attrib(default=Factory(list)) # noqa: N815
|
|
89
|
+
setUp = attrib(default=Factory(list)) # noqa: N815
|
|
90
|
+
setUpResults = attrib(default=Factory(list)) # noqa: N815
|
|
91
|
+
tearDown = attrib(default=Factory(list)) # noqa: N815
|
|
92
|
+
tearDownResults = attrib(default=Factory(list)) # noqa: N815
|
|
93
|
+
resultLinks = attrib(default=Factory(list)) # noqa: N815
|
|
94
|
+
duration = attrib(default=None)
|
|
95
|
+
failureReasonNames = attrib(default=Factory(list)) # noqa: N815
|
|
96
|
+
traces = attrib(default=None)
|
|
97
|
+
outcome = attrib(default=None)
|
|
98
|
+
status_type = attrib(default=None)
|
|
99
|
+
namespace = attrib(default=None)
|
|
100
|
+
attachments = attrib(default=Factory(list))
|
|
101
|
+
parameters = attrib(default=Factory(dict))
|
|
102
|
+
properties = attrib(default=Factory(dict))
|
|
103
|
+
classname = attrib(default=None)
|
|
104
|
+
title = attrib(default=None)
|
|
105
|
+
description = attrib(default=None)
|
|
106
|
+
links = attrib(default=Factory(list))
|
|
107
|
+
labels = attrib(default=Factory(list))
|
|
108
|
+
tags = attrib(default=Factory(list))
|
|
109
|
+
workItemsID = attrib(default=Factory(list)) # noqa: N815
|
|
110
|
+
message = attrib(default="")
|
|
111
|
+
started_on = attrib(default=None)
|
|
112
|
+
completed_on = attrib(default=None)
|
|
113
|
+
externalKey = attrib(default=None)
|
|
114
|
+
|
|
115
|
+
step_depth = attrib(default=Factory(list))
|
|
116
|
+
result_depth = attrib(default=Factory(list))
|
|
117
|
+
|
|
118
|
+
def add_attributes(self, attrs):
|
|
119
|
+
self.title = attrs['originalname']
|
|
120
|
+
self.autoTestName = attrs['originalname']
|
|
121
|
+
self.externalKey = attrs['originalname']
|
|
122
|
+
self.description = attrs['doc']
|
|
123
|
+
self.template = attrs['template']
|
|
124
|
+
self.classname = attrs['longname'].split('.')[-2]
|
|
125
|
+
for tag in attrs['tags']:
|
|
126
|
+
attr, value = _parse_testit_tag(tag)
|
|
127
|
+
if attr:
|
|
128
|
+
if attr == 'externalid':
|
|
129
|
+
self.externalID = _clean_value(value)
|
|
130
|
+
elif attr == 'displayname':
|
|
131
|
+
self.autoTestName = _clean_value(value)
|
|
132
|
+
elif attr == 'title':
|
|
133
|
+
self.title = _clean_value(value)
|
|
134
|
+
elif attr == 'description':
|
|
135
|
+
self.description = _clean_value(value)
|
|
136
|
+
elif attr == 'workitemsid' or attr == 'workitemsids':
|
|
137
|
+
value = ast.literal_eval(value)
|
|
138
|
+
if isinstance(value, (str, int)):
|
|
139
|
+
self.workItemsID.append(str(value))
|
|
140
|
+
elif isinstance(value, list):
|
|
141
|
+
self.workItemsID.extend([str(i) for i in value])
|
|
142
|
+
else:
|
|
143
|
+
logger.error(f"[TestIt] Wrong workitem format: {value}")
|
|
144
|
+
elif attr == 'links':
|
|
145
|
+
value = ast.literal_eval(value)
|
|
146
|
+
try:
|
|
147
|
+
if isinstance(value, dict):
|
|
148
|
+
self.links.append(Link()\
|
|
149
|
+
.set_url(value['url'])\
|
|
150
|
+
.set_title(value.get('title', None))\
|
|
151
|
+
.set_link_type(value.get('type', LinkType.RELATED))\
|
|
152
|
+
.set_description(value.get('description', None)))
|
|
153
|
+
elif isinstance(value, list):
|
|
154
|
+
self.links.extend([Link()\
|
|
155
|
+
.set_url(link['url'])\
|
|
156
|
+
.set_title(link.get('title', None))\
|
|
157
|
+
.set_link_type(link.get('type', LinkType.RELATED))\
|
|
158
|
+
.set_description(link.get('description', None)) for link in value if isinstance(link, dict)])
|
|
159
|
+
except ValueError as e:
|
|
160
|
+
logger.error(f"[TestIt] Link Error: {e}")
|
|
161
|
+
elif attr == 'labels':
|
|
162
|
+
value = ast.literal_eval(value)
|
|
163
|
+
if isinstance(value, (str, int)):
|
|
164
|
+
self.labels.append(Label(value))
|
|
165
|
+
elif isinstance(value, list):
|
|
166
|
+
self.labels.extend([Label(item) for item in value if isinstance(item, (str, int))])
|
|
167
|
+
elif attr == 'tags':
|
|
168
|
+
value = ast.literal_eval(value)
|
|
169
|
+
if isinstance(value, (str, int)):
|
|
170
|
+
self.tags.append(str(value))
|
|
171
|
+
elif isinstance(value, list):
|
|
172
|
+
self.tags.extend([str(item) for item in value if isinstance(item, (str, int))])
|
|
173
|
+
elif attr == 'namespace':
|
|
174
|
+
self.namespace = _clean_value(value)
|
|
175
|
+
elif attr == 'classname':
|
|
176
|
+
self.classname = _clean_value(value)
|
|
177
|
+
else:
|
|
178
|
+
logger.error(f"[TestIt] Unknown attribute: {attr}")
|
|
179
|
+
if not self.externalID:
|
|
180
|
+
self.externalID = get_hash(attrs['longname'])
|
|
181
|
+
|
|
182
|
+
def add_step(self, step_type, title, description, parameters):
|
|
183
|
+
if len(self.step_depth) == 0:
|
|
184
|
+
if step_type.lower() == 'setup':
|
|
185
|
+
self.setUp.append(Step(title, description))
|
|
186
|
+
self.step_depth.append(self.setUp[-1])
|
|
187
|
+
self.setUpResults.append(StepResult(title, description, parameters=parameters))
|
|
188
|
+
self.result_depth.append(self.setUpResults[-1])
|
|
189
|
+
elif step_type.lower() == 'teardown':
|
|
190
|
+
self.tearDown.append(Step(title, description))
|
|
191
|
+
self.step_depth.append(self.tearDown[-1])
|
|
192
|
+
self.tearDownResults.append(StepResult(title, description, parameters=parameters))
|
|
193
|
+
self.result_depth.append(self.tearDownResults[-1])
|
|
194
|
+
else:
|
|
195
|
+
self.steps.append(Step(title, description))
|
|
196
|
+
self.step_depth.append(self.steps[-1])
|
|
197
|
+
self.stepResults.append(StepResult(title, description, parameters=parameters))
|
|
198
|
+
self.result_depth.append(self.stepResults[-1])
|
|
199
|
+
elif 1 <= len(self.step_depth) < 14:
|
|
200
|
+
self.step_depth[-1].steps.append(Step(title, description))
|
|
201
|
+
self.step_depth.append(self.step_depth[-1].steps[-1])
|
|
202
|
+
self.result_depth[-1].step_results.append(StepResult(title, description, parameters=parameters))
|
|
203
|
+
self.result_depth.append(self.result_depth[-1].step_results[-1])
|
|
204
|
+
|
|
205
|
+
def add_step_result(self, title, start, complete, duration, outcome, attachments):
|
|
206
|
+
if self.result_depth:
|
|
207
|
+
if self.result_depth[-1].title == title:
|
|
208
|
+
step = self.result_depth.pop()
|
|
209
|
+
step.started_on = start
|
|
210
|
+
step.completed_on = complete
|
|
211
|
+
step.duration = duration
|
|
212
|
+
step.outcome = outcome
|
|
213
|
+
step.attachments = attachments
|
|
214
|
+
if self.step_depth:
|
|
215
|
+
if self.step_depth[-1].title == title:
|
|
216
|
+
self.step_depth.pop()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class Option:
|
|
220
|
+
|
|
221
|
+
def __init__(self, **kwargs):
|
|
222
|
+
if kwargs.get('tmsUrl', None):
|
|
223
|
+
self.set_url = kwargs.get('tmsUrl', None)
|
|
224
|
+
if kwargs.get('tmsPrivateToken', None):
|
|
225
|
+
self.set_private_token = kwargs.get('tmsPrivateToken', None)
|
|
226
|
+
if kwargs.get('tmsProjectId', None):
|
|
227
|
+
self.set_project_id = kwargs.get('tmsProjectId', None)
|
|
228
|
+
if kwargs.get('tmsConfigurationId', None):
|
|
229
|
+
self.set_configuration_id = kwargs.get('tmsConfigurationId', None)
|
|
230
|
+
if kwargs.get('tmsTestRunId', None):
|
|
231
|
+
self.set_test_run_id = kwargs.get('tmsTestRunId', None)
|
|
232
|
+
if kwargs.get('tmsProxy', None):
|
|
233
|
+
self.set_tms_proxy = kwargs.get('tmsProxy', None)
|
|
234
|
+
if kwargs.get('tmsTestRunName', None):
|
|
235
|
+
self.set_test_run_name = kwargs.get('tmsTestRunName', None)
|
|
236
|
+
if kwargs.get('tmsAdapterMode', None):
|
|
237
|
+
self.set_adapter_mode = kwargs.get('tmsAdapterMode', None)
|
|
238
|
+
if kwargs.get('tmsConfigFile', None):
|
|
239
|
+
self.set_config_file = kwargs.get('tmsConfigFile', None)
|
|
240
|
+
if kwargs.get('tmsCertValidation', None):
|
|
241
|
+
self.set_cert_validation = kwargs.get('tmsCertValidation', None)
|
|
242
|
+
if kwargs.get('tmsAutomaticCreationTestCases', None):
|
|
243
|
+
self.set_automatic_creation_test_cases = kwargs.get('tmsAutomaticCreationTestCases', None)
|
|
244
|
+
if kwargs.get('tmsAutomaticUpdationLinksToTestCases', None):
|
|
245
|
+
self.set_automatic_updation_links_to_test_cases = kwargs.get('tmsAutomaticUpdationLinksToTestCases', None)
|
|
246
|
+
if kwargs.get('tmsImportRealtime', None):
|
|
247
|
+
self.set_import_realtime = kwargs.get('tmsImportRealtime', None)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from testit_python_commons.models.step_result import StepResult
|
|
6
|
+
from testit_python_commons.models.test_result import TestResult
|
|
7
|
+
from testit_python_commons.models.outcome_type import OutcomeType
|
|
8
|
+
from testit_python_commons.models.status_type import StatusType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def convert_time(time):
|
|
12
|
+
date = datetime.strptime(time, "%Y%m%d %H:%M:%S.%f")
|
|
13
|
+
date = date.replace(tzinfo=datetime.now().astimezone().tzinfo)
|
|
14
|
+
return date
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def convert_executable_test_to_test_result_model(executable_test: dict) -> TestResult:
|
|
18
|
+
return TestResult()\
|
|
19
|
+
.set_external_id(executable_test['externalID'])\
|
|
20
|
+
.set_autotest_name(executable_test['autoTestName'])\
|
|
21
|
+
.set_step_results(
|
|
22
|
+
step_results_to_autotest_steps_model(executable_test['stepResults']))\
|
|
23
|
+
.set_setup_results(
|
|
24
|
+
step_results_to_autotest_steps_model(executable_test['setUpResults']))\
|
|
25
|
+
.set_teardown_results(
|
|
26
|
+
step_results_to_autotest_steps_model(executable_test['tearDownResults']))\
|
|
27
|
+
.set_duration(executable_test['duration'])\
|
|
28
|
+
.set_outcome(executable_test['outcome'])\
|
|
29
|
+
.set_status_type(executable_test['status_type'])\
|
|
30
|
+
.set_traces(executable_test['traces'])\
|
|
31
|
+
.set_attachments(executable_test['attachments'])\
|
|
32
|
+
.set_parameters(executable_test['parameters'])\
|
|
33
|
+
.set_properties(executable_test['properties'])\
|
|
34
|
+
.set_namespace(executable_test['namespace'])\
|
|
35
|
+
.set_classname(executable_test['classname'])\
|
|
36
|
+
.set_title(executable_test['title'])\
|
|
37
|
+
.set_description(executable_test['description'])\
|
|
38
|
+
.set_links(executable_test['links'])\
|
|
39
|
+
.set_result_links(executable_test['resultLinks'])\
|
|
40
|
+
.set_labels(executable_test['labels']) \
|
|
41
|
+
.set_tags(executable_test['tags'])\
|
|
42
|
+
.set_work_item_ids(executable_test['workItemsID'])\
|
|
43
|
+
.set_message(executable_test['message'])\
|
|
44
|
+
.set_external_key(executable_test['externalKey'])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def step_results_to_autotest_steps_model(step_results: dict) -> List[StepResult]:
|
|
48
|
+
autotest_model_steps = []
|
|
49
|
+
|
|
50
|
+
for step_result in step_results:
|
|
51
|
+
step_result_model = StepResult()\
|
|
52
|
+
.set_title(step_result['title'])\
|
|
53
|
+
.set_description(step_result['description'])\
|
|
54
|
+
.set_outcome(step_result['outcome'])\
|
|
55
|
+
.set_duration(step_result['duration'])\
|
|
56
|
+
.set_attachments(step_result['attachments'])
|
|
57
|
+
|
|
58
|
+
if 'parameters' in step_result:
|
|
59
|
+
step_result_model.set_parameters(step_result['parameters'])
|
|
60
|
+
|
|
61
|
+
if 'step_results' in step_result:
|
|
62
|
+
step_result_model.set_step_results(
|
|
63
|
+
step_results_to_autotest_steps_model(step_result['step_results']))
|
|
64
|
+
|
|
65
|
+
autotest_model_steps.append(step_result_model)
|
|
66
|
+
|
|
67
|
+
return autotest_model_steps
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_hash(value: str):
|
|
71
|
+
md = hashlib.sha256(bytes(value, encoding='utf-8'))
|
|
72
|
+
return md.hexdigest()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
STEP_STATUSES = {
|
|
76
|
+
'FAIL': OutcomeType.FAILED,
|
|
77
|
+
'PASS': OutcomeType.PASSED,
|
|
78
|
+
'SKIP': OutcomeType.SKIPPED,
|
|
79
|
+
'NOT RUN': OutcomeType.SKIPPED
|
|
80
|
+
}
|
|
81
|
+
STATUS_TYPES = {
|
|
82
|
+
'FAIL': StatusType.FAILED,
|
|
83
|
+
'PASS': StatusType.SUCCEEDED,
|
|
84
|
+
'SKIP': StatusType.INCOMPLETE,
|
|
85
|
+
'NOT RUN': StatusType.INCOMPLETE
|
|
86
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testit-adapter-robotframework
|
|
3
|
+
Version: 4.2.8
|
|
4
|
+
Summary: Robot Framework adapter for Test IT
|
|
5
|
+
Home-page: https://github.com/testit-tms/adapters-python/
|
|
6
|
+
Author: Integration team
|
|
7
|
+
Author-email: integrations@testit.software
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: attrs
|
|
19
|
+
Requires-Dist: robotframework
|
|
20
|
+
Requires-Dist: testit-python-commons==4.2.8
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Test IT TMS adapter for Robot Framework
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
[](https://pypi.python.org/pypi/testit-adapter-robotframework)
|
|
36
|
+
[](https://pypi.python.org/pypi/testit-adapter-robotframework)
|
|
37
|
+
[](https://github.com/testit-tms/adapters-python)
|
|
38
|
+
|
|
39
|
+
## Getting Started
|
|
40
|
+
|
|
41
|
+
### Installation
|
|
42
|
+
```
|
|
43
|
+
pip install testit-adapter-robotframework
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Configuration
|
|
49
|
+
|
|
50
|
+
| Description | File property | Environment variable | CLI argument |
|
|
51
|
+
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|--------------------------------------|
|
|
52
|
+
| Location of the TMS instance | url | TMS_URL | tmsUrl |
|
|
53
|
+
| API secret key [How to getting API secret key?](https://github.com/testit-tms/.github/tree/main/configuration#privatetoken) | privateToken | TMS_PRIVATE_TOKEN | tmsPrivateToken |
|
|
54
|
+
| ID of project in TMS instance [How to getting project ID?](https://github.com/testit-tms/.github/tree/main/configuration#projectid) | projectId | TMS_PROJECT_ID | tmsProjectId |
|
|
55
|
+
| ID of configuration in TMS instance [How to getting configuration ID?](https://github.com/testit-tms/.github/tree/main/configuration#configurationid) | configurationId | TMS_CONFIGURATION_ID | tmsConfigurationId |
|
|
56
|
+
| ID of the created test run in TMS instance.<br/>It's necessary for **adapterMode** 0 or 1 | testRunId | TMS_TEST_RUN_ID | tmsTestRunId |
|
|
57
|
+
| Parameter for specifying the name of test run in TMS instance (**It's optional**). If it is not provided, it is created automatically | testRunName | TMS_TEST_RUN_NAME | tmsTestRunName |
|
|
58
|
+
| Adapter mode. Default value - 0. The adapter supports following modes:<br/>0 - in this mode, the adapter filters tests by test run ID and configuration ID, and sends the results to the test run<br/>1 - in this mode, the adapter sends all results to the test run without filtering or [with filtering CLI](#run-with-filter)<br/>2 - in this mode, the adapter creates a new test run and sends results to the new test run | adapterMode | TMS_ADAPTER_MODE | tmsAdapterMode |
|
|
59
|
+
| It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION | tmsCertValidation |
|
|
60
|
+
| Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)<br/>false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases |
|
|
61
|
+
| Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will update links to test cases<br/>false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases |
|
|
62
|
+
| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:<br/>true - in this mode, the adapter will create/update each autotest in real time<br/>false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime |
|
|
63
|
+
| Url of proxy server (**It's optional**) | tmsProxy | TMS_PROXY | tmsProxy |
|
|
64
|
+
| Name (**including extension**) of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile |
|
|
65
|
+
| Sync storage port (**It's optional, 49152 by default**) | syncStoragePort | TMS_SYNC_STORAGE_PORT | syncStoragePort |
|
|
66
|
+
|
|
67
|
+
#### File
|
|
68
|
+
|
|
69
|
+
Add `[testit]` block to your **pyproject.toml** or create **connection_config.ini** file in the root directory of the project:
|
|
70
|
+
```
|
|
71
|
+
[testit]
|
|
72
|
+
URL = URL
|
|
73
|
+
privateToken = USER_PRIVATE_TOKEN
|
|
74
|
+
projectId = PROJECT_ID
|
|
75
|
+
configurationId = CONFIGURATION_ID
|
|
76
|
+
testRunId = TEST_RUN_ID
|
|
77
|
+
testRunName = TEST_RUN_NAME
|
|
78
|
+
adapterMode = ADAPTER_MODE
|
|
79
|
+
certValidation = CERT_VALIDATION
|
|
80
|
+
automaticCreationTestCases = AUTOMATIC_CREATION_TEST_CASES
|
|
81
|
+
automaticUpdationLinksToTestCases = AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES
|
|
82
|
+
importRealtime = IMPORT_REALTIME
|
|
83
|
+
|
|
84
|
+
# This section are optional. It enables debug mode.
|
|
85
|
+
[debug]
|
|
86
|
+
tmsProxy = TMS_PROXY
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Examples
|
|
90
|
+
|
|
91
|
+
Launch with a `pyproject.toml` or `connection_config.ini` file in the root directory of the project:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
$ robot -v testit TEST_DIRECTORY
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Launch with command-line parameters (parameters are case-insensitive):
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
$ robot -v testit -v tmsUrl:URL -v tmsPrivateToken:USER_PRIVATE_TOKEN -v tmsProjectId:PROJECT_ID -v tmsConfigurationId:CONFIGURATION_ID -v tmsTestRunId:TEST_RUN_ID -v tmsTestRunName:TEST_RUN_NAME -v tmsAdapterMode:ADAPTER_MODE -v tmsProxy:'{"http":"http://localhost:8888","https":"http://localhost:8888"}' -v tmsConfigFile:<optional file> -v tmsCertValidation:CERT_VALIDATION -v tmsAutomaticCreationTestCases:AUTOMATIC_CREATION_TEST_CASES -v tmsAutomaticUpdationLinksToTestCases:AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES -v tmsImportRealtime:IMPORT_REALTIME TEST_DIRECTORY
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If you want to enable debug mode then see [How to enable debug logging?](https://github.com/testit-tms/adapters-python/tree/main/testit-python-commons)
|
|
104
|
+
|
|
105
|
+
#### Run with filter
|
|
106
|
+
To create filter by autotests you can use the Test IT CLI (use adapterMode 1 for run with filter):
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
$ export TMS_TOKEN=<YOUR_TOKEN>
|
|
110
|
+
$ testit autotests_filter
|
|
111
|
+
--url https://tms.testit.software \
|
|
112
|
+
--configuration-id 5236eb3f-7c05-46f9-a609-dc0278896464 \
|
|
113
|
+
--testrun-id 6d4ac4b7-dd67-4805-b879-18da0b89d4a8 \
|
|
114
|
+
--framework robotframework \
|
|
115
|
+
--output tmp/filter.txt
|
|
116
|
+
|
|
117
|
+
$ robot -v testit -v tmsTestRunId:6d4ac4b7-dd67-4805-b879-18da0b89d4a8 -v tmsAdapterMode:1 $(cat tmp/filter.txt) TEST_DIRECTORY
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Tags
|
|
121
|
+
|
|
122
|
+
Tags can be used to specify information about autotest. Tags are space sensitive, use only one space between words.
|
|
123
|
+
|
|
124
|
+
Description of tags:
|
|
125
|
+
- `testit.workItemsId` - a method that links autotests with manual tests. Receives the array of manual tests' IDs
|
|
126
|
+
- `testit.displayName` - internal autotest name (used in Test IT)
|
|
127
|
+
- `testit.externalId` - unique internal autotest ID (used in Test IT)
|
|
128
|
+
- `testit.title` - autotest name specified in the autotest card. If not specified, the name from the displayName method is used
|
|
129
|
+
- `testit.description` - autotest description specified in the autotest card
|
|
130
|
+
- `testit.links` - links listed in the autotest card
|
|
131
|
+
- `testit.labels` - labels listed in the autotest card
|
|
132
|
+
- `testit.tags` - tags listed in the autotest card
|
|
133
|
+
- `testit.nameSpace` - directory in the TMS system (default - file's name of test)
|
|
134
|
+
- `testit.className` - subdirectory in the TMS system (default - class's name of test)
|
|
135
|
+
|
|
136
|
+
Description of scenario methods:
|
|
137
|
+
- `Add Links` - links in the autotest result
|
|
138
|
+
- `Add Link` - add one link in the autotest result
|
|
139
|
+
- `Add Attachments` - uploading files in the autotest result
|
|
140
|
+
- `Add Attachment` - upload given content with given filename in the autotest result
|
|
141
|
+
- `Add Message` - information about autotest in the autotest result
|
|
142
|
+
|
|
143
|
+
Description of methods:
|
|
144
|
+
- `testit.addWorkItemIds` - a dynamic method that links autotests with manual tests. Receives the array of manual tests' IDs
|
|
145
|
+
- `testit.addDisplayName` - a dynamic method for adding internal autotest name (used in Test IT)
|
|
146
|
+
- `testit.addExternalId` - a dynamic method for adding unique internal autotest ID (used in Test IT)
|
|
147
|
+
- `testit.addTitle` - a dynamic method for adding autotest name specified in the autotest card. If not specified, the name from the displayName method is used
|
|
148
|
+
- `testit.addDescription` - a dynamic method for adding autotest description specified in the autotest card
|
|
149
|
+
- `testit.addLabels` - a dynamic method for adding labels listed in the autotest card
|
|
150
|
+
- `testit.addTags` - a dynamic method for adding tags listed in the autotest card
|
|
151
|
+
- `testit.addLinks` - links in the autotest result
|
|
152
|
+
- `testit.addAttachments` - uploading files in the autotest result
|
|
153
|
+
- `testit.addMessage` - information about autotest in the autotest result
|
|
154
|
+
- `testit.addNameSpace` - a dynamic method for adding directory in the TMS system (default - file's name of test)
|
|
155
|
+
- `testit.addClassName` - a dynamic method for adding subdirectory in the TMS system (default - class's name of test)
|
|
156
|
+
- `testit.addParameter` - a dynamic method for adding parameter in the autotest result
|
|
157
|
+
- `testit.step` - usage in the "with" construct to designation a step in the body of the test
|
|
158
|
+
|
|
159
|
+
### Parallel execution
|
|
160
|
+
|
|
161
|
+
You can also run your test in parallel with [Pabot](https://pabot.org/).
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
$ pabot --pabotlib -v testit <test directory>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
All other settings are the same as for standard execution.
|
|
168
|
+
|
|
169
|
+
### Examples
|
|
170
|
+
|
|
171
|
+
```robotframework
|
|
172
|
+
*** Settings ***
|
|
173
|
+
Documentation Main Suite with examples
|
|
174
|
+
Library testit_adapter_robotframework.TMSLibrary
|
|
175
|
+
|
|
176
|
+
*** Variables ***
|
|
177
|
+
&{SIMPLE_LINK} url=http://google.com
|
|
178
|
+
&{FULL_LINK} url=http://google.co.uk title=Google type=Related description=just a link
|
|
179
|
+
|
|
180
|
+
@{LINKS} ${SIMPLE_LINK} ${FULL_LINK}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
*** Test Cases ***
|
|
184
|
+
Simple Test
|
|
185
|
+
[Setup] Setup
|
|
186
|
+
Do Something
|
|
187
|
+
Do Another Thing
|
|
188
|
+
Log I'am a step
|
|
189
|
+
[Teardown] Teardown
|
|
190
|
+
|
|
191
|
+
Simple Test with link as variable
|
|
192
|
+
[Tags] testit.links:${SIMPLE_LINK}
|
|
193
|
+
[Setup] Setup
|
|
194
|
+
Do Something
|
|
195
|
+
Do Another Thing
|
|
196
|
+
Log I'am a step
|
|
197
|
+
[Teardown] Teardown
|
|
198
|
+
|
|
199
|
+
Simple Test with link as dict
|
|
200
|
+
[Tags] testit.links:${{{'url': 'http://google.com', 'type':'Issue'}}}
|
|
201
|
+
[Setup] Setup
|
|
202
|
+
Do Something
|
|
203
|
+
Do Another Thing
|
|
204
|
+
Log I'am a step
|
|
205
|
+
[Teardown] Teardown
|
|
206
|
+
|
|
207
|
+
Simple Test with WorkitemId as string
|
|
208
|
+
[Tags] testit.workitemsID:123
|
|
209
|
+
[Setup] Setup
|
|
210
|
+
Do Something
|
|
211
|
+
Do Another Thing
|
|
212
|
+
Log I'am a step
|
|
213
|
+
[Teardown] Teardown
|
|
214
|
+
|
|
215
|
+
Simple Test with WorkitemId as list
|
|
216
|
+
[Tags] testit.workitemsID:${{[123, '456']}}
|
|
217
|
+
[Setup] Setup
|
|
218
|
+
Do Something
|
|
219
|
+
Do Another Thing
|
|
220
|
+
Log I'am a step
|
|
221
|
+
[Teardown] Teardown
|
|
222
|
+
|
|
223
|
+
Simple Test with Title or Description or DisplayName with simple formatting
|
|
224
|
+
[Documentation] Tags are space sensitive, use only one space between words
|
|
225
|
+
[Tags] testit.displayName:This works testit.title:'This also works'
|
|
226
|
+
... testit.description:"This works too"
|
|
227
|
+
[Setup] Setup
|
|
228
|
+
Do Something
|
|
229
|
+
Do Another Thing
|
|
230
|
+
Log I'am a step
|
|
231
|
+
[Teardown] Teardown
|
|
232
|
+
|
|
233
|
+
Test With All Params
|
|
234
|
+
[Documentation] It's better to use this kind of formatting for different data types in tags
|
|
235
|
+
[Tags] testit.externalID:123 testit.title:${{'Different title'}} testit.displayName:${{'Different name'}}
|
|
236
|
+
... testit.description:${{'Different description'}} testit.workitemsID:${{[123, '456']}}
|
|
237
|
+
... testit.links:${{{'url': 'http://google.com', 'type':'Issue'}}} testit.labels:${{['smoke', 'lol']}}
|
|
238
|
+
[Setup] Setup
|
|
239
|
+
Log Something
|
|
240
|
+
Log Another
|
|
241
|
+
[Teardown] Teardown
|
|
242
|
+
|
|
243
|
+
Test With Add Link
|
|
244
|
+
[Setup] Setup
|
|
245
|
+
Do Something
|
|
246
|
+
Do Another Thing
|
|
247
|
+
Add Links @{LINKS}
|
|
248
|
+
# You can also add one link (default type is Defect)
|
|
249
|
+
Add Link http://ya.ru
|
|
250
|
+
Add Link http://ya.ru type=Issue
|
|
251
|
+
Add Link ${SIMPLE_LINK}[url]
|
|
252
|
+
[Teardown] Teardown
|
|
253
|
+
|
|
254
|
+
Test With Add Attachment
|
|
255
|
+
[Setup] Setup
|
|
256
|
+
Do Something
|
|
257
|
+
Do Another Thing
|
|
258
|
+
${V} Get Variables
|
|
259
|
+
Add Attachment '${V}' file.txt
|
|
260
|
+
Add Attachments images/banner.png images/icon.png
|
|
261
|
+
[Teardown] Teardown
|
|
262
|
+
|
|
263
|
+
Test With Add Message
|
|
264
|
+
[Setup] Setup
|
|
265
|
+
Do Something
|
|
266
|
+
Do Another Thing
|
|
267
|
+
Add Message Wow, it's my error message!
|
|
268
|
+
Fail
|
|
269
|
+
[Teardown] Teardown
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Parameterized test
|
|
273
|
+
|
|
274
|
+
> [!WARNING]
|
|
275
|
+
> When linking a parameterized autotest to a parameterized test case, please consider the problematic points:
|
|
276
|
+
> - In TMS test cases have a table with parameters, but autotests do not. They are not equal entities, so there may be incompatibility in terms of parameters
|
|
277
|
+
> - Running a parameterized test case, TMS expects the results of all related autotests with all the parameters specified in the test case table
|
|
278
|
+
> - In TMS, the parameters are limited to the string type, so the adapter transmits absolutely all the autotest parameters as a string. This implies the following problematic point for the test case table
|
|
279
|
+
> - TMS expects a complete **textual** match of the name and value of the parameters of the test case table with the autotest parameters
|
|
280
|
+
|
|
281
|
+
```robotframework
|
|
282
|
+
*** Variables ***
|
|
283
|
+
${NUMBER} 1
|
|
284
|
+
${VALUE} String01
|
|
285
|
+
|
|
286
|
+
Parametrized test success
|
|
287
|
+
[Tags] testit.externalID:parametrized_test_${NUMBER}_${VALUE}_success
|
|
288
|
+
... testit.displayName:parametrized_test_${NUMBER}_${VALUE}_success_display_name
|
|
289
|
+
... testit.title:parametrized_test_${NUMBER}_${VALUE}_success_title
|
|
290
|
+
... testit.description:parametrized_test_${NUMBER}_${VALUE}_success
|
|
291
|
+
Get Parameters ${NUMBER} ${VALUE}
|
|
292
|
+
Return True
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
# Contributing
|
|
296
|
+
|
|
297
|
+
You can help to develop the project. Any contributions are **greatly appreciated**.
|
|
298
|
+
|
|
299
|
+
* If you have suggestions for adding or removing projects, feel free to [open an issue](https://github.com/testit-tms/adapters-python/issues/new) to discuss it, or directly create a pull request after you edit the *README.md* file with necessary changes.
|
|
300
|
+
* Please make sure you check your spelling and grammar.
|
|
301
|
+
* Create individual PR for each suggestion.
|
|
302
|
+
* Please also read through the [Code Of Conduct](https://github.com/testit-tms/adapters-python/blob/master/CODE_OF_CONDUCT.md) before posting your first idea as well.
|
|
303
|
+
|
|
304
|
+
# License
|
|
305
|
+
|
|
306
|
+
Distributed under the Apache-2.0 License. See [LICENSE](https://github.com/testit-tms/adapters-python/blob/master/LICENSE.md) for more information.
|
|
307
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
testit_adapter_robotframework/TMSLibrary.py,sha256=Z6W3FnqvTKDECBBHC1burtk8OlcqOhUl1HcwTpia01o,4423
|
|
2
|
+
testit_adapter_robotframework/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
testit_adapter_robotframework/listeners.py,sha256=v1oEugbngd4SdEX7Gj14CFFBa9sf9s_g73kyxLqwa-g,4534
|
|
4
|
+
testit_adapter_robotframework/models.py,sha256=789WfFloUX_rdqZtSCfwJvHxprixcSPMzaiGQoXCUo0,10478
|
|
5
|
+
testit_adapter_robotframework/utils.py,sha256=i401vds352xEwMIeq3RGwjUVcD841qyrq32oITTsyOI,3275
|
|
6
|
+
testit_adapter_robotframework-4.2.8.dist-info/METADATA,sha256=YU6Ko5mWyzkC0fPBMQkPCRcnTqS1pwb4u2bRqLBpRTk,20309
|
|
7
|
+
testit_adapter_robotframework-4.2.8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
testit_adapter_robotframework-4.2.8.dist-info/top_level.txt,sha256=gvzgvq2_M-2bTCANQrOt-I41giIyCvI1n1OispwHg3g,30
|
|
9
|
+
testit_adapter_robotframework-4.2.8.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
testit_adapter_robotframework
|