cloudbeat-common 0.0.1__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,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloudbeat-common
3
+ Version: 0.0.1
4
+ Summary: Contains the common types and API client for CloudBeat
5
+ Home-page: https://cloudbeat.io/
6
+ Author: CBNR Cloud Solutions LTD
7
+ Author-email: info@cloudbeat.io
8
+ License: Apache-2.0
9
+ Project-URL: Source, https://github.com/cloudbeat-io/cb-kit-python
10
+ Keywords: cloudbeat testing reporting python
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.7
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.6
25
+ Description-Content-Type: text/markdown
26
+ Requires-Dist: attrs>=16.0.0
27
+ Requires-Dist: pluggy>=0.4.0
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description-content-type
32
+ Dynamic: home-page
33
+ Dynamic: keywords
34
+ Dynamic: license
35
+ Dynamic: project-url
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
@@ -0,0 +1,8 @@
1
+ from cloudbeat_common.models import TestStatus
2
+ from cloudbeat_common.reporter import CbTestReporter
3
+
4
+
5
+ __all__ = [
6
+ 'TestStatus',
7
+ 'CbTestReporter'
8
+ ]
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloudbeat-common
3
+ Version: 0.0.1
4
+ Summary: Contains the common types and API client for CloudBeat
5
+ Home-page: https://cloudbeat.io/
6
+ Author: CBNR Cloud Solutions LTD
7
+ Author-email: info@cloudbeat.io
8
+ License: Apache-2.0
9
+ Project-URL: Source, https://github.com/cloudbeat-io/cb-kit-python
10
+ Keywords: cloudbeat testing reporting python
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.7
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.6
25
+ Description-Content-Type: text/markdown
26
+ Requires-Dist: attrs>=16.0.0
27
+ Requires-Dist: pluggy>=0.4.0
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description-content-type
32
+ Dynamic: home-page
33
+ Dynamic: keywords
34
+ Dynamic: license
35
+ Dynamic: project-url
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
@@ -0,0 +1,12 @@
1
+ cloudbeat.py
2
+ setup.py
3
+ cloudbeat_common.egg-info/PKG-INFO
4
+ cloudbeat_common.egg-info/SOURCES.txt
5
+ cloudbeat_common.egg-info/dependency_links.txt
6
+ cloudbeat_common.egg-info/requires.txt
7
+ cloudbeat_common.egg-info/top_level.txt
8
+ src/__init__.py
9
+ src/json_util.py
10
+ src/models.py
11
+ src/reporter.py
12
+ tests/test_reporter.py
@@ -0,0 +1,2 @@
1
+ attrs>=16.0.0
2
+ pluggy>=0.4.0
@@ -0,0 +1,2 @@
1
+ cloudbeat
2
+ cloudbeat_common
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,59 @@
1
+ import os
2
+ from setuptools import setup
3
+
4
+ PACKAGE = "cloudbeat-common"
5
+
6
+ classifiers = [
7
+ 'Development Status :: 5 - Production/Stable',
8
+ 'Intended Audience :: Developers',
9
+ 'License :: OSI Approved :: Apache Software License',
10
+ 'Topic :: Software Development :: Quality Assurance',
11
+ 'Topic :: Software Development :: Testing',
12
+ 'Programming Language :: Python :: 3',
13
+ 'Programming Language :: Python :: 3 :: Only',
14
+ 'Programming Language :: Python :: 3.7',
15
+ 'Programming Language :: Python :: 3.8',
16
+ 'Programming Language :: Python :: 3.9',
17
+ 'Programming Language :: Python :: 3.10',
18
+ 'Programming Language :: Python :: 3.11',
19
+ 'Programming Language :: Python :: 3.12',
20
+ ]
21
+
22
+ install_requires = [
23
+ "attrs>=16.0.0",
24
+ "pluggy>=0.4.0",
25
+ ]
26
+
27
+
28
+ def get_readme(fname):
29
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
30
+
31
+
32
+ def main():
33
+ setup(
34
+ name=PACKAGE,
35
+ version="0.0.1",
36
+ use_scm_version={"root": "..", "relative_to": __file__},
37
+ setup_requires=['setuptools_scm'],
38
+ description="Contains the common types and API client for CloudBeat",
39
+ url="https://cloudbeat.io/",
40
+ project_urls={
41
+ "Source": "https://github.com/cloudbeat-io/cb-kit-python",
42
+ },
43
+ author="CBNR Cloud Solutions LTD",
44
+ author_email="info@cloudbeat.io",
45
+ license="Apache-2.0",
46
+ classifiers=classifiers,
47
+ keywords="cloudbeat testing reporting python",
48
+ # long_description=get_readme("README.md"),
49
+ long_description_content_type="text/markdown",
50
+ packages=["cloudbeat_common"],
51
+ package_dir={"cloudbeat_common": 'src'},
52
+ install_requires=install_requires,
53
+ py_modules=['cloudbeat', 'cloudbeat_common'],
54
+ python_requires='>=3.6'
55
+ )
56
+
57
+
58
+ if __name__ == '__main__':
59
+ main()
File without changes
@@ -0,0 +1,82 @@
1
+ import json
2
+
3
+ from cloudbeat_common.models import TestResult, SuiteResult, CaseResult, StepResult
4
+
5
+
6
+ def to_json(result: TestResult):
7
+ return json.dumps(result, cls=CbResultEncoder, indent=4)
8
+
9
+
10
+ def _test_result_to_json(tr: TestResult):
11
+ if not isinstance(tr, TestResult):
12
+ return None
13
+ return {
14
+ "runId": tr.run_id,
15
+ "instanceId": tr.instance_id,
16
+ "startTime": tr.start_time,
17
+ "endTime": tr.end_time,
18
+ "duration": tr.duration,
19
+ "capabilities": tr.capabilities,
20
+ "options": tr.options,
21
+ "metaData": tr.meta_data,
22
+ "environmentVariables": tr.environment_variables,
23
+ "testAttributes": tr.test_attributes,
24
+ "suites": list(map(lambda s: _suite_result_to_json(s), tr.suites))
25
+ }
26
+
27
+
28
+ def _suite_result_to_json(r: SuiteResult):
29
+ if not isinstance(r, SuiteResult):
30
+ return None
31
+ return {
32
+ "id": r.id,
33
+ "name": r.name,
34
+ "fqn": r.fqn,
35
+ "startTime": r.start_time,
36
+ "endTime": r.end_time,
37
+ "duration": r.duration,
38
+ "status": r.status,
39
+ "cases": list(map(lambda c: _case_result_to_json(c), r.cases))
40
+ }
41
+
42
+
43
+ def _case_result_to_json(r: CaseResult):
44
+ if not isinstance(r, CaseResult):
45
+ return None
46
+ return {
47
+ "id": r.id,
48
+ "name": r.name,
49
+ "display_name": r.display_name,
50
+ "description": r.description,
51
+ "fqn": r.fqn,
52
+ "startTime": r.start_time,
53
+ "endTime": r.end_time,
54
+ "duration": r.duration,
55
+ "status": r.status,
56
+ "context": r.context,
57
+ "arguments": r.arguments,
58
+ "steps": list(map(lambda s: _step_result_to_json(s), r.steps)),
59
+ "hooks": list(map(lambda s: _step_result_to_json(s), r.hooks))
60
+ }
61
+
62
+
63
+ def _step_result_to_json(r: StepResult):
64
+ if not isinstance(r, StepResult):
65
+ return None
66
+ return {
67
+ "id": r.id,
68
+ "name": r.name,
69
+ "fqn": r.fqn,
70
+ "startTime": r.start_time,
71
+ "endTime": r.end_time,
72
+ "duration": r.duration,
73
+ "status": r.status,
74
+ "steps": list(map(lambda s: _step_result_to_json(s), r.steps))
75
+ }
76
+
77
+
78
+ class CbResultEncoder(json.JSONEncoder):
79
+ def default(self, obj):
80
+ if isinstance(obj, TestResult):
81
+ return _test_result_to_json(obj)
82
+ return super().default(obj)
@@ -0,0 +1,212 @@
1
+ import time
2
+ from typing import List
3
+
4
+ from attr import attrs, attrib
5
+ from attr import Factory
6
+ from collections import OrderedDict, defaultdict
7
+ import uuid
8
+
9
+
10
+ class TestStatus:
11
+ FAILED = 'failed'
12
+ BROKEN = 'broken'
13
+ PASSED = 'passed'
14
+ SKIPPED = 'skipped'
15
+ UNKNOWN = 'unknown'
16
+
17
+
18
+ class StepType:
19
+ GENERAL = 'general'
20
+ HOOK = 'hook'
21
+ TRANSACTION = 'transaction'
22
+ HTTP = 'http'
23
+ ASSERT = 'assert'
24
+
25
+
26
+ @attrs
27
+ class CbConfig:
28
+ is_ready = attrib(default=False)
29
+ run_id = attrib(default=None)
30
+ run_group = attrib(default=None)
31
+ instance_id = attrib(default=None)
32
+ project_id = attrib(default=None)
33
+ api_token = attrib(default=None)
34
+ api_endpoint_url = attrib(default=None)
35
+ selenium_url = attrib(default=None)
36
+ appium_url = attrib(default=None)
37
+ capabilities = attrib(default=defaultdict(OrderedDict))
38
+ options = attrib(default=defaultdict(OrderedDict))
39
+ metadata = attrib(default=defaultdict(OrderedDict))
40
+ env_vars = attrib(default=defaultdict(OrderedDict))
41
+
42
+
43
+ @attrs
44
+ class TestResult:
45
+ start_time = attrib(default=None)
46
+ end_time = attrib(default=None)
47
+ duration = attrib(default=None)
48
+
49
+ run_id = attrib(default=None)
50
+ instance_id = attrib(default=None)
51
+ capabilities = attrib(default=defaultdict(OrderedDict))
52
+ options = attrib(default=defaultdict(OrderedDict))
53
+ meta_data = attrib(default=defaultdict(OrderedDict))
54
+ environment_variables = attrib(default=defaultdict(OrderedDict))
55
+ test_attributes = attrib(default=defaultdict(OrderedDict))
56
+
57
+ hooks = attrib(default=Factory(list))
58
+ suites = attrib(default=Factory(list))
59
+ failures = attrib(default=Factory(list))
60
+ status: TestStatus = attrib(default=None)
61
+
62
+ def __iter__(self):
63
+ yield from {
64
+ "startTime": self.start_time,
65
+ "endTime": self.end_time,
66
+ "duration": self.duration,
67
+ "runId": self.run_id,
68
+ "instanceId": self.instance_id
69
+ }.items()
70
+
71
+ def __str__(self):
72
+ return json.dumps(dict(self), cls=MyJSONEncoder, ensure_ascii=False)
73
+
74
+ def start(self, run_id, instance_id, options, caps, metadata, env_vars):
75
+ self.run_id = run_id
76
+ self.instance_id = instance_id
77
+ self.start_time = int(time.time() * 1000)
78
+ self.options = options
79
+ self.capabilities = caps
80
+ self.meta_data = metadata
81
+ self.environment_variables = env_vars
82
+
83
+ def end(self):
84
+ self.end_time = int(time.time() * 1000)
85
+ self.duration = self.end_time - self.start_time
86
+
87
+
88
+ @attrs
89
+ class TestableResultBase:
90
+ id = attrib(default=str(uuid.uuid4()))
91
+ name = attrib(default=None)
92
+ display_name = attrib(default=None)
93
+ description = attrib(default=None)
94
+ fqn = attrib(default=None)
95
+ status = attrib(default=TestStatus.PASSED)
96
+ attachments = attrib(default=Factory(list))
97
+ arguments = attrib(default=Factory(list))
98
+ hooks = attrib(default=Factory(list))
99
+ start_time = attrib(default=None)
100
+ end_time = attrib(default=None)
101
+ duration = attrib(default=None)
102
+
103
+
104
+ @attrs
105
+ class SuiteResult(TestableResultBase):
106
+ cases = attrib(default=Factory(list))
107
+
108
+ def start(self, name, fqn=None):
109
+ self.name = name
110
+ self.fqn = fqn
111
+ self.start_time = int(time.time() * 1000)
112
+
113
+ def end(self, status=None):
114
+ self.end_time = int(time.time() * 1000)
115
+ self.duration = self.end_time - self.start_time
116
+ # Mark suite as FAILED if at least one of the test case has failed
117
+ has_failed_cases = any(c.status == TestStatus.FAILED for c in self.cases)
118
+ if has_failed_cases and status is None:
119
+ self.status = TestStatus.FAILED
120
+ elif status is not None:
121
+ self.status = status
122
+
123
+ def add_case(self, case_result):
124
+ self.cases.append(case_result)
125
+
126
+
127
+ @attrs
128
+ class StepResult(TestableResultBase):
129
+ type: StepType = attrib(default=None)
130
+ steps = attrib(default=Factory(list))
131
+ logs = attrib(default=Factory(list))
132
+
133
+ def start(self, name, fqn=None):
134
+ self.name = name
135
+ self.fqn = fqn
136
+ self.start_time = int(time.time() * 1000)
137
+
138
+ def end(self, status=TestStatus.PASSED):
139
+ self.end_time = int(time.time() * 1000)
140
+ self.duration = self.end_time - self.start_time
141
+ self.status = status if status is not None else TestStatus.PASSED
142
+
143
+
144
+ @attrs
145
+ class CaseResult(TestableResultBase):
146
+ context = defaultdict(OrderedDict)
147
+ steps = attrib(default=Factory(list))
148
+ _started_steps_stack: List[StepResult] = []
149
+
150
+ def start(self, name, fqn=None):
151
+ self.name = name
152
+ self.fqn = fqn
153
+ self.start_time = int(time.time() * 1000)
154
+
155
+ def end(self, status=None):
156
+ # End all unfinished steps
157
+ for _ in range(len(self._started_steps_stack)):
158
+ step_result = self._started_steps_stack.pop()
159
+ step_result.end()
160
+ # Do not override the calculated FAILED status by the status function argument
161
+ if status is not None and self.status != TestStatus.FAILED:
162
+ self.status = status
163
+ self.end_time = int(time.time() * 1000)
164
+ self.duration = self.end_time - self.start_time
165
+
166
+ def start_hook(self, name):
167
+ hook_result = StepResult()
168
+ hook_result.start(name)
169
+ hook_result.type = StepType.HOOK
170
+ self.hooks.append(hook_result)
171
+ self._started_steps_stack.append(hook_result)
172
+ return hook_result
173
+
174
+ def end_hook(self, status=TestStatus.PASSED):
175
+ if len(self.hooks) == 0:
176
+ return None
177
+ last_hook = self.hooks[len(self.hooks) - 1]
178
+ # End hook's child steps, if remain open
179
+ if len(self._started_steps_stack) > 0:
180
+ while last_step := self._started_steps_stack.pop():
181
+ if last_step == last_hook:
182
+ break
183
+ last_step.end()
184
+ last_hook.end(status)
185
+ return last_hook
186
+
187
+ def start_step(self, name, fqn=None):
188
+ # Check if there is a started step
189
+ parent_step = self._started_steps_stack[len(self._started_steps_stack) - 1] \
190
+ if len(self._started_steps_stack) > 0 else None
191
+ step_result = StepResult()
192
+ step_result.start(name, fqn)
193
+ if parent_step is None:
194
+ self.steps.append(step_result)
195
+ else:
196
+ parent_step.steps.append(step_result)
197
+ self._started_steps_stack.append(step_result)
198
+ return step_result
199
+
200
+ def end_step(self, status=TestStatus.PASSED):
201
+ if len(self._started_steps_stack) == 0 or len(self.steps) == 0:
202
+ return None
203
+ last_step = self._started_steps_stack.pop() \
204
+ if len(self._started_steps_stack) > 0 else self.steps[len(self.steps) - 1]
205
+ last_step.end(status)
206
+ if status == TestStatus.FAILED:
207
+ self.status = TestStatus.FAILED
208
+ return last_step
209
+
210
+ def add_parameters(self, parameters):
211
+ # TODO: merge with the existing parameters
212
+ self.context["params"] = parameters
@@ -0,0 +1,151 @@
1
+ import threading
2
+ from collections import OrderedDict, defaultdict
3
+ import platform
4
+
5
+ from cloudbeat_common.models import TestResult, CbConfig, SuiteResult, CaseResult, StepResult
6
+ from cloudbeat_common.json_util import to_json
7
+
8
+
9
+ class ThreadContext:
10
+ _thread_context = defaultdict(OrderedDict)
11
+ _init_thread: threading.Thread
12
+
13
+ @property
14
+ def thread_context(self):
15
+ context = self._thread_context[threading.current_thread()]
16
+ if not context and threading.current_thread() is not self._init_thread:
17
+ uuid, last_item = next(reversed(self._thread_context[self._init_thread].items()))
18
+ context[uuid] = last_item
19
+ return context
20
+
21
+ def __init__(self, *args, **kwargs):
22
+ self._init_thread = threading.current_thread()
23
+ super().__init__(*args, **kwargs)
24
+
25
+ def __setitem__(self, key, value):
26
+ self.thread_context.__setitem__(key, value)
27
+
28
+ def __getitem__(self, item):
29
+ return self.thread_context.__getitem__(item)
30
+
31
+ def __iter__(self):
32
+ return self.thread_context.__iter__()
33
+
34
+ def __reversed__(self):
35
+ return self.thread_context.__reversed__()
36
+
37
+ def get(self, key):
38
+ return self.thread_context.get(key)
39
+
40
+ def pop(self, key):
41
+ return self.thread_context.pop(key)
42
+
43
+ def cleanup(self):
44
+ stopped_threads = []
45
+ for thread in self._thread_context.keys():
46
+ if not thread.is_alive():
47
+ stopped_threads.append(thread)
48
+ for thread in stopped_threads:
49
+ del self._thread_context[thread]
50
+
51
+
52
+ class CbTestReporter:
53
+ _result: TestResult = None
54
+ _config: CbConfig = None
55
+
56
+ def __init__(self, config: CbConfig):
57
+ self._context = ThreadContext()
58
+ self._config = config
59
+
60
+ def start_instance(self):
61
+ self._result = TestResult()
62
+ self._result.start(
63
+ self._config.run_id,
64
+ self._config.instance_id,
65
+ self._config.options,
66
+ self._config.capabilities,
67
+ self._config.metadata,
68
+ self._config.env_vars)
69
+ self._add_system_attributes()
70
+
71
+ def end_instance(self) -> None:
72
+ if self._result is None:
73
+ return
74
+ self._result.end()
75
+ # Serializing json
76
+ json_str = to_json(self._result)
77
+
78
+ # Writing to sample.json
79
+ with open(".CB_TEST_RESULTS.json", "w") as outfile:
80
+ outfile.write(json_str)
81
+
82
+ def start_suite(self, name, fqn=None):
83
+ if self._result is None:
84
+ return None
85
+ suite_result = SuiteResult()
86
+ suite_result.start(name, fqn)
87
+ self._result.suites.append(suite_result)
88
+ self._context["suite"] = suite_result
89
+ self._context["case"] = None
90
+ return suite_result
91
+
92
+ def end_suite(self):
93
+ if self._context["suite"] is None:
94
+ return None
95
+ suite_result: SuiteResult = self._context["suite"]
96
+ suite_result.end()
97
+ return suite_result
98
+
99
+ def start_case(self, name, fqn=None):
100
+ if self._context["suite"] is None:
101
+ return None
102
+ case_result = CaseResult()
103
+ case_result.start(name, fqn)
104
+ suite_result: SuiteResult = self._context["suite"]
105
+ suite_result.add_case(case_result)
106
+ self._context["case"] = case_result
107
+ return case_result
108
+
109
+ def end_case(self, status=None):
110
+ case_result: CaseResult = self._context["case"] if "case" in self._context else None
111
+ if case_result is None:
112
+ return None
113
+ # TODO: end started steps of the case
114
+ case_result.end(status)
115
+ return case_result
116
+
117
+ def start_case_hook(self, name):
118
+ case_result: CaseResult = self._context["case"] if "case" in self._context else None
119
+ if case_result is None:
120
+ return None
121
+ return case_result.start_hook(name)
122
+
123
+ def end_case_hook(self, status=None):
124
+ case_result: CaseResult = self._context["case"] if "case" in self._context else None
125
+ if case_result is None:
126
+ return None
127
+ return case_result.end_hook(status)
128
+
129
+ def start_step(self, name, fqn=None):
130
+ if self._context["case"] is None:
131
+ return None
132
+ case_result: CaseResult = self._context["case"]
133
+ step_result = case_result.start_step(name, fqn)
134
+ return step_result
135
+
136
+ def end_step(self):
137
+ if self._context["case"] is None:
138
+ return None
139
+ case_result: CaseResult = self._context["case"]
140
+ return case_result.end_step()
141
+
142
+ def _add_system_attributes(self):
143
+ self._result.test_attributes["agent.hostname"] = platform.node()
144
+ self._result.test_attributes["agent.os.name"] = platform.system()
145
+ # Determine OS version
146
+ os_version = platform.version() if platform.system() != "Darwin" else platform.mac_ver()[0]
147
+ self._result.test_attributes["agent.os.version"] = os_version
148
+
149
+ @property
150
+ def result(self):
151
+ return self._result
@@ -0,0 +1,64 @@
1
+ from src.reporter import CbTestReporter
2
+ from src.models import SuiteResult, CaseResult, StepResult
3
+
4
+
5
+ def test_init(cb_reporter: CbTestReporter):
6
+ assert cb_reporter._config is not None
7
+ assert cb_reporter.result is None
8
+
9
+
10
+ def test_start_instance(cb_reporter: CbTestReporter):
11
+ cb_reporter.start_instance()
12
+ assert cb_reporter.result is not None
13
+
14
+
15
+ def test_start_suite(cb_reporter: CbTestReporter):
16
+ # cb_reporter.start_instance()
17
+ # assert cb_reporter.result is not None
18
+ cb_reporter.start_suite("my suite", "cb.python.suite")
19
+ assert len(cb_reporter.result.suites) > 0
20
+ assert cb_reporter.result.suites[0].name == "my suite"
21
+
22
+
23
+ def test_start_case(cb_reporter: CbTestReporter):
24
+ case_name = "my case"
25
+ case_fqn = "cb.python.suite.case"
26
+ case_result: CaseResult = cb_reporter.start_case(case_name, case_fqn)
27
+ assert len(cb_reporter.result.suites[0].cases) > 0
28
+ assert case_result is not None
29
+ assert cb_reporter.result.suites[0].cases[0] == case_result
30
+ assert case_result.name == case_name
31
+ assert case_result.fqn == case_fqn
32
+
33
+
34
+ def test_start_step(cb_reporter: CbTestReporter):
35
+ step_name = "my step"
36
+ step_result: StepResult = cb_reporter.start_step(step_name)
37
+ assert len(cb_reporter.result.suites[0].cases[0].steps) > 0
38
+ assert step_result is not None
39
+ assert cb_reporter.result.suites[0].cases[0].steps[0] == step_result
40
+ assert step_result.name == step_name
41
+
42
+
43
+ def test_start_sub_step(cb_reporter: CbTestReporter):
44
+ step_name = "my sub step"
45
+ step_result: StepResult = cb_reporter.start_step(step_name)
46
+ assert step_result is not None
47
+ assert len(cb_reporter.result.suites[0].cases[0].steps[0].steps) > 0, \
48
+ "Sub step must be created"
49
+
50
+
51
+ def test_end_sub_step(cb_reporter: CbTestReporter):
52
+ step_result: StepResult = cb_reporter.end_step()
53
+
54
+
55
+ def test_end_case(cb_reporter: CbTestReporter):
56
+ case_result: CaseResult = cb_reporter.end_case()
57
+
58
+
59
+ def test_end_suite(cb_reporter: CbTestReporter):
60
+ suite_result: SuiteResult = cb_reporter.end_suite()
61
+
62
+
63
+ def test_end_instance(cb_reporter: CbTestReporter):
64
+ cb_reporter.end_instance()