qase-python-commons 3.0.0__tar.gz → 3.0.2b2__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.
Files changed (36) hide show
  1. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/PKG-INFO +2 -2
  2. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/pyproject.toml +3 -3
  3. qase_python_commons-3.0.2b2/src/qase/commons/client/api_v1_client.py +247 -0
  4. qase_python_commons-3.0.2b2/src/qase/commons/client/base_api_client.py +88 -0
  5. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/config.py +4 -1
  6. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/loader.py +4 -4
  7. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/logger.py +4 -0
  8. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/attachment.py +8 -5
  9. qase_python_commons-3.0.2b2/src/qase/commons/models/basemodel.py +7 -0
  10. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/relation.py +5 -2
  11. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/result.py +8 -10
  12. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/run.py +6 -6
  13. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/step.py +12 -10
  14. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/suite.py +4 -2
  15. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/reporters/core.py +16 -6
  16. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/reporters/report.py +1 -1
  17. qase_python_commons-3.0.2b2/src/qase/commons/reporters/testops.py +130 -0
  18. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase_python_commons.egg-info/PKG-INFO +2 -2
  19. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase_python_commons.egg-info/SOURCES.txt +3 -0
  20. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase_python_commons.egg-info/requires.txt +1 -1
  21. qase_python_commons-3.0.0/src/qase/commons/reporters/testops.py +0 -369
  22. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/README.md +0 -0
  23. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/setup.cfg +0 -0
  24. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/__init__.py +0 -0
  25. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/exceptions/reporter.py +0 -0
  26. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/__init__.py +0 -0
  27. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/models/runtime.py +0 -0
  28. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/profilers/__init__.py +0 -0
  29. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/profilers/db.py +0 -0
  30. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/profilers/network.py +0 -0
  31. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/profilers/sleep.py +0 -0
  32. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/reporters/__init__.py +0 -0
  33. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/utils.py +0 -0
  34. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase/commons/validators/base.py +0 -0
  35. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase_python_commons.egg-info/dependency_links.txt +0 -0
  36. {qase_python_commons-3.0.0 → qase_python_commons-3.0.2b2}/src/qase_python_commons.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qase-python-commons
3
- Version: 3.0.0
3
+ Version: 3.0.2b2
4
4
  Summary: A library for Qase TestOps and Qase Report
5
5
  Author-email: Qase Team <support@qase.io>
6
6
  Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/master/qase-python-commons
@@ -22,7 +22,7 @@ Requires-Python: >=3.7
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: certifi>=2024.2.2
24
24
  Requires-Dist: attrs>=23.2.0
25
- Requires-Dist: qaseio
25
+ Requires-Dist: qase-api-client~=1.0.0b2
26
26
  Requires-Dist: more_itertools
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pytest; extra == "testing"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qase-python-commons"
7
- version = "3.0.0"
7
+ version = "3.0.2b2"
8
8
  description = "A library for Qase TestOps and Qase Report"
9
9
  readme = "README.md"
10
10
  authors = [{name = "Qase Team", email = "support@qase.io"}]
@@ -30,7 +30,7 @@ requires-python = ">=3.7"
30
30
  dependencies = [
31
31
  "certifi>=2024.2.2",
32
32
  "attrs>=23.2.0",
33
- "qaseio",
33
+ "qase-api-client~=1.0.0b2",
34
34
  "more_itertools"
35
35
  ]
36
36
 
@@ -99,4 +99,4 @@ exclude_also = [
99
99
  "@(abc\\.)?abstractmethod",
100
100
  ]
101
101
 
