• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

testit-tms / adapters-python / 23762390641

30 Mar 2026 07:01PM UTC coverage: 17.402% (+0.02%) from 17.381%
23762390641

push

github

web-flow
feat: support custom statuses. (#246)

* feat: support custom statuses.

* feat: support custom statuses.

* fix: set status for behave.

* fix: update sync-storage

* chore: update sync-storage version

* chore: update runner logic to type only

* chore: update version

* chore: update ci

* chore: update sync-storage version

* fix messages from failed steps for robotframework.

* fix: set status for behave.

* fix: small sync-storage fixes

* chore: update ci

---------

Co-authored-by: pavel.butuzov <pavel.butuzov@testit.software>
Co-authored-by: Dmitry Ermakovich <dmitry.ermakovich@testit.software>

7 of 54 new or added lines in 10 files covered. (12.96%)

2 existing lines in 2 files now uncovered.

284 of 1632 relevant lines covered (17.4%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/testit-adapter-robotframework/src/testit_adapter_robotframework/models.py
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

9
from .utils import get_hash
×
10

11

12
LinkTypes = ['Related', 'BlockedBy', 'Defect', 'Issue', 'Requirement', 'Repository']
×
13

14

15
def link_type_check(self, attribute, value):
×
16
    if value.title() not in LinkTypes:
×
17
        raise ValueError(f"Incorrect Link type: {value}")
×
18

19

20
def url_check(self, attribute, value):
×
21
    if not bool(re.match(
×
22
            r"(https?|ftp)://"
23
            r"(\w+(-\w+)*\.)?"
24
            r"((\w+(-\w+)*)\.(\w+))"
25
            r"(\.\w+)*"
26
            r"([\w\-._~/]*)*(?<!\.)",
27
            value)):
28
        raise ValueError(f"Incorrect URL: {value}")
×
29

30

31
class Default:
×
32

33
    def order(self):
×
34
        return asdict(self)
×
35

36

37
@s
×
38
class StepResult(Default):
×
39
    title = attrib(default='')
×
40
    description = attrib(default='')
×
41
    started_on = attrib(default=None)
×
42
    completed_on = attrib(default=None)
×
43
    duration = attrib(default=None)
×
44
    outcome = attrib(default=None)
×
45
    step_results = attrib(default=Factory(list))
×
46
    attachments = attrib(default=Factory(list))
×
47
    parameters = attrib(default=Factory(dict))
×
48

49

50
@s
×
51
class Step(Default):
×
52
    title = attrib()
×
53
    description = attrib()
×
54
    steps = attrib(default=Factory(list))
×
55

56

57
@s
×
58
class Label:
×
59
    name = attrib()
×
60

61

62
@s(kw_only=True)
×
63
class Autotest(Default):
×
64
    externalID = attrib(default=None)  # noqa: N815
×
65
    autoTestName = attrib()  # noqa: N815
×
66
    steps = attrib(default=Factory(list))
×
67
    stepResults = attrib(default=Factory(list))  # noqa: N815
×
68
    setUp = attrib(default=Factory(list))  # noqa: N815
×
69
    setUpResults = attrib(default=Factory(list))  # noqa: N815
×
70
    tearDown = attrib(default=Factory(list))  # noqa: N815
×
71
    tearDownResults = attrib(default=Factory(list))  # noqa: N815
×
72
    resultLinks = attrib(default=Factory(list))  # noqa: N815
×
73
    duration = attrib(default=None)
×
74
    failureReasonNames = attrib(default=Factory(list))  # noqa: N815
×
75
    traces = attrib(default=None)
×
76
    outcome = attrib(default=None)
×
NEW
77
    status_type = attrib(default=None)
×
78
    namespace = attrib(default=None)
×
79
    attachments = attrib(default=Factory(list))
×
80
    parameters = attrib(default=Factory(dict))
×
81
    properties = attrib(default=Factory(dict))
×
82
    classname = attrib(default=None)
×
83
    title = attrib(default=None)
×
84
    description = attrib(default=None)
×
85
    links = attrib(default=Factory(list))
×
86
    labels = attrib(default=Factory(list))
×
87
    tags = attrib(default=Factory(list))
×
88
    workItemsID = attrib(default=Factory(list))  # noqa: N815
×
89
    message = attrib(default="")
×
90
    started_on = attrib(default=None)
×
91
    completed_on = attrib(default=None)
×
92
    externalKey = attrib(default=None)
×
93

94
    step_depth = attrib(default=Factory(list))
×
95
    result_depth = attrib(default=Factory(list))
×
96

97
    def add_attributes(self, attrs):
×
98
        self.title = attrs['originalname']
×
99
        self.autoTestName = attrs['originalname']
×
100
        self.externalKey = attrs['originalname']
×
101
        self.description = attrs['doc']
×
102
        self.template = attrs['template']
×
103
        self.classname = attrs['longname'].split('.')[-2]
×
104
        for tag in attrs['tags']:
×
105
            if tag.lower().startswith('testit.'):
×
106
                attr = re.findall(r'(?<=\.).*?(?=:)', tag)[0].strip().lower()
×
107
                value = tag.split(':', 1)[-1].strip()
×
108
                if attr == 'externalid':
×
109
                    self.externalID = str(value).replace("'", "").replace('"', '')
×
110
                elif attr == 'displayname':
×
111
                    self.autoTestName = str(value).replace("'", "").replace('"', '')
×
112
                elif attr == 'title':
×
113
                    self.title = str(value).replace("'", "").replace('"', '')
×
114
                elif attr == 'description':
×
115
                    self.description = str(value).replace("'", "").replace('"', '')
×
116
                elif attr == 'workitemsid' or attr == 'workitemsids':
×
117
                    value = ast.literal_eval(value)
×
118
                    if isinstance(value, (str, int)):
×
119
                        self.workItemsID.append(str(value))
×
120
                    elif isinstance(value, list):
×
121
                        self.workItemsID.extend([str(i) for i in value])
×
122
                    else:
123
                        logger.error(f"[TestIt] Wrong workitem format: {value}")
×
124
                elif attr == 'links':
×
125
                    value = ast.literal_eval(value)
×
126
                    try:
×
127
                        if isinstance(value, dict):
×
128
                            self.links.append(Link()\
×
129
                                .set_url(value['url'])\
130
                                .set_title(value.get('title', None))\
131
                                .set_link_type(value.get('type', None))\
132
                                .set_description(value.get('description', None)))
133
                        elif isinstance(value, list):
×
134
                            self.links.extend([Link()\
×
135
                                .set_url(link['url'])\
136
                                .set_title(link.get('title', None))\
137
                                .set_link_type(link.get('type', None))\
138
                                .set_description(link.get('description', None)) for link in value if isinstance(link, dict)])
139
                    except ValueError as e:
×
140
                        logger.error(f"[TestIt] Link Error: {e}")
×
141
                elif attr == 'labels':
×
142
                    value = ast.literal_eval(value)
×
143
                    if isinstance(value, (str, int)):
×
144
                        self.labels.append(Label(value))
×
145
                    elif isinstance(value, list):
×
146
                        self.labels.extend([Label(item) for item in value if isinstance(item, (str, int))])
×
147
                elif attr == 'tags':
×
148
                    value = ast.literal_eval(value)
×
149
                    if isinstance(value, (str, int)):
×
150
                        self.tags.append(str(value))
×
151
                    elif isinstance(value, list):
×
152
                        self.tags.extend([str(item) for item in value if isinstance(item, (str, int))])
×
153
                elif attr == 'namespace':
×
154
                    self.namespace = str(value).replace("'", "").replace('"', '')
×
155
                elif attr == 'classname':
×
156
                    self.classname = str(value).replace("'", "").replace('"', '')
×
157
                else:
158
                    logger.error(f"[TestIt] Unknown attribute: {attr}")
×
159
        if not self.externalID:
×
160
            self.externalID = get_hash(attrs['longname'])
×
161

162
    def add_step(self, step_type, title, description, parameters):
×
163
        if len(self.step_depth) == 0:
×
164
            if step_type.lower() == 'setup':
×
165
                self.setUp.append(Step(title, description))
×
166
                self.step_depth.append(self.setUp[-1])
×
167
                self.setUpResults.append(StepResult(title, description, parameters=parameters))
×
168
                self.result_depth.append(self.setUpResults[-1])
×
169
            elif step_type.lower() == 'teardown':
×
170
                self.tearDown.append(Step(title, description))
×
171
                self.step_depth.append(self.tearDown[-1])
×
172
                self.tearDownResults.append(StepResult(title, description, parameters=parameters))
×
173
                self.result_depth.append(self.tearDownResults[-1])
×
174
            else:
175
                self.steps.append(Step(title, description))
×
176
                self.step_depth.append(self.steps[-1])
×
177
                self.stepResults.append(StepResult(title, description, parameters=parameters))
×
178
                self.result_depth.append(self.stepResults[-1])
×
179
        elif 1 <= len(self.step_depth) < 14:
×
180
            self.step_depth[-1].steps.append(Step(title, description))
×
181
            self.step_depth.append(self.step_depth[-1].steps[-1])
×
182
            self.result_depth[-1].step_results.append(StepResult(title, description, parameters=parameters))
×
183
            self.result_depth.append(self.result_depth[-1].step_results[-1])
×
184

185
    def add_step_result(self, title, start, complete, duration, outcome, attachments):
×
186
        if self.result_depth:
×
187
            if self.result_depth[-1].title == title:
×
188
                step = self.result_depth.pop()
×
189
                step.started_on = start
×
190
                step.completed_on = complete
×
191
                step.duration = duration
×
192
                step.outcome = outcome
×
193
                step.attachments = attachments
×
194
        if self.step_depth:
×
195
            if self.step_depth[-1].title == title:
×
196
                self.step_depth.pop()
×
197

198

199
class Option:
×
200

201
    def __init__(self, **kwargs):
×
202
        if kwargs.get('tmsUrl', None):
×
203
            self.set_url = kwargs.get('tmsUrl', None)
×
204
        if kwargs.get('tmsPrivateToken', None):
×
205
            self.set_private_token = kwargs.get('tmsPrivateToken', None)
×
206
        if kwargs.get('tmsProjectId', None):
×
207
            self.set_project_id = kwargs.get('tmsProjectId', None)
×
208
        if kwargs.get('tmsConfigurationId', None):
×
209
            self.set_configuration_id = kwargs.get('tmsConfigurationId', None)
×
210
        if kwargs.get('tmsTestRunId', None):
×
211
            self.set_test_run_id = kwargs.get('tmsTestRunId', None)
×
212
        if kwargs.get('tmsProxy', None):
×
213
            self.set_tms_proxy = kwargs.get('tmsProxy', None)
×
214
        if kwargs.get('tmsTestRunName', None):
×
215
            self.set_test_run_name = kwargs.get('tmsTestRunName', None)
×
216
        if kwargs.get('tmsAdapterMode', None):
×
217
            self.set_adapter_mode = kwargs.get('tmsAdapterMode', None)
×
218
        if kwargs.get('tmsConfigFile', None):
×
219
            self.set_config_file = kwargs.get('tmsConfigFile', None)
×
220
        if kwargs.get('tmsCertValidation', None):
×
221
            self.set_cert_validation = kwargs.get('tmsCertValidation', None)
×
222
        if kwargs.get('tmsAutomaticCreationTestCases', None):
×
223
            self.set_automatic_creation_test_cases = kwargs.get('tmsAutomaticCreationTestCases', None)
×
224
        if kwargs.get('tmsAutomaticUpdationLinksToTestCases', None):
×
225
            self.set_automatic_updation_links_to_test_cases = kwargs.get('tmsAutomaticUpdationLinksToTestCases', None)
×
226
        if kwargs.get('tmsImportRealtime', None):
×
227
            self.set_import_realtime = kwargs.get('tmsImportRealtime', None)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc