testit-adapter-nose 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.
File without changes
@@ -0,0 +1,74 @@
1
+ import testit_python_commons.services as adapter
2
+ from testit_python_commons.services import (
3
+ AdapterManager,
4
+ StepManager)
5
+ from .utils import (
6
+ form_test,
7
+ get_outcome,
8
+ convert_executable_test_to_test_result_model,
9
+ STATUS_TYPE,
10
+ )
11
+
12
+
13
+ class AdapterListener(object):
14
+ __executable_test = None
15
+
16
+ def __init__(self, adapter_manager: AdapterManager, step_manager: StepManager, top_level_directory: str):
17
+ self.__adapter_manager = adapter_manager
18
+ self.__step_manager = step_manager
19
+ self.__top_level_directory = top_level_directory
20
+
21
+ def start_launch(self):
22
+ test_run_id = self.__adapter_manager.get_test_run_id()
23
+
24
+ self.__adapter_manager.set_test_run_id(test_run_id)
25
+ self.__adapter_manager.on_running_started()
26
+
27
+ def stop_launch(self):
28
+ self.__adapter_manager.on_block_completed()
29
+ self.__adapter_manager.write_tests()
30
+
31
+ def get_tests_for_launch(self):
32
+ self.__adapter_manager.on_running_started()
33
+ return self.__adapter_manager.get_autotests_for_launch()
34
+
35
+ def start_test(self, test):
36
+ self.__executable_test = form_test(test, self.__top_level_directory)
37
+
38
+ def set_outcome(self, event):
39
+ outcome, message, trace = get_outcome(event)
40
+
41
+ self.__executable_test['outcome'] = outcome
42
+ self.__executable_test['status_type'] = STATUS_TYPE[outcome]
43
+ self.__executable_test['traces'] = trace
44
+
45
+ if not self.__executable_test['message']:
46
+ self.__executable_test['message'] = message
47
+
48
+ def stop_test(self):
49
+ test_results_steps = self.__step_manager.get_steps_tree()
50
+ self.__executable_test['stepResults'] = test_results_steps
51
+
52
+ self.__adapter_manager.write_test(
53
+ convert_executable_test_to_test_result_model(self.__executable_test))
54
+ self.__executable_test = None
55
+
56
+ @adapter.hookimpl
57
+ def add_link(self, link):
58
+ if self.__executable_test:
59
+ self.__executable_test['resultLinks'].append(link)
60
+
61
+ @adapter.hookimpl
62
+ def add_message(self, test_message):
63
+ if self.__executable_test:
64
+ self.__executable_test['message'] = str(test_message)
65
+
66
+ @adapter.hookimpl
67
+ def add_attachments(self, attach_paths: list or tuple):
68
+ if self.__executable_test:
69
+ self.__executable_test['attachments'] += self.__adapter_manager.load_attachments(attach_paths)
70
+
71
+ @adapter.hookimpl
72
+ def create_attachment(self, body, name: str):
73
+ if self.__executable_test:
74
+ self.__executable_test['attachments'] += self.__adapter_manager.create_attachment(body, name)
@@ -0,0 +1,43 @@
1
+ from nose2.events import Plugin
2
+
3
+ from testit_python_commons.services import TmsPluginManager
4
+
5
+ from .listener import AdapterListener
6
+
7
+
8
+ class TmsPlugin(Plugin):
9
+ configSection = 'testit'
10
+ commandLineSwitch = (None, 'testit', 'TMS adapter for Nose')
11
+ __listener = None
12
+ __tests_for_launch = None
13
+ __top_level_directory = None
14
+
15
+ def __init__(self, *args, **kwargs):
16
+ super(TmsPlugin, self).__init__(*args, **kwargs)
17
+
18
+ def handleDir(self, event):
19
+ if not self.__top_level_directory:
20
+ self.__top_level_directory = event.topLevelDirectory
21
+
22
+ def startTestRun(self, event):
23
+ self.__listener = AdapterListener(
24
+ TmsPluginManager.get_adapter_manager(),
25
+ TmsPluginManager.get_step_manager(),
26
+ self.__top_level_directory)
27
+
28
+ TmsPluginManager.get_plugin_manager().register(self.__listener)
29
+
30
+ self.__listener.start_launch()
31
+ self.__tests_for_launch = self.__listener.get_tests_for_launch()
32
+
33
+ def afterTestRun(self, event):
34
+ self.__listener.stop_launch()
35
+
36
+ def startTest(self, event):
37
+ self.__listener.start_test(event.test)
38
+
39
+ def stopTest(self, event):
40
+ self.__listener.stop_test()
41
+
42
+ def testOutcome(self, event):
43
+ self.__listener.set_outcome(event)
@@ -0,0 +1,410 @@
1
+ import hashlib
2
+ import logging
3
+ import re
4
+ import os
5
+ from typing import List
6
+ from traceback import format_exception_only
7
+ from nose2 import (
8
+ util,
9
+ result
10
+ )
11
+ import inspect
12
+
13
+ from testit_python_commons.models.link import Link
14
+ from testit_python_commons.models.link_type import LinkType
15
+ from testit_python_commons.models.test_result import TestResult
16
+ from testit_python_commons.models.status_type import StatusType
17
+
18
+
19
+ __ARRAY_TYPES = (frozenset, list, set, tuple,)
20
+ STATUS_TYPE = {
21
+ result.PASS: StatusType.SUCCEEDED,
22
+ result.FAIL: StatusType.FAILED,
23
+ result.ERROR: StatusType.FAILED,
24
+ result.SKIP: StatusType.INCOMPLETE,
25
+ }
26
+
27
+ def status_details(event):
28
+ message, trace = None, None
29
+ if event.exc_info:
30
+ exc_type, value, _ = event.exc_info
31
+ message = '\n'.join(format_exception_only(exc_type, value)) if exc_type or value else None
32
+ trace = ''.join(util.exc_info_to_string(event.exc_info, event.test))
33
+ elif event.reason:
34
+ message = event.reason
35
+
36
+ return message, trace
37
+
38
+
39
+ def get_outcome(event):
40
+ outcome = event.outcome
41
+ message = None
42
+ trace = None
43
+
44
+ if outcome == result.PASS and not event.expected:
45
+ message = "test passes unexpectedly"
46
+ elif outcome == result.FAIL and not event.expected:
47
+ message, trace = status_details(event)
48
+ elif outcome == result.ERROR:
49
+ message, trace = status_details(event)
50
+ elif outcome == result.SKIP:
51
+ message, trace = status_details(event)
52
+
53
+ return outcome, message, trace
54
+
55
+
56
+ def form_test(item, top_level_directory):
57
+ if hasattr(item, '_testFunc'):
58
+ function = item._testFunc
59
+ elif hasattr(item, '_testMethodName'):
60
+ function = getattr(item.__class__, item._testMethodName)
61
+ else:
62
+ raise Exception('')
63
+
64
+ return {
65
+ 'externalID': __get_external_id_from(item, function),
66
+ 'autoTestName': __get_display_name_from(item, function),
67
+ 'steps': [],
68
+ 'stepResults': [],
69
+ 'setUp': [],
70
+ 'setUpResults': [],
71
+ 'tearDown': [],
72
+ 'tearDownResults': [],
73
+ 'resultLinks': [],
74
+ 'duration': 0,
75
+ 'outcome': None,
76
+ 'status_type': None,
77
+ 'failureReasonName': None,
78
+ 'traces': None,
79
+ 'attachments': [],
80
+ 'parameters': get_all_parameters(item),
81
+ 'properties': __get_properties_from(function),
82
+ 'namespace': __get_namespace_from(item, function),
83
+ 'classname': __get_class_name_from(item, function),
84
+ 'title': __get_title_from(item, function),
85
+ 'description': __get_description_from(item, function),
86
+ 'links': __get_links_from(item, function),
87
+ 'labels': __get_labels_from(item, function),
88
+ 'tags': __get_tags_from(item, function),
89
+ 'workItemsID': __get_work_item_ids_from(item, function),
90
+ 'message': None,
91
+ 'externalKey': __get_fullname(function, top_level_directory)
92
+ }
93
+
94
+
95
+ def __get_display_name_from(item, function):
96
+ display_name = __search_attribute(function, 'test_displayname')
97
+
98
+ if not display_name:
99
+ return function.__doc__ if \
100
+ function.__doc__ else function.__name__
101
+
102
+ return collect_parameters_in_string_attribute(display_name, get_all_parameters(item))
103
+
104
+
105
+ def __get_external_id_from(item, function):
106
+ external_id = __search_attribute(function, 'test_external_id')
107
+
108
+ if not external_id:
109
+ return get_hash(function.__qualname__ + function.__name__)
110
+
111
+ return collect_parameters_in_string_attribute(external_id, get_all_parameters(item))
112
+
113
+
114
+ def __get_title_from(item, function):
115
+ title = __search_attribute(function, 'test_title')
116
+
117
+ if not title:
118
+ return None
119
+
120
+ return collect_parameters_in_string_attribute(title, get_all_parameters(item))
121
+
122
+
123
+ def __get_description_from(item, function):
124
+ description = __search_attribute(function, 'test_description')
125
+
126
+ if not description:
127
+ return None
128
+
129
+ return collect_parameters_in_string_attribute(description, get_all_parameters(item))
130
+
131
+
132
+ def __get_namespace_from(item, function):
133
+ namespace = __search_attribute(function, 'test_namespace')
134
+
135
+ if not namespace:
136
+ return function.__module__
137
+
138
+ return collect_parameters_in_string_attribute(namespace, get_all_parameters(item))
139
+
140
+
141
+ def __get_class_name_from(item, function):
142
+ class_name = __search_attribute(function, 'test_classname')
143
+
144
+ if not class_name:
145
+ i = function.__qualname__.find('.')
146
+
147
+ if i != -1:
148
+ return function.__qualname__[:i]
149
+
150
+ return None
151
+
152
+ return collect_parameters_in_string_attribute(class_name, get_all_parameters(item))
153
+
154
+
155
+ def __get_links_from(item, function):
156
+ links = __search_attribute(function, 'test_links')
157
+
158
+ if not links:
159
+ return []
160
+
161
+ return __set_parameters_to_links(links, get_all_parameters(item))
162
+
163
+
164
+ def __get_properties_from(function):
165
+ return __search_attribute(function, 'test_properties')
166
+
167
+
168
+ def __set_parameters_to_links(links, all_parameters):
169
+ if not all_parameters:
170
+ return links
171
+
172
+ links_with_parameters = []
173
+
174
+ for link in links:
175
+ links_with_parameters.append(
176
+ Link()
177
+ .set_url(
178
+ collect_parameters_in_string_attribute(
179
+ link.get_url(),
180
+ all_parameters))
181
+ .set_title(
182
+ collect_parameters_in_string_attribute(
183
+ link.get_title(),
184
+ all_parameters) if link.get_title() else None)
185
+ .set_link_type(
186
+ collect_parameters_in_string_attribute(
187
+ link.get_link_type(),
188
+ all_parameters) if link.get_link_type() else LinkType.RELATED)
189
+ .set_description(
190
+ collect_parameters_in_string_attribute(
191
+ link.get_description(),
192
+ all_parameters) if link.get_description() else None))
193
+
194
+ return links_with_parameters
195
+
196
+
197
+ def __get_labels_from(item, function) -> List[dict]:
198
+ test_labels = __search_attribute(function, 'test_labels')
199
+
200
+ if not test_labels:
201
+ return []
202
+
203
+ labels = []
204
+
205
+ for label in test_labels:
206
+ result = collect_parameters_in_mass_attribute(
207
+ label,
208
+ get_all_parameters(item))
209
+
210
+ if isinstance(result, __ARRAY_TYPES):
211
+ for l in result:
212
+ labels.append({
213
+ 'name': str(l)
214
+ })
215
+ else:
216
+ labels.append({
217
+ 'name': str(result)
218
+ })
219
+
220
+ return labels
221
+
222
+
223
+ def __get_tags_from(item, function) -> List[str]:
224
+ test_tags = __search_attribute(function, 'test_tags')
225
+
226
+ if not test_tags:
227
+ return []
228
+
229
+ tags = []
230
+
231
+ for tag in test_tags:
232
+ result = collect_parameters_in_mass_attribute(
233
+ tag,
234
+ get_all_parameters(item))
235
+
236
+ if isinstance(result, __ARRAY_TYPES):
237
+ for t in result:
238
+ tags.append(str(t))
239
+ else:
240
+ tags.append(str(result))
241
+
242
+ return tags
243
+
244
+
245
+ def __get_work_item_ids_from(item, function):
246
+ test_workitems_id = __search_attribute(function, 'test_workitems_id')
247
+
248
+ if not test_workitems_id:
249
+ return []
250
+
251
+ all_parameters = get_all_parameters(item)
252
+
253
+ if not all_parameters:
254
+ return test_workitems_id
255
+
256
+ result = collect_parameters_in_mass_attribute(test_workitems_id[0], all_parameters)
257
+
258
+ return map(str, result) if isinstance(result, __ARRAY_TYPES) else [str(result)]
259
+
260
+
261
+ def __get_fullname(function, top_level_directory: str):
262
+ module_file_name = __get_module_file_name_by_test_function(function)
263
+
264
+ if not module_file_name:
265
+ return __join_nose_test_node([function.__module__, function.__qualname__])
266
+
267
+ absolute_module_path = __get_absolute_module_path_by_file_name(module_file_name)
268
+ module_node = __convert_absolute_module_path_to_nose_module_node(absolute_module_path, top_level_directory)
269
+
270
+ return __join_nose_test_node([module_node, function.__module__, function.__qualname__])
271
+
272
+
273
+ def __get_module_file_name_by_test_function(test_function):
274
+ return __import__(test_function.__module__).__file__
275
+
276
+
277
+ def __get_absolute_module_path_by_file_name(module_file_name: str):
278
+ return os.path.dirname(module_file_name)
279
+
280
+
281
+ def __convert_absolute_module_path_to_nose_module_node(absolute_module_path: str, top_level_directory: str):
282
+ directories_in_project = absolute_module_path.replace(top_level_directory + os.sep, '')
283
+
284
+ return directories_in_project.replace(os.sep, '.')
285
+
286
+
287
+ def __join_nose_test_node(test_node_parts: List[str]):
288
+ return ".".join(test_node_parts)
289
+
290
+
291
+ def __search_attribute(function, attribute):
292
+ if hasattr(function, attribute):
293
+ return getattr(function, attribute)
294
+
295
+ return
296
+
297
+
298
+ def get_all_parameters(item):
299
+ def _params(names, values):
300
+ return {name: str(value) for name, value in zip(names, values)}
301
+
302
+ test_id = item.id()
303
+
304
+ if len(test_id.split("\n")) > 1:
305
+ if hasattr(item, "_testFunc"):
306
+ wrapper_arg_spec = inspect.getfullargspec(item._testFunc)
307
+ arg_set, obj = wrapper_arg_spec.defaults
308
+ args = inspect.signature(obj).parameters.keys()
309
+
310
+ return _params(args, arg_set)
311
+ elif hasattr(item, "_testMethodName"):
312
+ method = getattr(item, item._testMethodName)
313
+ wrapper_arg_spec = inspect.getfullargspec(method)
314
+ obj, arg_set = wrapper_arg_spec.defaults
315
+ test_arg_spec = inspect.getfullargspec(obj)
316
+ args = test_arg_spec.args
317
+
318
+ return _params(args[1:], arg_set)
319
+
320
+
321
+ def collect_parameters_in_string_attribute(attribute, all_parameters):
322
+ param_keys = re.findall(r"\{(.*?)\}", attribute)
323
+
324
+ if len(param_keys) > 0:
325
+ for param_key in param_keys:
326
+ parameter = get_parameter(param_key, all_parameters)
327
+
328
+ if parameter is not None:
329
+ attribute = attribute.replace("{" + param_key + "}", str(parameter))
330
+
331
+ return attribute
332
+
333
+
334
+ def collect_parameters_in_mass_attribute(attribute, all_parameters):
335
+ param_keys = re.findall(r"\{(.*?)\}", attribute)
336
+
337
+ if len(param_keys) == 1:
338
+ parameter = get_parameter(param_keys[0], all_parameters)
339
+
340
+ if parameter is not None:
341
+ return parameter
342
+
343
+ if len(param_keys) > 1:
344
+ logging.error(f'(For type tuple, list, set) support only one key!')
345
+
346
+ return attribute
347
+
348
+
349
+ def get_parameter(key_for_parameter, all_parameters):
350
+ id_keys_in_parameter = re.findall(r'\[(.*?)\]', key_for_parameter)
351
+
352
+ if len(id_keys_in_parameter) > 1:
353
+ logging.error("(For type tuple, list, set, dict) support only one level!")
354
+
355
+ return
356
+
357
+ if len(id_keys_in_parameter) == 0:
358
+ if key_for_parameter not in all_parameters:
359
+ logging.error(f"Key for parameter {key_for_parameter} not found")
360
+
361
+ return
362
+
363
+ return all_parameters[key_for_parameter]
364
+
365
+ parameter_key = key_for_parameter.replace("[" + id_keys_in_parameter[0] + "]", "")
366
+ id_key_in_parameter = id_keys_in_parameter[0].strip("\'\"")
367
+
368
+ nested = all_parameters.get(parameter_key)
369
+
370
+ if id_key_in_parameter.isdigit() and isinstance(nested, (list, tuple)):
371
+ index = int(id_key_in_parameter)
372
+ if index in range(len(nested)):
373
+ return nested[index]
374
+
375
+ if isinstance(nested, dict) and id_key_in_parameter in nested:
376
+ return nested[id_key_in_parameter]
377
+
378
+ logging.error(f"Not key: {key_for_parameter} in run parameters or other keys problem")
379
+
380
+
381
+ def get_hash(value: str):
382
+ md = hashlib.sha256(bytes(value, encoding='utf-8'))
383
+ return md.hexdigest()
384
+
385
+
386
+ def convert_executable_test_to_test_result_model(executable_test: dict) -> TestResult:
387
+ return TestResult()\
388
+ .set_external_id(executable_test['externalID'])\
389
+ .set_autotest_name(executable_test['autoTestName'])\
390
+ .set_step_results(executable_test['stepResults'])\
391
+ .set_setup_results(executable_test['setUpResults'])\
392
+ .set_teardown_results(executable_test['tearDownResults'])\
393
+ .set_duration(executable_test['duration'])\
394
+ .set_outcome(executable_test['outcome'])\
395
+ .set_status_type(executable_test['status_type'])\
396
+ .set_traces(executable_test['traces'])\
397
+ .set_attachments(executable_test['attachments'])\
398
+ .set_parameters(executable_test['parameters'])\
399
+ .set_properties(executable_test['properties'])\
400
+ .set_namespace(executable_test['namespace'])\
401
+ .set_classname(executable_test['classname'])\
402
+ .set_title(executable_test['title'])\
403
+ .set_description(executable_test['description'])\
404
+ .set_links(executable_test['links'])\
405
+ .set_result_links(executable_test['resultLinks'])\
406
+ .set_labels(executable_test['labels'])\
407
+ .set_tags(executable_test['tags'])\
408
+ .set_work_item_ids(executable_test['workItemsID'])\
409
+ .set_message(executable_test['message'])\
410
+ .set_external_key(executable_test['externalKey'])
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: testit-adapter-nose
3
+ Version: 4.2.8
4
+ Summary: Nose 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: nose2
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 Nose
32
+
33
+ ![Test IT](https://raw.githubusercontent.com/testit-tms/adapters-python/master/images/banner.png)
34
+
35
+ [![Release
36
+ Status](https://img.shields.io/pypi/v/testit-adapter-nose?style=plastic)](https://pypi.python.org/pypi/testit-adapter-nose)
37
+ [![Downloads](https://img.shields.io/pypi/dm/testit-adapter-nose?style=plastic)](https://pypi.python.org/pypi/testit-adapter-nose)
38
+ [![GitHub contributors](https://img.shields.io/github/contributors/testit-tms/adapters-python?style=plastic)](https://github.com/testit-tms/adapters-python)
39
+
40
+ ## Getting Started
41
+
42
+ ### Installation
43
+
44
+ ```
45
+ pip install testit-adapter-nose
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ### Configuration
51
+
52
+ | Description | File property | Environment variable |
53
+ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|
54
+ | Location of the TMS instance | url | TMS_URL |
55
+ | API secret key [How to getting API secret key?](https://github.com/testit-tms/.github/tree/main/configuration#privatetoken) | privateToken | TMS_PRIVATE_TOKEN |
56
+ | 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 |
57
+ | 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 |
58
+ | ID of the created test run in TMS instance.<br/>It's necessary for **adapterMode** 1 | testRunId | TMS_TEST_RUN_ID |
59
+ | 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 |
60
+ | Adapter mode. Default value - 1. The adapter supports following modes:<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 |
61
+ | It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION |
62
+ | 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 |
63
+ | 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 |
64
+ | 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 |
65
+ | Url of proxy server (**It's optional**) | tmsProxy | TMS_PROXY |
66
+ | Name (**including extension**) of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE |
67
+ | Sync storage port (**It's optional, 49152 by default**) | syncStoragePort | TMS_SYNC_STORAGE_PORT |
68
+
69
+ #### File
70
+
71
+ Add `[testit]` block to your **pyproject.toml** or create **connection_config.ini** file in the root directory of the project:
72
+ ```
73
+ [testit]
74
+ URL = URL
75
+ privateToken = USER_PRIVATE_TOKEN
76
+ projectId = PROJECT_ID
77
+ configurationId = CONFIGURATION_ID
78
+ testRunId = TEST_RUN_ID
79
+ testRunName = TEST_RUN_NAME
80
+ adapterMode = ADAPTER_MODE
81
+ certValidation = CERT_VALIDATION
82
+ automaticCreationTestCases = AUTOMATIC_CREATION_TEST_CASES
83
+ automaticUpdationLinksToTestCases = AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES
84
+ importRealtime = IMPORT_REALTIME
85
+
86
+ # This section are optional. It enables debug mode.
87
+ [debug]
88
+ tmsProxy = TMS_PROXY
89
+ ```
90
+
91
+ #### Examples
92
+
93
+ Launch with a `pyproject.toml` or `connection_config.ini` file in the root directory of the project:
94
+
95
+ ```
96
+ $ nose2 --testit
97
+ ```
98
+
99
+ If you want to enable debug mode then
100
+ see [How to enable debug logging?](https://github.com/testit-tms/adapters-python/tree/main/testit-python-commons)
101
+
102
+ #### Run with filter
103
+ To create filter by autotests you can use the Test IT CLI (use adapterMode 1 for run with filter):
104
+
105
+ ```
106
+ $ export TMS_TOKEN=<YOUR_TOKEN>
107
+ $ testit autotests_filter
108
+ --url https://tms.testit.software \
109
+ --configuration-id 5236eb3f-7c05-46f9-a609-dc0278896464 \
110
+ --testrun-id 6d4ac4b7-dd67-4805-b879-18da0b89d4a8 \
111
+ --framework nose \
112
+ --output tmp/filter.txt
113
+
114
+ $ nose2 $(cat tmp/filter.txt) --testit
115
+ ```
116
+
117
+ ### Decorators
118
+
119
+ Decorators can be used to specify information about autotest.
120
+
121
+ Description of decorators:
122
+
123
+ - `testit.workItemIds` - a method that links autotests with manual tests. Receives the array of manual tests' IDs
124
+ - `testit.displayName` - internal autotest name (used in Test IT)
125
+ - `testit.externalId` - unique internal autotest ID (used in Test IT)
126
+ - `testit.title` - autotest name specified in the autotest card. If not specified, the name from the displayName method is used
127
+ - `testit.description` - autotest description specified in the autotest card
128
+ - `testit.labels` - labels listed in the autotest card
129
+ - `testit.tags` - tags listed in the autotest card
130
+ - `testit.links` - links listed in the autotest card
131
+ - `testit.step` - the designation of the step called in the body of the test or other step
132
+ - `testit.nameSpace` - directory in the TMS system (default - file's name of test)
133
+ - `testit.className` - subdirectory in the TMS system (default - class's name of test)
134
+
135
+ All decorators support the use of parameterization attributes
136
+
137
+ Description of methods:
138
+
139
+ - `testit.addWorkItemIds` - a dynamic method that links autotests with manual tests. Receives the array of manual tests' IDs
140
+ - `testit.addDisplayName` - a dynamic method for adding internal autotest name (used in Test IT)
141
+ - `testit.addExternalId` - a dynamic method for adding unique internal autotest ID (used in Test IT)
142
+ - `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
143
+ - `testit.addDescription` - a dynamic method for adding autotest description specified in the autotest card
144
+ - `testit.addLabels` - a dynamic method for adding labels listed in the autotest card
145
+ - `testit.addTags` - a dynamic method for adding tags listed in the autotest card
146
+ - `testit.addLinks` - links in the autotest result
147
+ - `testit.addAttachments` - uploading files in the autotest result
148
+ - `testit.addMessage` - information about autotest in the autotest result
149
+ - `testit.addNameSpace` - a dynamic method for adding directory in the TMS system (default - file's name of test)
150
+ - `testit.addClassName` - a dynamic method for adding subdirectory in the TMS system (default - class's name of test)
151
+ - `testit.addParameter` - a dynamic method for adding parameter in the autotest result
152
+ - `testit.step` - usage in the "with" construct to designation a step in the body of the test
153
+
154
+ ### Examples
155
+
156
+ #### Simple test
157
+
158
+ ```py
159
+ import pytest
160
+ import testit
161
+
162
+
163
+ # Test with a minimal set of decorators
164
+ @testit.externalId('Simple_autotest2')
165
+ def test_2():
166
+ """Simple autotest 2"""
167
+ assert oneStep()
168
+ assert twoStep()
169
+
170
+
171
+ @testit.step
172
+ def oneStep():
173
+ assert oneOneStep()
174
+ assert oneTwoStep()
175
+ return True
176
+
177
+
178
+ @testit.step
179
+ def twoStep():
180
+ return True
181
+
182
+
183
+ @testit.step('step 1.1', 'description')
184
+ def oneOneStep():
185
+ return True
186
+
187
+
188
+ @testit.step('step 2')
189
+ def oneTwoStep():
190
+ return True
191
+ ```
192
+
193
+ #### Parameterized test
194
+
195
+ > [!WARNING]
196
+ > When linking a parameterized autotest to a parameterized test case, please consider the problematic points:
197
+ > - 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
198
+ > - Running a parameterized test case, TMS expects the results of all related autotests with all the parameters specified in the test case table
199
+ > - 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
200
+ > - TMS expects a complete **textual** match of the name and value of the parameters of the test case table with the autotest parameters
201
+
202
+ ```py
203
+ # Parameterized test with a full set of decorators
204
+ from os.path import join, dirname
205
+
206
+ import testit
207
+ from nose2.tools import params
208
+
209
+ @params(1, 2, 3)
210
+ @testit.workItemIds(627)
211
+ @testit.externalId('param {num}')
212
+ @testit.displayName('param {num}')
213
+ @testit.title('Test with params')
214
+ @testit.description('E2E_autotest')
215
+ @testit.tags('parameters', 'test')
216
+ @testit.links(url='https://dumps.example.com/module/JCP-777')
217
+ @testit.links(url='https://dumps.example.com/module/JCP-777',
218
+ title='JCP-777',
219
+ type=testit.LinkType.RELATED,
220
+ description='Description of JCP-777')
221
+ def test_nums(num):
222
+ assert num < 4
223
+ ```
224
+
225
+ # Contributing
226
+
227
+ You can help to develop the project. Any contributions are **greatly appreciated**.
228
+
229
+ * If you have suggestions for adding or removing projects, feel free
230
+ to [open an issue](https://github.com/testit-tms/adapters-python/issues/new) to discuss it, or directly create a pull
231
+ request after you edit the *README.md* file with necessary changes.
232
+ * Please make sure you check your spelling and grammar.
233
+ * Create individual PR for each suggestion.
234
+ * Please also read through
235
+ the [Code Of Conduct](https://github.com/testit-tms/adapters-python/blob/master/CODE_OF_CONDUCT.md) before posting
236
+ your first idea as well.
237
+
238
+ # License
239
+
240
+ Distributed under the Apache-2.0 License.
241
+ See [LICENSE](https://github.com/testit-tms/adapters-python/blob/master/LICENSE.md) for more information.
242
+
@@ -0,0 +1,9 @@
1
+ testit_adapter_nose/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ testit_adapter_nose/listener.py,sha256=FoHDxfjhBuP_01fEgC7HLJzbQnjo8Blb2Z4EonuPRPg,2613
3
+ testit_adapter_nose/plugin.py,sha256=WJ9v30T9Bjvn2toK5f1p-YbbyK4T1ByNpghn_qIirks,1284
4
+ testit_adapter_nose/utils.py,sha256=gqvsog1p_4RM2iInrT7J7aBf3OZiKcf_BYGc96k23ug,12906
5
+ testit_adapter_nose-4.2.8.dist-info/METADATA,sha256=Um4-HJfFjhReL-npL2NJx9rFmibLIw26D5ZGtzdtQrU,14438
6
+ testit_adapter_nose-4.2.8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
7
+ testit_adapter_nose-4.2.8.dist-info/entry_points.txt,sha256=v2UoVNaRVZYkCyg8lvcHPQRAznR-C6QCEYsPt-VNhMA,79
8
+ testit_adapter_nose-4.2.8.dist-info/top_level.txt,sha256=aWoMZg9QARCSvZaPfRFCa3EZ6Jnil0viMGk1Cz0moIo,20
9
+ testit_adapter_nose-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,2 @@
1
+ [nose.plugins.0.10]
2
+ testit_adapter_nose = testit_adapter_nose.plugin:TmsPlugin
@@ -0,0 +1 @@
1
+ testit_adapter_nose