102
- ignore_errors = false
102
+ ignore_errors = false
@@ -0,0 +1,247 @@
1
+ from typing import Dict, Union
2
+
3
+ import certifi
4
+ from qase.api_client_v1 import ApiClient, ProjectsApi, Project, EnvironmentsApi, RunsApi, AttachmentsApi, \
5
+ AttachmentGet, RunCreate, ResultsApi, ResultcreateBulk
6
+ from qase.api_client_v1.configuration import Configuration
7
+ from .. import ConfigManager, Logger
8
+ from .base_api_client import BaseApiClient
9
+ from ..exceptions.reporter import ReporterException
10
+ from ..models import Attachment, Result, Step
11
+ from ..models.step import StepType
12
+
13
+
14
+ class ApiV1Client(BaseApiClient):
15
+ def __init__(self, config: ConfigManager, logger: Logger):
16
+ self.logger = logger
17
+ self.config = config
18
+
19
+ try:
20
+ self.logger.log_debug("Preparing API client")
21
+ configuration = Configuration()
22
+ configuration.api_key['TokenAuth'] = self.config.get('testops.api.token')
23
+ configuration.ssl_ca_cert = certifi.where()
24
+ host = self.config.get('testops.api.host', 'qase.io')
25
+ if self.config.get('testops.api.enterprise', False, bool):
26
+ configuration.host = f'https://api-{host}/v1'
27
+ self.web = f'https://{host}'
28
+ else:
29
+ configuration.host = f'https://api.{host}/v1'
30
+ self.web = f'https://app.{host}'
31
+
32
+ self.client = ApiClient(configuration)
33
+ self.logger.log_debug("API client prepared")
34
+ except Exception as e:
35
+ self.logger.log(f"Error at preparing API client: {e}", "error")
36
+ raise ReporterException(e)
37
+
38
+ def get_project(self, project_code: str) -> Union[Project, None]:
39
+ try:
40
+ self.logger.log_debug(f"Getting project {project_code}")
41
+ response = ProjectsApi(self.client).get_project(code=project_code)
42
+ if hasattr(response, 'result'):
43
+ self.logger.log_debug(f"Project {project_code} found: {response.result.to_json()}")
44
+ return response.result
45
+ raise ReporterException("Unable to find given project code")
46
+ except Exception as e:
47
+ self.logger.log("Exception when calling ProjectApi->get_project: %s\n" % e, "error")
48
+ raise ReporterException("Exception when calling ProjectApi")
49
+
50
+ def get_environment(self, environment: str, project_code: str) -> Union[str, None]:
51
+ try:
52
+ self.logger.log_debug(f"Getting environment {environment}")
53
+ api_instance = EnvironmentsApi(self.client)
54
+ response = api_instance.get_environments(code=project_code)
55
+ if hasattr(response, 'result') and hasattr(response.result, 'entities'):
56
+ for env in response.result.entities:
57
+ if env.slug == environment:
58
+ self.logger.log_debug(f"Environment {environment} found: {env.to_json()}")
59
+ return env.id
60
+ self.logger.log_debug(f"Environment {environment} not found")
61
+ return None
62
+ except Exception as e:
63
+ self.logger.log("Exception when calling EnvironmentsApi->get_environments: %s\n" % e, "error")
64
+ raise ReporterException(e)
65
+
66
+ def complete_run(self, project_code: str, run_id: str) -> None:
67
+ api_runs = RunsApi(self.client)
68
+ self.logger.log_debug(f"Completing run {run_id}")
69
+ res = api_runs.get_run(project_code, run_id).result
70
+ if res.status == 1:
71
+ self.logger.log_debug(f"Run {run_id} already completed")
72
+ return
73
+ try:
74
+ api_runs.complete_run(project_code, run_id)
75
+ self.logger.log(f"Run {run_id} was completed successfully", "info")
76
+ except Exception as e:
77
+ self.logger.log(f"Error at completing run {run_id}: {e}", "error")
78
+ raise ReporterException(e)
79
+
80
+ def _upload_attachment(self, project_code: str, attachment: Attachment) -> Union[AttachmentGet, None]:
81
+ try:
82
+ self.logger.log_debug(f"Uploading attachment {attachment.id} for project {project_code}")
83
+ attach_api = AttachmentsApi(self.client)
84
+ response = attach_api.upload_attachment(project_code, file=[attachment.get_for_upload()])
85
+
86
+ return response.result
87
+
88
+ except Exception as e:
89
+ self.logger.log(f"Error at uploading attachment: {e}", "error")
90
+ raise ReporterException(e)
91
+
92
+ def create_test_run(self, project_code: str, title: str, description: str, plan_id=None,
93
+ environment_id=None) -> str:
94
+ kwargs = dict(
95
+ title=title,
96
+ description=description,
97
+ environment_id=(int(environment_id) if environment_id else None),
98
+ plan_id=(int(plan_id) if plan_id else plan_id),
99
+ is_autotest=True
100
+ )
101
+ self.logger.log_debug(f"Creating test run with parameters: {kwargs}")
102
+ try:
103
+ result = RunsApi(self.client).create_run(
104
+ code=project_code,
105
+ run_create=RunCreate(**{k: v for k, v in kwargs.items() if v is not None})
106
+ )
107
+
108
+ self.logger.log(f"Test run was created: {self.web}/run/{project_code}/dashboard/{result.result.id}", "info")
109
+
110
+ return result.result.id
111
+
112
+ except Exception as e:
113
+ self.logger.log(f"Error at creating test run: {e}", "error")
114
+ raise ReporterException(e)
115
+
116
+ def check_test_run(self, project_code: str, run_id: int) -> bool:
117
+ api_runs = RunsApi(self.client)
118
+ run = api_runs.get_run(code=project_code, id=run_id)
119
+ if run.result.id:
120
+ return True
121
+ return False
122
+
123
+ def send_results(self, project_code: str, run_id: int, results: []) -> None:
124
+ api_results = ResultsApi(self.client)
125
+ results_to_send = [self._prepare_result(project_code, result) for result in results]
126
+ self.logger.log_debug(f"Sending results for run {run_id}: {results_to_send}")
127
+ api_results.create_result_bulk(
128
+ code=project_code,
129
+ id=run_id,
130
+ resultcreate_bulk=ResultcreateBulk(
131
+ results=results_to_send
132
+ )
133
+ )
134
+ self.logger.log_debug(f"Results for run {run_id} sent successfully")
135
+
136
+ def _prepare_result(self, project_code: str, result: Result) -> Dict:
137
+ attached = []
138
+ if result.attachments:
139
+ for attachment in result.attachments:
140
+ attached.extend(self._upload_attachment(project_code, attachment))
141
+
142
+ steps = []
143
+ for step in result.steps:
144
+ prepared = self._prepare_step(project_code, step)
145
+ steps.append(prepared)
146
+
147
+ case_data = {
148
+ "title": result.get_title(),
149
+ "description": result.get_field('description'),
150
+ "preconditions": result.get_field('preconditions'),
151
+ "postconditions": result.get_field('postconditions'),
152
+ }
153
+
154
+ for key, param in result.params.items():
155
+ # Hack to match old TestOps API
156
+ if param == "":
157
+ result.params[key] = "empty"
158
+
159
+ if result.get_field('severity'):
160
+ case_data["severity"] = result.get_field('severity')
161
+
162
+ if result.get_field('priority'):
163
+ case_data["priority"] = result.get_field('priority')
164
+
165
+ if result.get_field('layer'):
166
+ case_data["layer"] = result.get_field('layer')
167
+
168
+ if result.get_suite_title():
169
+ case_data["suite_title"] = "\t".join(result.get_suite_title().split("."))
170
+
171
+ result_model = {
172
+ "status": result.execution.status,
173
+ "stacktrace": result.execution.stacktrace,
174
+ "time_ms": result.execution.duration,
175
+ "comment": result.message,
176
+ "attachments": [attach.hash for attach in attached],
177
+ "steps": steps,
178
+ "param": result.params,
179
+ "defect": self.config.get('testops.defect', False, bool),
180
+ }
181
+
182
+ test_ops_id = result.get_testops_id()
183
+
184
+ if test_ops_id:
185
+ result_model["case_id"] = test_ops_id
186
+ result_model["case"] = None
187
+ return result_model
188
+
189
+ result_model["case_id"] = None
190
+ result_model["case"] = case_data
191
+
192
+ self.logger.log_debug(f"Prepared result: {result_model}")
193
+
194
+ return result_model
195
+
196
+ def _prepare_step(self, project_code: str, step: Step) -> Dict:
197
+ prepared_children = []
198
+
199
+ try:
200
+ prepared_step = {"time": step.execution.duration, "status": step.execution.status}
201
+
202
+ if step.execution.status == 'untested':
203
+ prepared_step["status"] = 'passed'
204
+
205
+ if step.step_type == StepType.TEXT:
206
+ prepared_step['action'] = step.data.action
207
+ if step.data.expected_result:
208
+ prepared_step['expected_result'] = step.data.expected_result
209
+
210
+ if step.step_type == StepType.REQUEST:
211
+ prepared_step['action'] = step.data.request_method + " " + step.data.request_url
212
+ if step.data.request_body:
213
+ step.attachments.append(
214
+ Attachment(file_name='request_body.txt', content=step.data.request_body, mime_type='text/plain',
215
+ temporary=True))
216
+ if step.data.request_headers:
217
+ step.attachments.append(
218
+ Attachment(file_name='request_headers.txt', content=step.data.request_headers,
219
+ mime_type='text/plain', temporary=True))
220
+ if step.data.response_body:
221
+ step.attachments.append(Attachment(file_name='response_body.txt', content=step.data.response_body,
222
+ mime_type='text/plain', temporary=True))
223
+ if step.data.response_headers:
224
+ step.attachments.append(
225
+ Attachment(file_name='response_headers.txt', content=step.data.response_headers,
226
+ mime_type='text/plain', temporary=True))
227
+
228
+ if step.step_type == StepType.GHERKIN:
229
+ prepared_step['action'] = step.data.keyword
230
+
231
+ if step.step_type == StepType.SLEEP:
232
+ prepared_step['action'] = f"Sleep for {step.data.duration} seconds"
233
+
234
+ if step.attachments:
235
+ uploaded_attachments = []
236
+ for file in step.attachments:
237
+ uploaded_attachments.extend(self._upload_attachment(project_code, file))
238
+ prepared_step['attachments'] = [attach.hash for attach in uploaded_attachments]
239
+
240
+ if step.steps:
241
+ for substep in step.steps:
242
+ prepared_children.append(self._prepare_step(project_code, substep))
243
+ prepared_step["steps"] = prepared_children
244
+ return prepared_step
245
+ except Exception as e:
246
+ self.logger.log(f"Error at preparing step: {e}", "error")
247
+ raise ReporterException(e)
@@ -0,0 +1,88 @@
1
+ import abc
2
+ from typing import Union
3
+
4
+ from qase.api_client_v1 import Project, AttachmentGet
5
+
6
+ from ..models import Attachment
7
+
8
+
9
+ class BaseApiClient(abc.ABC):
10
+ @abc.abstractmethod
11
+ def get_project(self, project_code: str) -> Union[Project, None]:
12
+ """
13
+ Load a project from Qase TestOps by code and return project data
14
+
15
+ :param project_code: project code
16
+ :return: project data or None if project not found
17
+ """
18
+
19
+ pass
20
+
21
+ @abc.abstractmethod
22
+ def get_environment(self, environment: str, project_code: str) -> Union[str, None]:
23
+ """
24
+ Load an environment from Qase TestOps by name and returns environment id
25
+
26
+ :param environment: environment name
27
+ :param project_code: project code
28
+ :return: environment id or None if environment not found
29
+ """
30
+ pass
31
+
32
+ @abc.abstractmethod
33
+ def complete_run(self, project_code: str, run_id: str) -> None:
34
+ """
35
+ Complete a test run in Qase TestOps
36
+
37
+ :param project_code: project code
38
+ :param run_id: test run id
39
+ :return: None
40
+ """
41
+ pass
42
+
43
+ @abc.abstractmethod
44
+ def _upload_attachment(self, project_code: str, attachment: Attachment) -> Union[AttachmentGet, None]:
45
+ """
46
+ Upload an attachment to Qase TestOps
47
+
48
+ :param project_code: project code
49
+ :param attachment: attachment model
50
+ :return: attachment data or None if attachment not uploaded
51
+ """
52
+ pass
53
+
54
+ @abc.abstractmethod
55
+ def create_test_run(self, project_code: str, title: str, description: str, plan_id=None,
56
+ environment_id=None) -> int:
57
+ """
58
+ Create a test run in Qase TestOps
59
+
60
+ :param project_code: project code
61
+ :param title: test run title
62
+ :param description: test run description
63
+ :param plan_id: plan id
64
+ :param environment_id: environment id
65
+ :return: test run id
66
+ """
67
+ pass
68
+
69
+ @abc.abstractmethod
70
+ def check_test_run(self, project_code: str, run_id: int) -> bool:
71
+ """
72
+ Check if test run exists in Qase TestOps
73
+ :param project_code: project code
74
+ :param run_id: test run id
75
+ :return: True if test run exists, False otherwise
76
+ """
77
+ pass
78
+
79
+ @abc.abstractmethod
80
+ def send_results(self, project_code: str, run_id: int, results: []) -> None:
81
+ """
82
+ Send test results to Qase TestOps
83
+ :param project_code: project code
84
+ :param run_id: test run id
85
+ :param results: results data
86
+ :return: None
87
+ """
88
+ pass
@@ -29,7 +29,7 @@ class ConfigManager:
29
29
  # Use _get_config method to get the value. If None, return default.
