diff --git a/allure-behave/src/listener.py b/allure-behave/src/listener.py index c584f6b5..ad543526 100644 --- a/allure-behave/src/listener.py +++ b/allure-behave/src/listener.py @@ -178,6 +178,18 @@ def attach_data(self, body, name, attachment_type, extension): def attach_file(self, source, name, attachment_type, extension): self.logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) + @allure_commons.hookimpl + def global_attach_data(self, body, name, attachment_type, extension): + self.logger.global_attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) + + @allure_commons.hookimpl + def global_attach_file(self, source, name, attachment_type, extension): + self.logger.global_attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) + + @allure_commons.hookimpl + def global_error(self, message, trace): + self.logger.global_error(message=message, trace=trace) + @allure_commons.hookimpl def add_description(self, test_description): test_result = self.logger.get_test(None) diff --git a/allure-pytest-bdd/src/allure_api_listener.py b/allure-pytest-bdd/src/allure_api_listener.py index e132e8e2..0b980725 100644 --- a/allure-pytest-bdd/src/allure_api_listener.py +++ b/allure-pytest-bdd/src/allure_api_listener.py @@ -16,6 +16,9 @@ from .utils import apply_link_pattern from .utils import attach_data from .utils import attach_file +from .utils import global_attach_data +from .utils import global_attach_file +from .utils import global_error from .utils import get_link_patterns from .steps import start_step from .steps import stop_step @@ -117,3 +120,15 @@ def attach_data(self, body, name, attachment_type, extension): @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): attach_file(self.lifecycle, source, name, attachment_type, extension) + + @allure_commons.hookimpl + def global_attach_data(self, body, name, attachment_type, extension): + global_attach_data(self.lifecycle, body, name, attachment_type, extension) + + @allure_commons.hookimpl + def global_attach_file(self, source, name, attachment_type, extension): + global_attach_file(self.lifecycle, source, name, attachment_type, extension) + + @allure_commons.hookimpl + def global_error(self, message, trace): + global_error(self.lifecycle, message, trace) diff --git a/allure-pytest-bdd/src/utils.py b/allure-pytest-bdd/src/utils.py index 609a1509..43bc17a1 100644 --- a/allure-pytest-bdd/src/utils.py +++ b/allure-pytest-bdd/src/utils.py @@ -323,6 +323,30 @@ def attach_file(lifecycle, source, name, attachment_type, extension=None): ) +def global_attach_data(lifecycle, body, name, attachment_type, extension=None): + lifecycle.global_attach_data( + uuid4(), + body, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + +def global_attach_file(lifecycle, source, name, attachment_type, extension=None): + lifecycle.global_attach_file( + uuid4(), + source, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + +def global_error(lifecycle, message, trace=None): + lifecycle.global_error(message=message, trace=trace) + + def format_csv(rows): with io.StringIO() as buffer: writer = csv.writer(buffer) diff --git a/allure-pytest/setup.py b/allure-pytest/setup.py index de5ea954..64f0c1b9 100644 --- a/allure-pytest/setup.py +++ b/allure-pytest/setup.py @@ -1,18 +1,5 @@ import os -import sys from setuptools import setup -from pkg_resources import require, DistributionNotFound, VersionConflict - -try: - require("pytest-allure-adaptor") - print(""" - You have pytest-allure-adaptor installed. - You need to remove pytest-allure-adaptor from your site-packages - before installing allure-pytest, or conflicts may result. - """) - sys.exit() -except (DistributionNotFound, VersionConflict): - pass PACKAGE = "allure-pytest" diff --git a/allure-pytest/src/listener.py b/allure-pytest/src/listener.py index 21c06750..10ec29df 100644 --- a/allure-pytest/src/listener.py +++ b/allure-pytest/src/listener.py @@ -257,6 +257,30 @@ def attach_data(self, body, name, attachment_type, extension): def attach_file(self, source, name, attachment_type, extension): self.allure_logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) + @allure_commons.hookimpl + def global_attach_data(self, body, name, attachment_type, extension): + self.allure_logger.global_attach_data( + uuid4(), + body, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + @allure_commons.hookimpl + def global_attach_file(self, source, name, attachment_type, extension): + self.allure_logger.global_attach_file( + uuid4(), + source, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + @allure_commons.hookimpl + def global_error(self, message, trace): + self.allure_logger.global_error(message=message, trace=trace) + @allure_commons.hookimpl def add_title(self, test_title): test_result = self.allure_logger.get_test(None) diff --git a/allure-python-commons-test/src/report.py b/allure-python-commons-test/src/report.py index 70e8b637..1db9413d 100644 --- a/allure-python-commons-test/src/report.py +++ b/allure-python-commons-test/src/report.py @@ -99,6 +99,12 @@ def __init__(self, result): "*attachment.*" ) } + self.globals = [ + json.load(file) for _, file in self._report_items( + result, + "*globals.json" + ) + ] @staticmethod def _report_items(report_dir, glob): diff --git a/allure-python-commons-test/src/result.py b/allure-python-commons-test/src/result.py index 97803d21..7012234f 100644 --- a/allure-python-commons-test/src/result.py +++ b/allure-python-commons-test/src/result.py @@ -201,6 +201,25 @@ def has_attachment_with_content( ) +def has_global_attachment_with_content( + attachments, + content_matcher, + attach_type=None, + name=None +): + return has_entry( + "attachments", + has_item( + all_of( + has_entry("name", name) if name else anything(), + has_entry("type", attach_type) if attach_type else anything(), + has_entry("timestamp", not_none()), + has_entry("source", maps_to(attachments, content_matcher)) + ) + ) + ) + + def with_id(): return has_entry("uuid", not_none()) @@ -213,6 +232,18 @@ def has_status_details(*matchers): return has_entry("statusDetails", all_of(*matchers)) +def has_global_error(*matchers): + return has_entry( + "errors", + has_item( + all_of( + has_entry("timestamp", not_none()), + *matchers + ) + ) + ) + + def with_message_contains(string): return has_entry("message", contains_string(string)) @@ -220,6 +251,8 @@ def with_message_contains(string): def with_trace_contains(string): return has_entry("trace", contains_string(string)) +def with_no_trace(): + return not_(has_entry("trace", anything())) def with_excluded(): return has_entry("excluded", True) diff --git a/allure-python-commons/src/allure/__init__.py b/allure-python-commons/src/allure/__init__.py index 4e72e402..6fe2f270 100644 --- a/allure-python-commons/src/allure/__init__.py +++ b/allure-python-commons/src/allure/__init__.py @@ -10,6 +10,8 @@ from allure_commons._allure import Dynamic as dynamic from allure_commons._allure import step from allure_commons._allure import attach +from allure_commons._allure import global_attach +from allure_commons._allure import global_error from allure_commons._allure import manual from allure_commons.types import Severity as severity_level from allure_commons.types import AttachmentType as attachment_type @@ -38,6 +40,8 @@ "dynamic", "severity_level", "attach", + "global_attach", + "global_error", "attachment_type", "parameter_mode" ] diff --git a/allure-python-commons/src/allure_commons/_allure.py b/allure-python-commons/src/allure_commons/_allure.py index 08fb5a87..607e1cb8 100644 --- a/allure-python-commons/src/allure_commons/_allure.py +++ b/allure-python-commons/src/allure_commons/_allure.py @@ -4,6 +4,7 @@ from allure_commons._core import plugin_manager from allure_commons.types import LabelType, LinkType, ParameterMode from allure_commons.utils import uuid4 +from allure_commons.utils import format_exception, format_traceback from allure_commons.utils import func_parameters, represent _TFunc = TypeVar("_TFunc", bound=Callable[..., Any]) @@ -216,6 +217,48 @@ def file(self, source, name=None, attachment_type=None, extension=None): attach = Attach() +class GlobalAttach: + + def __call__(self, body, name=None, attachment_type=None, extension=None): + plugin_manager.hook.global_attach_data( + body=body, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + def file(self, source, name=None, attachment_type=None, extension=None): + plugin_manager.hook.global_attach_file( + source=source, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + +global_attach = GlobalAttach() + + +@overload +def global_error(value: BaseException) -> None: + ... + + +@overload +def global_error(value: str, trace: Union[str, None] = None) -> None: + ... + + +def global_error(value, trace=None): + message = None + if isinstance(value, BaseException): + message = format_exception(type(value), value) + trace = format_traceback(value.__traceback__) + else: + message = value + plugin_manager.hook.global_error(message=message, trace=trace) + + class fixture: def __init__(self, fixture_function, parent_uuid=None, name=None): self._fixture_function = fixture_function diff --git a/allure-python-commons/src/allure_commons/_hooks.py b/allure-python-commons/src/allure_commons/_hooks.py index 0ff19a27..84e916d9 100644 --- a/allure-python-commons/src/allure_commons/_hooks.py +++ b/allure-python-commons/src/allure_commons/_hooks.py @@ -66,6 +66,18 @@ def attach_data(self, body, name, attachment_type, extension): def attach_file(self, source, name, attachment_type, extension): """ attach file """ + @hookspec + def global_attach_data(self, body, name, attachment_type, extension): + """ attach global data """ + + @hookspec + def global_attach_file(self, source, name, attachment_type, extension): + """ attach global file """ + + @hookspec + def global_error(self, message, trace): + """ global error """ + class AllureDeveloperHooks: @@ -100,3 +112,7 @@ def report_attached_file(self, source, file_name): @hookspec def report_attached_data(self, body, file_name): """ reporting """ + + @hookspec + def report_globals(self, globals_item): + """ reporting """ diff --git a/allure-python-commons/src/allure_commons/lifecycle.py b/allure-python-commons/src/allure_commons/lifecycle.py index f6a4ff33..e2c2251e 100644 --- a/allure-python-commons/src/allure_commons/lifecycle.py +++ b/allure-python-commons/src/allure_commons/lifecycle.py @@ -4,6 +4,7 @@ from allure_commons.model2 import TestResultContainer from allure_commons.model2 import TestResult from allure_commons.model2 import Attachment, ATTACHMENT_PATTERN +from allure_commons.model2 import GlobalAttachment, GlobalError, Globals from allure_commons.model2 import TestStepResult from allure_commons.model2 import ExecutableItem from allure_commons.model2 import TestBeforeResult @@ -124,14 +125,11 @@ def stop_after_fixture(self, uuid=None): fixture.stop = now() def _attach(self, uuid, name=None, attachment_type=None, extension=None, parent_uuid=None): - mime_type = attachment_type - extension = extension if extension else "attach" - - if type(attachment_type) is AttachmentType: - extension = attachment_type.extension - mime_type = attachment_type.mime_type - - file_name = ATTACHMENT_PATTERN.format(prefix=uuid, ext=extension) + file_name, mime_type = self.__resolve_attachment_filename_and_type( + uuid, + attachment_type=attachment_type, + extension=extension, + ) attachment = Attachment(source=file_name, name=name, type=mime_type) last_uuid = parent_uuid if parent_uuid else self._last_item_uuid(ExecutableItem) self._items[last_uuid].attachments.append(attachment) @@ -147,3 +145,41 @@ def attach_data(self, uuid, body, name=None, attachment_type=None, extension=Non file_name = self._attach(uuid, name=name, attachment_type=attachment_type, extension=extension, parent_uuid=parent_uuid) plugin_manager.hook.report_attached_data(body=body, file_name=file_name) + + def global_attach_file(self, uuid, source, name=None, attachment_type=None, extension=None): + file_name, mime_type = self.__resolve_attachment_filename_and_type( + uuid, + attachment_type=attachment_type, + extension=extension, + ) + plugin_manager.hook.report_attached_file(source=source, file_name=file_name) + plugin_manager.hook.report_globals(globals_item=Globals(attachments=[ + GlobalAttachment(source=file_name, name=name, type=mime_type, timestamp=now()) + ])) + + def global_attach_data(self, uuid, body, name=None, attachment_type=None, extension=None): + file_name, mime_type = self.__resolve_attachment_filename_and_type( + uuid, + attachment_type=attachment_type, + extension=extension, + ) + plugin_manager.hook.report_attached_data(body=body, file_name=file_name) + plugin_manager.hook.report_globals(globals_item=Globals(attachments=[ + GlobalAttachment(source=file_name, name=name, type=mime_type, timestamp=now()) + ])) + + def global_error(self, message=None, trace=None): + plugin_manager.hook.report_globals(globals_item=Globals(errors=[ + GlobalError(message=message, trace=trace, timestamp=now()) + ])) + + def __resolve_attachment_filename_and_type(self, uuid, attachment_type=None, extension=None): + mime_type = attachment_type + extension = extension if extension else "attach" + + if type(attachment_type) is AttachmentType: + extension = attachment_type.extension + mime_type = attachment_type.mime_type + + file_name = ATTACHMENT_PATTERN.format(prefix=uuid, ext=extension) + return file_name, mime_type diff --git a/allure-python-commons/src/allure_commons/logger.py b/allure-python-commons/src/allure_commons/logger.py index da3df2da..4345bc77 100644 --- a/allure-python-commons/src/allure_commons/logger.py +++ b/allure-python-commons/src/allure_commons/logger.py @@ -47,6 +47,10 @@ def report_attached_data(self, body, file_name): else: attached_file.write(body) + @hookimpl + def report_globals(self, globals_item): + self._report_item(globals_item) + class AllureMemoryLogger: @@ -54,6 +58,7 @@ def __init__(self): self.test_cases = [] self.test_containers = [] self.attachments = {} + self.globals = [] @hookimpl def report_result(self, result): @@ -72,3 +77,8 @@ def report_attached_file(self, source, file_name): @hookimpl def report_attached_data(self, body, file_name): self.attachments[file_name] = body + + @hookimpl + def report_globals(self, globals_item): + data = asdict(globals_item, filter=lambda _, v: v or v is False) + self.globals.append(data) diff --git a/allure-python-commons/src/allure_commons/model2.py b/allure-python-commons/src/allure_commons/model2.py index a117948c..cd069b17 100644 --- a/allure-python-commons/src/allure_commons/model2.py +++ b/allure-python-commons/src/allure_commons/model2.py @@ -5,6 +5,7 @@ TEST_GROUP_PATTERN = "{prefix}-container.json" TEST_CASE_PATTERN = "{prefix}-result.json" ATTACHMENT_PATTERN = "{prefix}-attachment.{ext}" +GLOBALS_PATTERN = "{prefix}-globals.json" INDENT = 4 @@ -95,6 +96,7 @@ class StatusDetails: message = attrib(default=None) trace = attrib(default=None) + @attrs class Attachment: name = attrib(default=None) @@ -102,6 +104,24 @@ class Attachment: type = attrib(default=None) +@attrs +class GlobalAttachment(Attachment): + timestamp = attrib(default=None) + + +@attrs +class GlobalError(StatusDetails): + timestamp = attrib(default=None) + + +@attrs +class Globals: + file_pattern = GLOBALS_PATTERN + + attachments = attrib(default=Factory(list)) + errors = attrib(default=Factory(list)) + + class Status: FAILED = "failed" BROKEN = "broken" diff --git a/allure-python-commons/src/allure_commons/reporter.py b/allure-python-commons/src/allure_commons/reporter.py index 14bbeb17..7e7b7594 100644 --- a/allure-python-commons/src/allure_commons/reporter.py +++ b/allure-python-commons/src/allure_commons/reporter.py @@ -5,6 +5,7 @@ from allure_commons.model2 import ExecutableItem from allure_commons.model2 import TestResult from allure_commons.model2 import Attachment, ATTACHMENT_PATTERN +from allure_commons.model2 import GlobalAttachment, GlobalError, Globals from allure_commons.utils import now from allure_commons._core import plugin_manager @@ -140,14 +141,7 @@ def stop_step(self, uuid, **kwargs): self._items.pop(uuid) def _attach(self, uuid, name=None, attachment_type=None, extension=None, parent_uuid=None): - mime_type = attachment_type - extension = extension if extension else "attach" - - if type(attachment_type) is AttachmentType: - extension = attachment_type.extension - mime_type = attachment_type.mime_type - - file_name = ATTACHMENT_PATTERN.format(prefix=uuid, ext=extension) + file_name, mime_type = self.__resolve_attachment_filename_and_type(uuid, attachment_type, extension) attachment = Attachment(source=file_name, name=name, type=mime_type) last_uuid = parent_uuid if parent_uuid else self._last_executable() self._items[last_uuid].attachments.append(attachment) @@ -163,3 +157,33 @@ def attach_data(self, uuid, body, name=None, attachment_type=None, extension=Non file_name = self._attach(uuid, name=name, attachment_type=attachment_type, extension=extension, parent_uuid=parent_uuid) plugin_manager.hook.report_attached_data(body=body, file_name=file_name) + + def global_attach_file(self, uuid, source, name=None, attachment_type=None, extension=None): + file_name, mime_type = self.__resolve_attachment_filename_and_type(uuid, attachment_type, extension) + plugin_manager.hook.report_attached_file(source=source, file_name=file_name) + plugin_manager.hook.report_globals(globals_item=Globals(attachments=[ + GlobalAttachment(source=file_name, name=name, type=mime_type, timestamp=now()) + ])) + + def global_attach_data(self, uuid, body, name=None, attachment_type=None, extension=None): + file_name, mime_type = self.__resolve_attachment_filename_and_type(uuid, attachment_type, extension) + plugin_manager.hook.report_attached_data(body=body, file_name=file_name) + plugin_manager.hook.report_globals(globals_item=Globals(attachments=[ + GlobalAttachment(source=file_name, name=name, type=mime_type, timestamp=now()) + ])) + + def global_error(self, message=None, trace=None): + plugin_manager.hook.report_globals(globals_item=Globals(errors=[ + GlobalError(message=message, trace=trace, timestamp=now()) + ])) + + def __resolve_attachment_filename_and_type(self, uuid, attachment_type=None, extension=None): + mime_type = attachment_type + extension = extension if extension else "attach" + + if type(attachment_type) is AttachmentType: + extension = attachment_type.extension + mime_type = attachment_type.mime_type + + file_name = ATTACHMENT_PATTERN.format(prefix=uuid, ext=extension) + return file_name, mime_type diff --git a/allure-robotframework/src/library/__init__.py b/allure-robotframework/src/library/__init__.py index ba680b46..d21bce6b 100644 --- a/allure-robotframework/src/library/__init__.py +++ b/allure-robotframework/src/library/__init__.py @@ -1,3 +1,3 @@ -from .allure_library import attach, attach_file +from .allure_library import attach, attach_file, global_attach, global_attach_file, global_error -__all__ = ["attach", "attach_file"] +__all__ = ["attach", "attach_file", "global_attach", "global_attach_file", "global_error"] diff --git a/allure-robotframework/src/library/allure_library.py b/allure-robotframework/src/library/allure_library.py index 2b77f5e7..e9d4f7b1 100644 --- a/allure-robotframework/src/library/allure_library.py +++ b/allure-robotframework/src/library/allure_library.py @@ -1,7 +1,7 @@ import allure -__all__ = ["attach", "attach_file"] +__all__ = ["attach", "attach_file", "global_attach", "global_attach_file", "global_error"] def _attachment_type(name): @@ -17,3 +17,15 @@ def attach(data, name=None, attachment_type=None, extension=None): def attach_file(source, name=None, attachment_type=None, extension=None): allure.attach.file(source, name=name, attachment_type=_attachment_type(attachment_type), extension=extension) + + +def global_attach(data, name=None, attachment_type=None, extension=None): + allure.global_attach(data, name=name, attachment_type=_attachment_type(attachment_type), extension=extension) + + +def global_attach_file(source, name=None, attachment_type=None, extension=None): + allure.global_attach.file(source, name=name, attachment_type=_attachment_type(attachment_type), extension=extension) + + +def global_error(message, trace=None): + allure.global_error(message, trace=trace) diff --git a/allure-robotframework/src/listener/allure_listener.py b/allure-robotframework/src/listener/allure_listener.py index 045c491a..6be71a19 100644 --- a/allure-robotframework/src/listener/allure_listener.py +++ b/allure-robotframework/src/listener/allure_listener.py @@ -239,6 +239,30 @@ def attach_data(self, body, name, attachment_type, extension): def attach_file(self, source, name, attachment_type, extension): self.lifecycle.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) + @allure_commons.hookimpl + def global_attach_data(self, body, name, attachment_type, extension): + self.lifecycle.global_attach_data( + uuid4(), + body, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + @allure_commons.hookimpl + def global_attach_file(self, source, name, attachment_type, extension): + self.lifecycle.global_attach_file( + uuid4(), + source, + name=name, + attachment_type=attachment_type, + extension=extension, + ) + + @allure_commons.hookimpl + def global_error(self, message, trace): + self.lifecycle.global_error(message=message, trace=trace) + @allure_commons.hookimpl def start_step(self, uuid, title, params): with self.lifecycle.start_step() as step: diff --git a/tests/allure_behave/acceptance/allure_api/globals/__init__.py b/tests/allure_behave/acceptance/allure_api/globals/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/globals/globals_test.py b/tests/allure_behave/acceptance/allure_api/globals/globals_test.py new file mode 100644 index 00000000..ce1a5f48 --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/globals/globals_test.py @@ -0,0 +1,56 @@ +import textwrap +from tests.allure_behave.behave_runner import AllureBehaveRunner as Runner +from hamcrest import assert_that, equal_to, has_item, has_length +from allure_commons_test.result import has_global_attachment_with_content +from allure_commons_test.result import has_global_error +from allure_commons_test.result import with_message_contains + + +def test_global_attachment_and_error_hooks(behave_runner: Runner): + behave_runner.run_behave( + feature_literals=[ + """ + Feature: Global attachments and errors + Scenario: Global hooks + Given noop + """ + ], + step_literals=["given('noop')(lambda c: None)"], + environment_literal=textwrap.dedent( + """ + import allure + + + def before_all(context): + allure.global_attach("behave global attachment", name="behave global") + + + def after_all(context): + allure.global_error("behave global error") + """ + ), + ) + + assert_that( + behave_runner.allure_results.globals, + has_length(2), + ) + + assert_that( + behave_runner.allure_results.globals, + has_item( + has_global_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("behave global attachment"), + name="behave global" + ) + ) + ) + assert_that( + behave_runner.allure_results.globals, + has_item( + has_global_error( + with_message_contains("behave global error") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/globals/__init__.py b/tests/allure_pytest/acceptance/globals/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_pytest/acceptance/globals/globals_test.py b/tests/allure_pytest/acceptance/globals/globals_test.py new file mode 100644 index 00000000..a697ab30 --- /dev/null +++ b/tests/allure_pytest/acceptance/globals/globals_test.py @@ -0,0 +1,80 @@ +from hamcrest import assert_that, has_item, all_of +from hamcrest import equal_to, ends_with, has_length +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +from allure_commons_test.result import has_global_attachment_with_content +from allure_commons_test.result import has_global_error +from allure_commons_test.result import with_message_contains +from allure_commons_test.result import with_trace_contains +from allure_commons_test.result import with_no_trace + + +def test_globals_from_session_hooks(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_pytest( + """ + def test_globals_from_session_hooks(): + pass + """, + conftest_literal=( + """ + import allure + + + def pytest_sessionstart(session): + allure.global_attach(body="global body", name="global attachment") + allure.global_attach.file(__file__, name="global attachment file") + allure.global_error("message only error") + + + def pytest_sessionfinish(session, exitstatus): + try: + raise ValueError("error from exception") + except ValueError as error: + allure.global_error(error) + allure.global_error("message with trace", "explicit trace") + """ + ) + ) + + assert_that( + allure_results.globals, + has_length(5), + ) + + assert_that( + allure_results.globals, + all_of( + has_item( + has_global_attachment_with_content( + allure_results.attachments, + equal_to("global body"), + name="global attachment" + ) + ), + has_item( + has_global_attachment_with_content( + allure_results.attachments, + ends_with("conftest.py"), + name="global attachment file" + ) + ), + has_item( + has_global_error( + with_message_contains("message only error"), + with_no_trace(), + ) + ), + has_item( + has_global_error( + with_message_contains("ValueError: error from exception"), + with_trace_contains("raise ValueError") + ) + ), + has_item( + has_global_error( + with_message_contains("message with trace"), + with_trace_contains("explicit trace") + ) + ) + ) + ) diff --git a/tests/allure_pytest_bdd/acceptance/globals_test.py b/tests/allure_pytest_bdd/acceptance/globals_test.py new file mode 100644 index 00000000..a0bc2d06 --- /dev/null +++ b/tests/allure_pytest_bdd/acceptance/globals_test.py @@ -0,0 +1,71 @@ +from hamcrest import assert_that +from hamcrest import equal_to +from hamcrest import has_item +from hamcrest import has_length + +from allure_commons_test.result import has_global_attachment_with_content +from allure_commons_test.result import has_global_error +from allure_commons_test.result import with_message_contains + +from tests.allure_pytest.pytest_runner import AllurePytestRunner + + +def test_global_attachment_and_error_from_hook(allure_pytest_bdd_runner: AllurePytestRunner): + feature_content = ( + """ + Feature: Foo + Scenario: Bar + Given noop + """ + ) + steps_content = ( + """ + from pytest_bdd import scenario, given + + @scenario("sample.feature", "Bar") + def test_scenario(): + pass + + @given("noop") + def given_noop(): + pass + """ + ) + conftest_content = ( + """ + import allure + + def pytest_sessionstart(session): + allure.global_attach("bdd global attachment", name="bdd global") + + def pytest_sessionfinish(session, exitstatus): + allure.global_error("bdd global error") + """ + ) + + allure_results = allure_pytest_bdd_runner.run_pytest( + ("sample.feature", feature_content), + steps_content, + conftest_literal=conftest_content, + ) + + assert_that(allure_results.globals, has_length(2)) + + assert_that( + allure_results.globals, + has_item( + has_global_attachment_with_content( + allure_results.attachments, + equal_to("bdd global attachment"), + name="bdd global", + ) + ) + ) + assert_that( + allure_results.globals, + has_item( + has_global_error( + with_message_contains("bdd global error") + ) + ) + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/globals/__init__.py b/tests/allure_robotframework/acceptance/allure_api/globals/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/globals/globals_test.py b/tests/allure_robotframework/acceptance/allure_api/globals/globals_test.py new file mode 100644 index 00000000..e9152ce2 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/globals/globals_test.py @@ -0,0 +1,77 @@ +""" ./allure-robotframework/examples/attachment.rst """ + +from hamcrest import assert_that, equal_to, has_item, has_length +from tests.allure_robotframework.robot_runner import AllureRobotRunner +from allure_commons_test.result import has_global_attachment_with_content +from allure_commons_test.result import has_global_error +from allure_commons_test.result import with_message_contains + + +def test_global_attachment_and_error(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_literals={ + "global-attachment.robot": ( + """ + *** Settings *** + Library AllureLibrary + Library ./lib.py + Suite Setup Global Attach robot global attachment name=robot global + Suite Teardown Global Error robot global error + + *** Test Cases *** + Global Attachment + Add Globals From Code + """ + ), + }, + library_literals={ + "lib.py": ( + """ + import allure + + def add_globals_from_code(): + allure.global_attach(body="global body", name="global attachment") + allure.global_error("message only error") + """ + ), + }, + ) + + assert_that(robot_runner.allure_results.globals, has_length(4)) + + assert_that( + robot_runner.allure_results.globals, + has_item( + has_global_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to("robot global attachment"), + name="robot global" + ) + ) + ) + assert_that( + robot_runner.allure_results.globals, + has_item( + has_global_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to("global body"), + name="global attachment" + ) + ) + ) + assert_that( + robot_runner.allure_results.globals, + has_item( + has_global_error( + with_message_contains("robot global error") + ) + ) + ) + assert_that( + robot_runner.allure_results.globals, + has_item( + has_global_error( + with_message_contains("message only error") + ) + ) + )