testit-adapter-nose 3.10.8.post550__tar.gz

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