30
30
  value = self._get_config(key)
31
31
  if value_type and value_type == bool:
32
- return self.parseBool(value or default)
32
+ return self.parseBool(value)
33
33
  return value or default
34
34
 
35
35
  def validate_config(self):
@@ -59,3 +59,6 @@ class ConfigManager:
59
59
  for key in keys[:-1]:
60
60
  config = config.get(key, {})
61
61
  return config.get(keys[-1], None)
62
+
63
+ def __str__(self):
64
+ return json.dumps(self.config, indent=4, sort_keys=True)
@@ -1,7 +1,7 @@
1
- from qaseio.api_client import ApiClient
2
- from qaseio.configuration import Configuration
3
- from qaseio.api.plans_api import PlansApi
4
- from qaseio.rest import ApiException
1
+ from qase.api_client_v1.api_client import ApiClient
2
+ from qase.api_client_v1.configuration import Configuration
3
+ from qase.api_client_v1.api.plans_api import PlansApi
4
+ from qase.api_client_v1.exceptions import ApiException
5
5
 
6
6
  import certifi
7
7
 
@@ -22,6 +22,10 @@ class Logger:
22
22
  with open(self.log_file, 'a') as f:
23
23
  f.write(log)
24
24
 
25
+ def log_debug(self, message: str):
26
+ if self.debug:
27
+ self.log(message, 'debug')
28
+
25
29
  @staticmethod
