cloudbeat-pytest 0.0.1__py3-none-any.whl

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.
File without changes
@@ -0,0 +1,42 @@
1
+ from cloudbeat_pytest.pytest_reporter import CbPyTestReporter
2
+ import importlib
3
+ import sys
4
+
5
+
6
+ class CbContext:
7
+ class __CbContext:
8
+ _reporter: CbPyTestReporter = None
9
+
10
+ def __init__(self, reporter: CbPyTestReporter):
11
+ self._reporter = reporter
12
+
13
+ def get_webdriver_listener(self):
14
+ pass
15
+
16
+ instance = None
17
+
18
+ def __new__(cls): # __new__ always a classmethod
19
+ if not CbContext.instance:
20
+ CbContext.instance = CbContext.__Report()
21
+ return CbContext.instance
22
+
23
+ def __getattr__(self, name):
24
+ return getattr(self.instance, name)
25
+
26
+ # def __setattr__(self, name):
27
+ # return setattr(self.instance, name)
28
+
29
+ @staticmethod
30
+ def init(reporter: CbPyTestReporter):
31
+ if not CbContext.instance:
32
+ CbContext.instance = CbContext.__CbContext(reporter)
33
+ print(sys.modules)
34
+ if "cloudbeat_playwright" in sys.modules:
35
+ pw_module = importlib.import_module(".wrapper", "cloudbeat_playwright")
36
+ pw_wrapper_class = getattr(pw_module, "CbPlaywrightWrapper")
37
+ CbContext.instance.__setattr__("pw", pw_wrapper_class(reporter))
38
+ if "cloudbeat_selenium" in sys.modules:
39
+ se_module = importlib.import_module("wrapper", "cloudbeat_selenium")
40
+ se_wrapper_class = getattr(se_module, "CbSeleniumWrapper")
41
+ CbContext.instance.__setattr__("se", se_wrapper_class(reporter))
42
+ return CbContext.instance
@@ -0,0 +1,84 @@
1
+ import sys
2
+ from itertools import chain, islice
3
+
4
+ from _pytest.doctest import DoctestItem
5
+ from _pytest.nodes import Item
6
+ from _pytest.python import Class, Function, Module
7
+ from _pytest.reports import TestReport
8
+ from cloudbeat_common.models import TestStatus
9
+
10
+
11
+ def get_module_details(item: Item):
12
+ head, maybe_class, tail = islice(chain(item.nodeid.split('::'), [None], [None]), 3)
13
+ class_name = maybe_class if tail else None
14
+ file_name, path = islice(chain(reversed(head.rsplit('/', 1)), [None]), 2)
15
+ module_name = file_name.split('.')[0]
16
+ package_name = path.replace('/', '.') if path else None
17
+ fqn = f"{package_name + '.' if package_name is not None else ''}{module_name}"
18
+ return {
19
+ "package_name": package_name,
20
+ "module_name": module_name,
21
+ "class_name": class_name,
22
+ "fqn": fqn
23
+ }
24
+
25
+
26
+ def get_test_details(item: Item):
27
+ return {
28
+ "fqn": item.nodeid,
29
+ "name": item.name
30
+ }
31
+
32
+
33
+ def calculate_status(result: TestReport):
34
+ if result.skipped:
35
+ return TestStatus.SKIPPED
36
+ elif result.failed:
37
+ return TestStatus.FAILED
38
+ return TestStatus.PASSED
39
+
40
+
41
+ def get_description(item):
42
+ if isinstance(item, (Class, Function, Module, Item)):
43
+ if hasattr(item, "obj"):
44
+ doc = item.obj.__doc__
45
+ if doc is not None:
46
+ return trim_docstring(doc)
47
+ if isinstance(item, DoctestItem):
48
+ return item.reportinfo()[2]
49
+
50
+
51
+ def trim_docstring(docstring: str) -> str:
52
+ """
53
+ Convert docstring.
54
+
55
+ :param docstring: input docstring
56
+ :return: trimmed docstring
57
+ """
58
+ if not docstring:
59
+ return ''
60
+ # Convert tabs to spaces (following the normal Python rules)
61
+ # and split into a list of lines:
62
+ lines = docstring.expandtabs().splitlines()
63
+ # Determine minimum indentation (first line doesn't count):
64
+ indent = sys.maxsize
65
+ for line in lines[1:]:
66
+ stripped = line.lstrip()
67
+ if stripped:
68
+ indent = min(indent, len(line) - len(stripped))
69
+ # Remove indentation (first line is special):
70
+ trimmed = [lines[0].strip()]
71
+ if indent < sys.maxsize:
72
+ for line in lines[1:]:
73
+ trimmed.append(line[indent:].rstrip())
74
+ # Strip off trailing and leading blank lines:
75
+ while trimmed and not trimmed[-1]:
76
+ trimmed.pop()
77
+ while trimmed and not trimmed[0]:
78
+ trimmed.pop(0)
79
+ # Return a single string:
80
+ return '\n'.join(trimmed)
81
+
82
+
83
+ def get_test_parameters(item: Item):
84
+ return item.callspec.params if hasattr(item, 'callspec') else None
@@ -0,0 +1,54 @@
1
+ import pytest
2
+
3
+ from cloudbeat_pytest.pytest_reporter import CbPyTestReporter
4
+
5
+
6
+ class CbTestListener:
7
+
8
+ def __init__(self, config):
9
+ self.config = config
10
+
11
+ @pytest.hookimpl(hookwrapper=True, tryfirst=True)
12
+ def pytest_runtest_protocol(self, item):
13
+ reporter: CbPyTestReporter = item.config.cb_reporter
14
+ print(f"Starting protocol: {item.name}")
15
+ reporter.start_protocol(item)
16
+ result = (yield).get_result()
17
+ reporter.end_protocol(item)
18
+ print(f"Finished protocol: {item.name}")
19
+
20
+ @pytest.hookimpl(hookwrapper=True)
21
+ def pytest_runtest_setup(self, item):
22
+ reporter: CbPyTestReporter = item.config.cb_reporter
23
+ reporter.start_setup(item)
24
+ print(f"Starting setup hook: {item.name}")
25
+ yield
26
+ print(f"Finished setup hook: {item.name}")
27
+
28
+ @pytest.hookimpl(hookwrapper=True)
29
+ def pytest_runtest_teardown(self, item):
30
+ reporter: CbPyTestReporter = item.config.cb_reporter
31
+ reporter.start_teardown(item)
32
+ print(f"Starting teardown hook: {item.name}")
33
+ yield
34
+ print(f"Finished teardown hook: {item.name}")
35
+
36
+ @pytest.hookimpl(hookwrapper=True)
37
+ def pytest_runtest_call(self, item):
38
+ print(f"Starting call hook: {item.name}")
39
+ yield
40
+ print(f"Finished call hook: {item.name}")
41
+
42
+ @pytest.hookimpl(hookwrapper=True)
43
+ def pytest_runtest_makereport(self, item, call):
44
+ reporter: CbPyTestReporter = item.config.cb_reporter
45
+ print(f"Starting makereport hook: {item.name}")
46
+ result = (yield).get_result()
47
+ if call.when == "call":
48
+ reporter.end_call(item, result)
49
+ elif call.when == "setup":
50
+ reporter.end_setup(item, result)
51
+ elif call.when == "teardown":
52
+ reporter.end_teardown(item, result)
53
+ # call.when
54
+ print(f"Finished makereport hook: {item.name}")
@@ -0,0 +1,71 @@
1
+ import os
2
+
3
+ import pytest
4
+ from cloudbeat_common.models import CbConfig
5
+
6
+ from cloudbeat_pytest.listener import CbTestListener
7
+ from cloudbeat_pytest.pytest_reporter import CbPyTestReporter
8
+
9
+ from cloudbeat_pytest.context import CbContext
10
+
11
+
12
+ def pytest_addoption(parser):
13
+ group = parser.getgroup("cloudbeat")
14
+ group.addoption(
15
+ "--name",
16
+ action="store",
17
+ dest="name",
18
+ default="World",
19
+ help='Default "name" for hello().',
20
+ )
21
+
22
+
23
+ def get_cb_config(config):
24
+ cb_config = CbConfig()
25
+ # if os.environ.get("CB_AGENT") is not None and os.environ["CB_AGENT"] == "true":
26
+ # cb_config.is_ready = True
27
+ cb_config.is_ready = True
28
+ cb_config.run_id = os.environ.get("CB_RUN_ID")
29
+ cb_config.instance_id = os.environ.get("CB_INSTANCE_ID")
30
+ cb_config.project_id = os.environ.get("CB_PROJECT_ID")
31
+ cb_config.api_endpoint_url = os.environ.get("CB_API_URL")
32
+ cb_config.api_token = os.environ.get("CB_API_KEY")
33
+ cb_config.selenium_url = os.environ.get("CB_SELENIUM_URL")
34
+ cb_config.appium_url = os.environ.get("CB_APPIUM_URL")
35
+ if os.environ.get("CB_BROWSER_NAME") is not None:
36
+ cb_config.capabilities["browserName"] = os.environ["CB_BROWSER_NAME"]
37
+ return cb_config
38
+
39
+
40
+ def pytest_configure(config):
41
+ print("--- pytest_configure")
42
+ # if not config.option.cb_enabled:
43
+ # return
44
+ cb_config: CbConfig = get_cb_config(config)
45
+ if not cb_config.is_ready:
46
+ return
47
+ config.cb_reporter = CbPyTestReporter(cb_config)
48
+ test_listener = CbTestListener(config)
49
+ config.pluginmanager.register(test_listener, 'cloudbeat_listener')
50
+
51
+
52
+ def pytest_sessionstart(session):
53
+ if session.config.cb_reporter is None:
54
+ return
55
+ reporter: CbPyTestReporter = session.config.cb_reporter
56
+ reporter.start_instance()
57
+
58
+
59
+ def pytest_sessionfinish(session):
60
+ if session.config.cb_reporter is None:
61
+ return
62
+ reporter: CbPyTestReporter = session.config.cb_reporter
63
+ reporter.end_instance()
64
+
65
+
66
+ @pytest.fixture(scope="session")
67
+ # instantiates ini file parses object
68
+ def cbx(request) -> CbContext:
69
+ reporter = request.session.config.cb_reporter
70
+ context = CbContext.init(reporter)
71
+ return context
@@ -0,0 +1,53 @@
1
+ from cloudbeat_common.reporter import CbTestReporter
2
+ from cloudbeat_common.models import TestStatus
3
+ from _pytest.nodes import Item
4
+ from _pytest.reports import TestReport
5
+ from cloudbeat_pytest.helpers import get_module_details, get_test_details, calculate_status, get_description, get_test_parameters
6
+
7
+
8
+ class CbPyTestReporter(CbTestReporter):
9
+ def start_protocol(self, item: Item):
10
+ # Check if current test's module has already a related suite result
11
+ module_details = get_module_details(item)
12
+ current_suite_result = self._context["suite"] if "suite" in self._context else None
13
+ if current_suite_result is None or current_suite_result.fqn != module_details["fqn"]:
14
+ CbTestReporter.start_suite(self, module_details["module_name"], module_details["fqn"])
15
+ test_details = get_test_details(item)
16
+ case_result = CbTestReporter.start_case(self, test_details["name"], test_details["fqn"])
17
+ # Set case status as SKIPPED by default, because if the test is skipped,
18
+ # pytest_runtest_call hook won't be called and we won't get any indication
19
+ # if the test has been skipped
20
+ case_result.status = TestStatus.SKIPPED
21
+ case_result.description = get_description(item)
22
+ test_parameters = get_test_parameters(item)
23
+ if test_parameters is not None and len(test_parameters) > 0:
24
+ case_result.add_parameters(test_parameters)
25
+
26
+ def end_protocol(self, item: Item):
27
+ # Call end_case again (previously called in end_call),
28
+ # so end_time will include teardown hook
29
+ CbTestReporter.end_case(self)
30
+ # Check if current test's module has already a related suite result
31
+ module_details = get_module_details(item)
32
+ suite_result = self._context["suite"] if "suite" in self._context else None
33
+ if suite_result is None or suite_result.fqn != module_details["fqn"]:
34
+ next(suite_result for suite in self.result.suites if suite.fqn == module_details["fqn"])
35
+ if suite_result is None:
36
+ return
37
+ suite_result.end()
38
+
39
+ def start_setup(self, item: Item):
40
+ CbTestReporter.start_case_hook(self, "setup")
41
+
42
+ def end_setup(self, item: Item, result: TestReport):
43
+ CbTestReporter.end_case_hook(self)
44
+
45
+ def start_teardown(self, item: Item):
46
+ CbTestReporter.start_case_hook(self, "teardown")
47
+
48
+ def end_teardown(self, item: Item, result: TestReport):
49
+ CbTestReporter.end_case_hook(self)
50
+
51
+ def end_call(self, item: Item, result: TestReport):
52
+ status = calculate_status(result)
53
+ CbTestReporter.end_case(self, status)
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloudbeat-pytest
3
+ Version: 0.0.1
4
+ Summary: CloudBeat Pytest Kit
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 pytest
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Framework :: Pytest
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Classifier: Topic :: Software Development :: Testing
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.7
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Requires-Python: >=3.6
26
+ Description-Content-Type: text/markdown
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: project-url
35
+ Dynamic: requires-python
36
+ Dynamic: summary
@@ -0,0 +1,11 @@
1
+ cloudbeat_pytest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ cloudbeat_pytest/context.py,sha256=RoFiI588QdW4fpOjz5b_CjO7-hzWzM9pBdZYvyEFsSo,1507
3
+ cloudbeat_pytest/helpers.py,sha256=iDLztVjMm_MEciIv46tm3RSpDl_iVRFzUwzTSBDdaws,2549
4
+ cloudbeat_pytest/listener.py,sha256=3NIlETBcdCzrlp1iBkRdN6p-Wt79jk_hhEz8lp3D1qM,1894
5
+ cloudbeat_pytest/plugin.py,sha256=gMbVnNag3g090SlXcVkH8qkf0AexEgsStL02mvooNF0,2198
6
+ cloudbeat_pytest/pytest_reporter.py,sha256=p6b9AQfI1EftPBcXrJBuslsdiH-jfssBY9i5FYKlJ8I,2625
7
+ cloudbeat_pytest-0.0.1.dist-info/METADATA,sha256=ti9pSrcplXtf5AVThyE6638HXrEPsIEd489IhWr-svg,1353
8
+ cloudbeat_pytest-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ cloudbeat_pytest-0.0.1.dist-info/entry_points.txt,sha256=VjdlGQ940f-bUu7dnl-_7IH1X1Pq5Z6H0S1ycHBaWSQ,54
10
+ cloudbeat_pytest-0.0.1.dist-info/top_level.txt,sha256=8_w40urWHZTezqXKQqqDvJgqIV8U-XfIotH-ppu4hzQ,17
11
+ cloudbeat_pytest-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [pytest11]
2
+ cloudbeat_pytest = cloudbeat_pytest.plugin
@@ -0,0 +1 @@
1
+ cloudbeat_pytest