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

testit-tms / adapters-python / 24415535531

14 Apr 2026 06:16PM UTC coverage: 17.482% (+0.08%) from 17.402%
24415535531

push

github

web-flow
feat: TMS-38870 fix link_types (#254)

* feat: TMS-38870 fix link_types

* fix link_type

2 of 4 new or added lines in 4 files covered. (50.0%)

286 of 1636 relevant lines covered (17.48%)

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
×
NEW
8
from testit_python_commons.models.link_type import LinkType
×
9

10

11
from .utils import get_hash
×
12

13

14
LinkTypes = ['Related', 'BlockedBy', 'Defect', 'Issue', 'Requirement', 'Repository']
×
15

16

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

21

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

32

33
class Default:
×
34

35
    def order(self):
×
36
        return asdict(self)
×
37

38

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

51

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

58

59
@s
×
60
class Label:
×
61
    name = attrib()
×
62

63

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

96
    step_depth = attrib(default=Factory(list))
×
97
    result_depth = attrib(default=Factory(list))
×
98

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

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

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

200

201
class Option:
×
202

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

© 2026 Coveralls, Inc