26
30
  def _get_timestamp(format: str = "%Y%m%d_%H:%M:%S"):
27
31
  now = datetime.datetime.now()
@@ -1,12 +1,14 @@
1
1
  import os
2
2
  import uuid
3
- from typing import Optional, Union
4
- from io import BytesIO, StringIO
5
3
  import json
6
4
  import pathlib
7
5
 
6
+ from typing import Optional, Union
7
+ from io import BytesIO, StringIO
8
+ from .basemodel import BaseModel
9
+
8
10
 
9
- class Attachment:
11
+ class Attachment(BaseModel):
10
12
  def __init__(self,
11
13
  file_name: str,
12
14
  mime_type: str,
@@ -40,10 +42,11 @@ class Attachment:
40
42
 
41
43
  def get_for_upload(self) -> BytesIO:
42
44
  if self.file_path:
43
- return pathlib.Path(os.path.abspath(self.file_path))
45
+ with open(self.file_path, "rb") as fc:
46
+ content = BytesIO(fc.read())
44
47
  else:
45
48
  if isinstance(self.content, str):
46
- content = BytesIO(self.content.encode('utf-8'))
49
+ content = BytesIO(bytes(self.content, 'utf-8'))
47
50
  elif isinstance(self.content, bytes):
48
51
  content = BytesIO(self.content)
49
52
  content.name = self.file_name
@@ -0,0 +1,7 @@
1
+ import json
2
+
3
+
4
+ class BaseModel:
5
+ def __str__(self) -> str:
6
+ return json.dumps(self, default=lambda o: o.__dict__ if hasattr(o, '__dict__') else str(o), indent=4,
7
+ sort_keys=True)
@@ -1,10 +1,13 @@
1
- class RelationSuite(object):
1
+ from .basemodel import BaseModel
2
+
3
+
4
+ class RelationSuite(BaseModel):
2
5
  def __init__(self, suite_id: int, title: str) -> None:
3
6
  self.suite_id = suite_id
4
7
  self.title = title
5
8
 
6
9
 
7
- class Relation(object):
10
+ class Relation(BaseModel):
8
11
  def __init__(self, type: str, data: RelationSuite):
9
12
  self.type = type
10
13
  self.data = data
@@ -1,8 +1,10 @@
1
- from typing import Type, Optional, Union, Dict, List
2
- from pathlib import PosixPath
3
1
  import time
4
2
  import uuid
5
3
  import json
4
+
5
+ from typing import Type, Optional, Union, Dict, List
6
+ from pathlib import PosixPath
7
+ from .basemodel import BaseModel
6
8
  from .step import Step
7
9
  from .suite import Suite
8
10
  from .attachment import Attachment
@@ -10,7 +12,7 @@ from .relation import Relation
10
12
  from .. import QaseUtils
11
13
 
12
14
 
13
- class Field:
15
+ class Field(BaseModel):
14
16
  def __init__(self,
15
17
  name: str,
16
18
  value: Union[str, list]):
@@ -18,7 +20,7 @@ class Field:
18
20
  self.value = value
19
21
 
20
22
 
21
- class Execution(object):
23
+ class Execution(BaseModel):
22
24
  def __init__(self,
23
25
  status: Optional[str] = None,
24
26
  end_time: int = 0,
@@ -47,7 +49,7 @@ class Execution(object):
47
49
  self.duration = (int)((self.end_time - self.start_time) * 1000)
48
50
 
49
51
 
50
- class Request(object):
52
+ class Request(BaseModel):
51
53
  def __init__(self,
52
54
  method: str,
53
55
  url: str,
@@ -65,7 +67,7 @@ class Request(object):
65
67
  self.response_body = response_body
66
68
 
67
69
 
68
- class Result(object):
70
+ class Result(BaseModel):
69
71
  def __init__(self, title: str, signature: str) -> None:
70
72
  self.id: str = str(uuid.uuid4())
71
73
  self.title: str = title
@@ -137,7 +139,3 @@ class Result(object):
137
139
 
138
140
  def set_run_id(self, run_id: str) -> None:
139
141
  self.run_id = run_id
140
-
141
- def to_json(self) -> str:
142
- return json.dumps(self, default=lambda o: o.__str__() if isinstance(o, PosixPath) else o.__dict__,
143
- sort_keys=False, indent=4)
@@ -1,8 +1,11 @@
1
1
  import json
2
+
2
3
  from typing import Optional, List
3
4
 
5
+ from .basemodel import BaseModel
6
+
4
7
 
5
- class RunExecution(object):
8
+ class RunExecution(BaseModel):
6
9
  def __init__(self,
7
10
  start_time: float,
8
11
  end_time: float,
@@ -17,7 +20,7 @@ class RunExecution(object):
17
20
  self.cumulative_duration += result["execution"]["duration"]
18
21
 
19
22
 
20
- class RunStats(object):
23
+ class RunStats(BaseModel):
21
24
  def __init__(self) -> None:
22
25
  self.passed = 0
23
26
  self.failed = 0
@@ -41,7 +44,7 @@ class RunStats(object):
41
44
  self.muted += 1
42
45
 
43
46
 
44
- class Run(object):
47
+ class Run(BaseModel):
45
48
  def __init__(self,
46
49
  title: str,
47
50
  start_time: float,
@@ -60,9 +63,6 @@ class Run(object):
60
63
  self.suites = suites
61
64
  self.environment = environment
62
65
 
63
- def to_json(self) -> str:
64
- return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False, indent=4)
65
-
66
66
  def add_result(self, result: dict):
67
67
  compact_result = {
68
68
  "id": result["id"],
@@ -1,8 +1,10 @@
1
- from enum import Enum
2
- from typing import Optional, Union, Dict, List, Type
3
1
  import time
4
2
  import uuid
3
+
4
+ from enum import Enum
5
+ from typing import Optional, Union, Dict, List, Type
5
6
  from .attachment import Attachment
7
+ from .basemodel import BaseModel
6
8
 
7
9
 
8
10
  class StepType(Enum):
@@ -14,27 +16,27 @@ class StepType(Enum):
14
16
  SLEEP = 'sleep'
15
17
 
16
18
 
17
- class StepTextData(object):
19
+ class StepTextData(BaseModel):
18
20
  def __init__(self, action: str, expected_result: Optional[str] = None):
19
21
  self.action = action
20
22
  self.expected_result = expected_result
21
23
 
22
24
 
23
- class StepAssertData(object):
25
+ class StepAssertData(BaseModel):
24
26
  def __init__(self, expected: str, actual: str, message: str):
25
27
  self.expected = expected
26
28
  self.actual = actual
27
29
  self.message = message
28
30
 
29
31
 
30
- class StepGherkinData(object):
32
+ class StepGherkinData(BaseModel):
31
33
  def __init__(self, keyword: str, name: str, line: int):
32
34
  self.keyword = keyword
33
35
  self.name = name
34
36
  self.line = line
35
37
 
36
38
 
37
- class StepRequestData(object):
39
+ class StepRequestData(BaseModel):
38
40
  def __init__(self, request_body: str, request_headers: Dict[str, str], request_method: str, request_url: str):
39
41
  self.response_headers = None
40
42
  self.response_body = None
@@ -60,17 +62,17 @@ class StepRequestData(object):
60
62
  self.response_headers = response_headers
61
63
 
62
64
 
63
- class StepDbQueryData(object):
65
+ class StepDbQueryData(BaseModel):
64
66
  def __init__(self, query: str, expected_result: str):
65
67
  self.query = query
66
68
 
67
69
 
68
- class StepSleepData(object):
70
+ class StepSleepData(BaseModel):
69
71
  def __init__(self, duration: int):
70
72
  self.duration = duration
71
73
 
72
74
 
73
- class StepExecution(object):
75
+ class StepExecution(BaseModel):
74
76
  def __init__(self, status: Optional[str] = 'untested', end_time: int = 0, duration: int = 0):
75
77
  self.start_time = time.time()
76
78
  self.status = status
@@ -88,7 +90,7 @@ class StepExecution(object):
88
90
  self.duration = int((self.end_time - self.start_time) * 1000)
89
91
 
90
92
 
91
- class Step(object):
93
+ class Step(BaseModel):
92
94
  def __init__(self,
93
95
  step_type: StepType,
94
96
  id: Optional[str],
@@ -1,9 +1,11 @@
1
- from typing import List, Optional
2
1
  import uuid
3
2
 
3
+ from typing import List, Optional
4
+
5
+ from .basemodel import BaseModel
4
6
 
5
- class Suite():
6
7
 
8
+ class Suite(BaseModel):
7
9
  def __init__(self, title: str, description: Optional[str] = None, parent_id: Optional[str] = None):
8
10
  self.title = title
9
11
  self.description = description