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.
@@ -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
+ ![Test IT](https://raw.githubusercontent.com/testit-tms/adapters-python/master/images/banner.png)
33
+
34
+ [![Release
35
+ Status](https://img.shields.io/pypi/v/testit-adapter-robotframework?style=plastic)](https://pypi.python.org/pypi/testit-adapter-robotframework)
36
+ [![Downloads](https://img.shields.io/pypi/dm/testit-adapter-robotframework?style=plastic)](https://pypi.python.org/pypi/testit-adapter-robotframework)
37
+ [![GitHub contributors](https://img.shields.io/github/contributors/testit-tms/adapters-python?style=plastic)](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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ testit_adapter_robotframework