From 8c8fc24e4e695e3a5b38a6a710e9d48b2892bd8a Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 19:15:40 +0300 Subject: [PATCH 01/18] Added support of base allure tags to allure-pytest-bdd. Labels like (id, title, etc.) --- allure-pytest-bdd/src/plugin.py | 16 ++++++++- allure-pytest-bdd/src/pytest_bdd_listener.py | 33 +++++++++++------- allure-pytest-bdd/src/utils.py | 36 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/allure-pytest-bdd/src/plugin.py b/allure-pytest-bdd/src/plugin.py index 5d6b8310..0aade79e 100644 --- a/allure-pytest-bdd/src/plugin.py +++ b/allure-pytest-bdd/src/plugin.py @@ -1,6 +1,9 @@ -import allure_commons import os + +import allure_commons from allure_commons.logger import AllureFileLogger + +from .helper import AllureTestHelper, AllureTitleHelper from .pytest_bdd_listener import PytestBDDListener @@ -22,9 +25,16 @@ def cleanup_factory(plugin): def clean_up(): name = allure_commons.plugin_manager.get_name(plugin) allure_commons.plugin_manager.unregister(name=name) + return clean_up +def pytest_addhooks(pluginmanager): + # Need register title hooks before conftest init + title_helper = AllureTitleHelper() + allure_commons.plugin_manager.register(title_helper) + + def pytest_configure(config): report_dir = config.option.allure_report_dir clean = False if config.option.collectonly else config.option.clean_alluredir @@ -32,6 +42,10 @@ def pytest_configure(config): if report_dir: report_dir = os.path.abspath(report_dir) + test_helper = AllureTestHelper(config) + allure_commons.plugin_manager.register(test_helper) + config.add_cleanup(cleanup_factory(test_helper)) + pytest_bdd_listener = PytestBDDListener() config.pluginmanager.register(pytest_bdd_listener) allure_commons.plugin_manager.register(pytest_bdd_listener) diff --git a/allure-pytest-bdd/src/pytest_bdd_listener.py b/allure-pytest-bdd/src/pytest_bdd_listener.py index d4c73115..a84593a2 100644 --- a/allure-pytest-bdd/src/pytest_bdd_listener.py +++ b/allure-pytest-bdd/src/pytest_bdd_listener.py @@ -1,22 +1,28 @@ -import pytest +from functools import partial + import allure_commons -from allure_commons.utils import now -from allure_commons.utils import uuid4 +import pytest +from allure_commons.lifecycle import AllureLifecycle from allure_commons.model2 import Label from allure_commons.model2 import Status - +from allure_commons.model2 import StatusDetails from allure_commons.types import LabelType, AttachmentType -from allure_commons.utils import platform_label from allure_commons.utils import host_tag, thread_tag from allure_commons.utils import md5 -from .utils import get_uuid -from .utils import get_step_name -from .utils import get_status_details -from .utils import get_pytest_report_status -from allure_commons.model2 import StatusDetails -from functools import partial -from allure_commons.lifecycle import AllureLifecycle -from .utils import get_full_name, get_name, get_params +from allure_commons.utils import now +from allure_commons.utils import platform_label +from allure_commons.utils import uuid4 + +from .utils import ( + get_uuid, + get_step_name, + get_status_details, + get_pytest_report_status, + get_full_name, + get_name, + get_params, + allure_labels, +) class PytestBDDListener: @@ -43,6 +49,7 @@ def pytest_bdd_before_scenario(self, request, feature, scenario): test_result.name = name test_result.start = now() test_result.historyId = md5(request.node.nodeid) + test_result.labels.extend([Label(name=name, value=value) for name, value in allure_labels(request.node)]) test_result.labels.append(Label(name=LabelType.HOST, value=self.host)) test_result.labels.append(Label(name=LabelType.THREAD, value=self.thread)) test_result.labels.append(Label(name=LabelType.FRAMEWORK, value="pytest-bdd")) diff --git a/allure-pytest-bdd/src/utils.py b/allure-pytest-bdd/src/utils.py index ac70aac2..b9baa6ab 100644 --- a/allure-pytest-bdd/src/utils.py +++ b/allure-pytest-bdd/src/utils.py @@ -5,6 +5,21 @@ from allure_commons.model2 import Status from allure_commons.model2 import Parameter from allure_commons.utils import format_exception +from allure_commons.types import LabelType + + +ALLURE_DESCRIPTION_MARK = 'allure_description' +ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html' +ALLURE_LABEL_MARK = 'allure_label' +ALLURE_LINK_MARK = 'allure_link' +ALLURE_UNIQUE_LABELS = [ + LabelType.SEVERITY, + LabelType.FRAMEWORK, + LabelType.HOST, + LabelType.SUITE, + LabelType.PARENT_SUITE, + LabelType.SUB_SUITE +] def get_step_name(step): @@ -48,3 +63,24 @@ def get_params(node): outline_params = params.pop('_pytest_bdd_example', {}) params.update(outline_params) return [Parameter(name=name, value=value) for name, value in params.items()] + + +def format_allure_link(config, url, link_type): + pattern = dict(config.option.allure_link_pattern).get(link_type, '{}') + return pattern.format(url) + + +def allure_labels(item): + unique_labels = dict() + labels = set() + for mark in item.iter_markers(name=ALLURE_LABEL_MARK): + label_type = mark.kwargs["label_type"] + if label_type in ALLURE_UNIQUE_LABELS: + if label_type not in unique_labels.keys(): + unique_labels[label_type] = mark.args[0] + else: + for arg in mark.args: + labels.add((label_type, arg)) + for k, v in unique_labels.items(): + labels.add((k, v)) + return labels From 994938c76fac325693906f0579e202abc48f47f7 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 19:22:01 +0300 Subject: [PATCH 02/18] Add an id label to pytest bdd tests --- tests/allure_pytest_bdd/acceptance/results/results_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/allure_pytest_bdd/acceptance/results/results_test.py b/tests/allure_pytest_bdd/acceptance/results/results_test.py index f8f35d2c..0053b4e6 100644 --- a/tests/allure_pytest_bdd/acceptance/results/results_test.py +++ b/tests/allure_pytest_bdd/acceptance/results/results_test.py @@ -14,8 +14,10 @@ ) STEPS_CONTENT = ( """ + import allure from pytest_bdd import scenario, given, when, then + @allure.id('1') @scenario("scenario.feature", "Simple passed example") def test_scenario_passes(): pass From 8af1be3daae45cf221e026a4745b4a1ee3e2242d Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 19:27:23 +0300 Subject: [PATCH 03/18] Commit the missed file --- allure-pytest-bdd/src/helper.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 allure-pytest-bdd/src/helper.py diff --git a/allure-pytest-bdd/src/helper.py b/allure-pytest-bdd/src/helper.py new file mode 100644 index 00000000..c63a1475 --- /dev/null +++ b/allure-pytest-bdd/src/helper.py @@ -0,0 +1,47 @@ +import pytest +import allure_commons +from .utils import ALLURE_DESCRIPTION_MARK, ALLURE_DESCRIPTION_HTML_MARK +from .utils import ALLURE_LABEL_MARK, ALLURE_LINK_MARK +from .utils import format_allure_link + + +class AllureTitleHelper: + @allure_commons.hookimpl + def decorate_as_title(self, test_title): + def decorator(func): + # pytest.fixture wraps function, so we need to get it directly + if getattr(func, '__pytest_wrapped__', None): + function = func.__pytest_wrapped__.obj + else: + function = func + function.__allure_display_name__ = test_title + return func + + return decorator + + +class AllureTestHelper: + def __init__(self, config): + self.config = config + + @allure_commons.hookimpl + def decorate_as_description(self, test_description): + allure_description = getattr(pytest.mark, ALLURE_DESCRIPTION_MARK) + return allure_description(test_description) + + @allure_commons.hookimpl + def decorate_as_description_html(self, test_description_html): + allure_description_html = getattr(pytest.mark, ALLURE_DESCRIPTION_HTML_MARK) + return allure_description_html(test_description_html) + + @allure_commons.hookimpl + def decorate_as_label(self, label_type, labels): + allure_label = getattr(pytest.mark, ALLURE_LABEL_MARK) + return allure_label(*labels, label_type=label_type) + + @allure_commons.hookimpl + def decorate_as_link(self, url, link_type, name): + url = format_allure_link(self.config, url, link_type) + allure_link = getattr(pytest.mark, ALLURE_LINK_MARK) + name = url if name is None else name + return allure_link(url, name=name, link_type=link_type) From 2386ff83455ef40ac730087b58e344180127a02e Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 19:45:55 +0300 Subject: [PATCH 04/18] Added the test for tags in allure-pytest-bdd --- .../acceptance/allure_tags_test.py | 69 +++++++++++++++++++ .../acceptance/results/results_test.py | 1 - 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/allure_pytest_bdd/acceptance/allure_tags_test.py diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py new file mode 100644 index 00000000..b2c122ca --- /dev/null +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -0,0 +1,69 @@ +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_history_id +from allure_commons_test.result import has_step +from allure_commons_test.result import with_status +from allure_commons_test.label import has_epic +from allure_commons_test.label import has_feature +from allure_commons_test.label import has_story +from allure_commons_test.label import has_label +from hamcrest import assert_that + +from tests.allure_pytest.pytest_runner import AllurePytestRunner + + +def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: AllurePytestRunner): + feature_content = ( + """ + Feature: Basic allure-pytest-bdd usage + Scenario: Simple passed example + Given the preconditions are satisfied + When the action is invoked + Then the postconditions are held + """ + ) + steps_content = ( + """ + from pytest_bdd import scenario, given, when, then + + @allure.id(123) + @allure.epic('My epic') + @allure.feature('My feature') + @allure.story('My story') + @scenario("scenario.feature", "Simple passed example") + def test_scenario_passes(): + pass + + @given("the preconditions are satisfied") + def given_the_preconditions_are_satisfied(): + pass + + @when("the action is invoked") + def when_the_action_is_invoked(): + pass + + @then("the postconditions are held") + def then_the_postconditions_are_held(): + pass + """ + ) + + output = allure_pytest_bdd_runner.run_pytest( + ("scenario.feature", feature_content), + steps_content + ) + + assert_that( + output, + has_test_case( + "Simple passed example", + with_status("passed"), + has_step("Given the preconditions are satisfied"), + has_step("When the action is invoked"), + has_step("Then the postconditions are held"), + has_history_id(), + has_epic("My epic"), + has_feature("My feature"), + has_story("My story"), + has_label("as_id", 123), + ) + ) diff --git a/tests/allure_pytest_bdd/acceptance/results/results_test.py b/tests/allure_pytest_bdd/acceptance/results/results_test.py index 0053b4e6..518cd758 100644 --- a/tests/allure_pytest_bdd/acceptance/results/results_test.py +++ b/tests/allure_pytest_bdd/acceptance/results/results_test.py @@ -17,7 +17,6 @@ import allure from pytest_bdd import scenario, given, when, then - @allure.id('1') @scenario("scenario.feature", "Simple passed example") def test_scenario_passes(): pass From 88b79da5960c2ec35bb5d278c0184e052839f9b6 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 19:49:21 +0300 Subject: [PATCH 05/18] Fix import --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index b2c122ca..793c400e 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -23,6 +23,7 @@ def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: Allur ) steps_content = ( """ + import allure from pytest_bdd import scenario, given, when, then @allure.id(123) From 22eb4213580e276740874012ac94919fb366d771 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 20:17:44 +0300 Subject: [PATCH 06/18] Added support description and allure tags --- allure-pytest-bdd/src/pytest_bdd_listener.py | 5 ++ allure-pytest-bdd/src/utils.py | 48 +++++++++++++++++-- .../acceptance/allure_tags_test.py | 29 +++++++---- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/allure-pytest-bdd/src/pytest_bdd_listener.py b/allure-pytest-bdd/src/pytest_bdd_listener.py index a84593a2..fcfecd34 100644 --- a/allure-pytest-bdd/src/pytest_bdd_listener.py +++ b/allure-pytest-bdd/src/pytest_bdd_listener.py @@ -22,6 +22,8 @@ get_name, get_params, allure_labels, + pytest_markers, + allure_description, ) @@ -50,11 +52,14 @@ def pytest_bdd_before_scenario(self, request, feature, scenario): test_result.start = now() test_result.historyId = md5(request.node.nodeid) test_result.labels.extend([Label(name=name, value=value) for name, value in allure_labels(request.node)]) + test_result.labels.extend( + [Label(name=LabelType.TAG, value=value) for value in pytest_markers(request.node)]) test_result.labels.append(Label(name=LabelType.HOST, value=self.host)) test_result.labels.append(Label(name=LabelType.THREAD, value=self.thread)) test_result.labels.append(Label(name=LabelType.FRAMEWORK, value="pytest-bdd")) test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) test_result.labels.append(Label(name=LabelType.FEATURE, value=feature.name)) + test_result.description = allure_description(request.node) test_result.parameters = get_params(request.node) finalizer = partial(self._scenario_finalizer, scenario) diff --git a/allure-pytest-bdd/src/utils.py b/allure-pytest-bdd/src/utils.py index b9baa6ab..e57d8aef 100644 --- a/allure-pytest-bdd/src/utils.py +++ b/allure-pytest-bdd/src/utils.py @@ -1,12 +1,13 @@ import os from uuid import UUID -from allure_commons.utils import md5 -from allure_commons.model2 import StatusDetails -from allure_commons.model2 import Status + from allure_commons.model2 import Parameter -from allure_commons.utils import format_exception +from allure_commons.model2 import Status +from allure_commons.model2 import StatusDetails from allure_commons.types import LabelType - +from allure_commons.utils import format_exception +from allure_commons.utils import md5 +from allure_commons.utils import represent ALLURE_DESCRIPTION_MARK = 'allure_description' ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html' @@ -84,3 +85,40 @@ def allure_labels(item): for k, v in unique_labels.items(): labels.add((k, v)) return labels + + +def get_marker_value(item, keyword): + marker = item.get_closest_marker(keyword) + return marker.args[0] if marker and marker.args else None + + +def allure_description(item): + description = get_marker_value(item, ALLURE_DESCRIPTION_MARK) + if description: + return description + elif hasattr(item, 'function'): + return item.function.__doc__ + + +def mark_to_str(marker): + args = [represent(arg) for arg in marker.args] + kwargs = ['{name}={value}'.format(name=key, value=represent(marker.kwargs[key])) for key in marker.kwargs] + if marker.name in ('filterwarnings', 'skip', 'skipif', 'xfail', 'usefixtures', 'tryfirst', 'trylast'): + markstr = '@pytest.mark.{name}'.format(name=marker.name) + else: + markstr = '{name}'.format(name=marker.name) + if args or kwargs: + parameters = ', '.join(args + kwargs) + markstr = '{}({})'.format(markstr, parameters) + return markstr + + +def pytest_markers(item): + for keyword in item.keywords.keys(): + if any([keyword.startswith('allure_'), keyword == 'parametrize']): + continue + marker = item.get_closest_marker(keyword) + if marker is None: + continue + + yield mark_to_str(marker) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 793c400e..55f8ddd9 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -1,11 +1,17 @@ -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_history_id -from allure_commons_test.result import has_step -from allure_commons_test.result import with_status -from allure_commons_test.label import has_epic -from allure_commons_test.label import has_feature -from allure_commons_test.label import has_story -from allure_commons_test.label import has_label +from allure_commons_test.label import ( + has_epic, + has_feature, + has_story, + has_label, + has_severity, +) +from allure_commons_test.report import ( + has_test_case, + has_history_id, + has_step, + with_status, +) +from allure_commons_test.result import has_description from hamcrest import assert_that from tests.allure_pytest.pytest_runner import AllurePytestRunner @@ -27,9 +33,12 @@ def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: Allur from pytest_bdd import scenario, given, when, then @allure.id(123) + @allure.title('My title') @allure.epic('My epic') @allure.feature('My feature') @allure.story('My story') + @allure.description('My description') + @allure.severity('critical') @scenario("scenario.feature", "Simple passed example") def test_scenario_passes(): pass @@ -62,9 +71,11 @@ def then_the_postconditions_are_held(): has_step("When the action is invoked"), has_step("Then the postconditions are held"), has_history_id(), + has_label("as_id", 123), + has_severity("critical"), has_epic("My epic"), has_feature("My feature"), has_story("My story"), - has_label("as_id", 123), + has_description("My description"), ) ) From 6365baf29a90b3cfdb9f81f115743bebe3aa8634 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 20:22:02 +0300 Subject: [PATCH 07/18] Fix import --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 55f8ddd9..3137823c 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -5,13 +5,13 @@ has_label, has_severity, ) -from allure_commons_test.report import ( +from allure_commons_test.result import ( has_test_case, has_history_id, has_step, with_status, + has_description, ) -from allure_commons_test.result import has_description from hamcrest import assert_that from tests.allure_pytest.pytest_runner import AllurePytestRunner From 8eea1d0b5876c74240037c51d09f9efb37a56814 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 20:25:01 +0300 Subject: [PATCH 08/18] Fix import --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 3137823c..462fb112 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -5,8 +5,8 @@ has_label, has_severity, ) +from allure_commons_test.report import has_test_case from allure_commons_test.result import ( - has_test_case, has_history_id, has_step, with_status, From f95c772e6690493aa9183055076c1382963da9bc Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 20:33:59 +0300 Subject: [PATCH 09/18] remove allure.title tags because it does not work --- allure-pytest-bdd/src/helper.py | 15 --------------- allure-pytest-bdd/src/plugin.py | 8 +------- .../acceptance/allure_tags_test.py | 1 - 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/allure-pytest-bdd/src/helper.py b/allure-pytest-bdd/src/helper.py index c63a1475..9f051bf0 100644 --- a/allure-pytest-bdd/src/helper.py +++ b/allure-pytest-bdd/src/helper.py @@ -5,21 +5,6 @@ from .utils import format_allure_link -class AllureTitleHelper: - @allure_commons.hookimpl - def decorate_as_title(self, test_title): - def decorator(func): - # pytest.fixture wraps function, so we need to get it directly - if getattr(func, '__pytest_wrapped__', None): - function = func.__pytest_wrapped__.obj - else: - function = func - function.__allure_display_name__ = test_title - return func - - return decorator - - class AllureTestHelper: def __init__(self, config): self.config = config diff --git a/allure-pytest-bdd/src/plugin.py b/allure-pytest-bdd/src/plugin.py index 0aade79e..8f14db90 100644 --- a/allure-pytest-bdd/src/plugin.py +++ b/allure-pytest-bdd/src/plugin.py @@ -3,7 +3,7 @@ import allure_commons from allure_commons.logger import AllureFileLogger -from .helper import AllureTestHelper, AllureTitleHelper +from .helper import AllureTestHelper from .pytest_bdd_listener import PytestBDDListener @@ -29,12 +29,6 @@ def clean_up(): return clean_up -def pytest_addhooks(pluginmanager): - # Need register title hooks before conftest init - title_helper = AllureTitleHelper() - allure_commons.plugin_manager.register(title_helper) - - def pytest_configure(config): report_dir = config.option.allure_report_dir clean = False if config.option.collectonly else config.option.clean_alluredir diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 462fb112..4bf94736 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -33,7 +33,6 @@ def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: Allur from pytest_bdd import scenario, given, when, then @allure.id(123) - @allure.title('My title') @allure.epic('My epic') @allure.feature('My feature') @allure.story('My story') From 298b68540be6b4f4b27b71dd38a99cd179f25ad4 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 20:35:50 +0300 Subject: [PATCH 10/18] cleaning --- tests/allure_pytest_bdd/acceptance/results/results_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/allure_pytest_bdd/acceptance/results/results_test.py b/tests/allure_pytest_bdd/acceptance/results/results_test.py index 518cd758..f8f35d2c 100644 --- a/tests/allure_pytest_bdd/acceptance/results/results_test.py +++ b/tests/allure_pytest_bdd/acceptance/results/results_test.py @@ -14,7 +14,6 @@ ) STEPS_CONTENT = ( """ - import allure from pytest_bdd import scenario, given, when, then @scenario("scenario.feature", "Simple passed example") From 8f01c930385f045ff9b3032d4f3ef29593b1b4a8 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 21:18:48 +0300 Subject: [PATCH 11/18] Added links support --- allure-pytest-bdd/src/plugin.py | 21 +++++++ allure-pytest-bdd/src/pytest_bdd_listener.py | 5 ++ allure-pytest-bdd/src/utils.py | 5 ++ .../acceptance/allure_tags_test.py | 60 ++++++++++++++++++- 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/allure-pytest-bdd/src/plugin.py b/allure-pytest-bdd/src/plugin.py index 8f14db90..1095d2a0 100644 --- a/allure-pytest-bdd/src/plugin.py +++ b/allure-pytest-bdd/src/plugin.py @@ -1,3 +1,4 @@ +import argparse import os import allure_commons @@ -20,6 +21,26 @@ def pytest_addoption(parser): dest="clean_alluredir", help="Clean alluredir folder if it exists") + parser.getgroup("general").addoption('--allure-link-pattern', + action="append", + dest="allure_link_pattern", + metavar="LINK_TYPE:LINK_PATTERN", + default=[], + type=link_pattern, + help="""Url pattern for link type. Allows short links in test, + like 'issue-1'. Text will be formatted to full url with python + str.format().""") + + +def link_pattern(string): + pattern = string.split(':', 1) + if not pattern[0]: + raise argparse.ArgumentTypeError('Link type is mandatory.') + + if len(pattern) != 2: + raise argparse.ArgumentTypeError('Link pattern is mandatory') + return pattern + def cleanup_factory(plugin): def clean_up(): diff --git a/allure-pytest-bdd/src/pytest_bdd_listener.py b/allure-pytest-bdd/src/pytest_bdd_listener.py index fcfecd34..f195747d 100644 --- a/allure-pytest-bdd/src/pytest_bdd_listener.py +++ b/allure-pytest-bdd/src/pytest_bdd_listener.py @@ -6,6 +6,7 @@ from allure_commons.model2 import Label from allure_commons.model2 import Status from allure_commons.model2 import StatusDetails +from allure_commons.model2 import Link from allure_commons.types import LabelType, AttachmentType from allure_commons.utils import host_tag, thread_tag from allure_commons.utils import md5 @@ -24,6 +25,7 @@ allure_labels, pytest_markers, allure_description, + allure_links, ) @@ -60,6 +62,9 @@ def pytest_bdd_before_scenario(self, request, feature, scenario): test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) test_result.labels.append(Label(name=LabelType.FEATURE, value=feature.name)) test_result.description = allure_description(request.node) + test_result.links.extend( + [Link(link_type, url, name) for link_type, url, name in allure_links(request.node)] + ) test_result.parameters = get_params(request.node) finalizer = partial(self._scenario_finalizer, scenario) diff --git a/allure-pytest-bdd/src/utils.py b/allure-pytest-bdd/src/utils.py index e57d8aef..df41322d 100644 --- a/allure-pytest-bdd/src/utils.py +++ b/allure-pytest-bdd/src/utils.py @@ -122,3 +122,8 @@ def pytest_markers(item): continue yield mark_to_str(marker) + + +def allure_links(item): + for mark in item.iter_markers(name=ALLURE_LINK_MARK): + yield mark.kwargs["link_type"], mark.args[0], mark.kwargs["name"] diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 4bf94736..13a2b096 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -11,9 +11,11 @@ has_step, with_status, has_description, + has_link, + has_issue_link, + has_test_case_link, ) from hamcrest import assert_that - from tests.allure_pytest.pytest_runner import AllurePytestRunner @@ -78,3 +80,59 @@ def then_the_postconditions_are_held(): has_description("My description"), ) ) + + +def test_simple_passed_scenario_with_links(allure_pytest_bdd_runner: AllurePytestRunner): + feature_content = ( + """ + Feature: Basic allure-pytest-bdd usage + Scenario: Simple passed example + Given the preconditions are satisfied + When the action is invoked + Then the postconditions are held + """ + ) + steps_content = ( + """ + import allure + from pytest_bdd import scenario, given, when, then + + @allure.link('https://example.org/simple-link') + @allure.issue('https://example.org/issue') + @allure.testcase('https://example.org/testcase') + @scenario("scenario.feature", "Simple passed example") + def test_scenario_passes(): + pass + + @given("the preconditions are satisfied") + def given_the_preconditions_are_satisfied(): + pass + + @when("the action is invoked") + def when_the_action_is_invoked(): + pass + + @then("the postconditions are held") + def then_the_postconditions_are_held(): + pass + """ + ) + + output = allure_pytest_bdd_runner.run_pytest( + ("scenario.feature", feature_content), + steps_content + ) + + assert_that( + output, + has_test_case( + "Simple passed example", + with_status("passed"), + has_step("Given the preconditions are satisfied"), + has_step("When the action is invoked"), + has_step("Then the postconditions are held"), + has_link('https://example.org/simple-link'), + has_issue_link('https://example.org/issue'), + has_test_case_link(), + ) + ) From 4f993be98b98db0e510cd8bae1c1b1bcddaecddf Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 21:22:34 +0300 Subject: [PATCH 12/18] fix test --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 13a2b096..acf6ad3f 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -133,6 +133,6 @@ def then_the_postconditions_are_held(): has_step("Then the postconditions are held"), has_link('https://example.org/simple-link'), has_issue_link('https://example.org/issue'), - has_test_case_link(), + has_test_case_link('https://example.org/testcase'), ) ) From 1d6763699dd80cc1b44ef5b3658b9568c2d3a64f Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 21:33:25 +0300 Subject: [PATCH 13/18] Added tests --- .../acceptance/allure_tags_test.py | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index acf6ad3f..380e2cdb 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -1,3 +1,4 @@ +import pytest from allure_commons_test.label import ( has_epic, has_feature, @@ -16,11 +17,13 @@ has_test_case_link, ) from hamcrest import assert_that + from tests.allure_pytest.pytest_runner import AllurePytestRunner -def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: AllurePytestRunner): - feature_content = ( +@pytest.fixture +def feature_content(): + return ( """ Feature: Basic allure-pytest-bdd usage Scenario: Simple passed example @@ -29,6 +32,9 @@ def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: Allur Then the postconditions are held """ ) + + +def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str): steps_content = ( """ import allure @@ -76,22 +82,71 @@ def then_the_postconditions_are_held(): has_severity("critical"), has_epic("My epic"), has_feature("My feature"), + has_feature("Basic allure-pytest-bdd usage"), has_story("My story"), has_description("My description"), ) ) -def test_simple_passed_scenario_with_links(allure_pytest_bdd_runner: AllurePytestRunner): - feature_content = ( +def test_simple_passed_scenario_with_multiply_allure_tags( + allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str +): + steps_content = ( """ - Feature: Basic allure-pytest-bdd usage - Scenario: Simple passed example - Given the preconditions are satisfied - When the action is invoked - Then the postconditions are held + import allure + from pytest_bdd import scenario, given, when, then + + @allure.epic('My epic 1') + @allure.epic('My epic 2') + @allure.feature('My feature 2') + @allure.feature('My feature 3') + @allure.story('My story 1') + @allure.story('My story 2') + @scenario("scenario.feature", "Simple passed example") + def test_scenario_passes(): + pass + + @given("the preconditions are satisfied") + def given_the_preconditions_are_satisfied(): + pass + + @when("the action is invoked") + def when_the_action_is_invoked(): + pass + + @then("the postconditions are held") + def then_the_postconditions_are_held(): + pass """ ) + + output = allure_pytest_bdd_runner.run_pytest( + ("scenario.feature", feature_content), + steps_content + ) + + assert_that( + output, + has_test_case( + "Simple passed example", + with_status("passed"), + has_step("Given the preconditions are satisfied"), + has_step("When the action is invoked"), + has_step("Then the postconditions are held"), + has_history_id(), + has_epic("My epic 1"), + has_epic("My epic 2"), + has_feature("Basic allure-pytest-bdd usage"), + has_feature("My feature 2"), + has_feature("My feature 3"), + has_story("My story 1"), + has_story("My story 2"), + ) + ) + + +def test_simple_passed_scenario_with_links(allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str): steps_content = ( """ import allure From 0d1783aed71bfb0fb4568a8b168672ebc6d71099 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 22:23:29 +0300 Subject: [PATCH 14/18] Unify the test --- .../acceptance/allure_tags_test.py | 206 +++++++++--------- 1 file changed, 107 insertions(+), 99 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 380e2cdb..8e649333 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -1,10 +1,15 @@ +from typing import Any + import pytest from allure_commons_test.label import ( has_epic, has_feature, has_story, has_label, - has_severity, + has_parent_suite, + has_suite, + has_sub_suite, + has_tag, ) from allure_commons_test.report import has_test_case from allure_commons_test.result import ( @@ -34,107 +39,47 @@ def feature_content(): ) -def test_simple_passed_scenario_with_allure_tags(allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str): - steps_content = ( +@pytest.mark.parametrize("test_name,decorators,checks", [ + ( + "suites_labels", + """ + @allure.suite('My Suite') + @allure.parent_suite('My Parent Suite') + @allure.sub_suite('My Sub Suite') + """, + ( + has_parent_suite('My Parent Suite'), + has_suite('My Suite'), + has_sub_suite('My Sub Suite'), + ), + ), + ( + "bdd_labels", """ - import allure - from pytest_bdd import scenario, given, when, then - - @allure.id(123) @allure.epic('My epic') @allure.feature('My feature') @allure.story('My story') @allure.description('My description') - @allure.severity('critical') - @scenario("scenario.feature", "Simple passed example") - def test_scenario_passes(): - pass - - @given("the preconditions are satisfied") - def given_the_preconditions_are_satisfied(): - pass - - @when("the action is invoked") - def when_the_action_is_invoked(): - pass - - @then("the postconditions are held") - def then_the_postconditions_are_held(): - pass - """ - ) - - output = allure_pytest_bdd_runner.run_pytest( - ("scenario.feature", feature_content), - steps_content - ) - - assert_that( - output, - has_test_case( - "Simple passed example", - with_status("passed"), - has_step("Given the preconditions are satisfied"), - has_step("When the action is invoked"), - has_step("Then the postconditions are held"), - has_history_id(), - has_label("as_id", 123), - has_severity("critical"), + """, + ( + has_feature("Basic allure-pytest-bdd usage"), # from the scenario has_epic("My epic"), has_feature("My feature"), - has_feature("Basic allure-pytest-bdd usage"), has_story("My story"), has_description("My description"), - ) - ) - - -def test_simple_passed_scenario_with_multiply_allure_tags( - allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str -): - steps_content = ( + ), + ), + ( + "bdd_multiply_labels", """ - import allure - from pytest_bdd import scenario, given, when, then - @allure.epic('My epic 1') @allure.epic('My epic 2') @allure.feature('My feature 2') @allure.feature('My feature 3') @allure.story('My story 1') @allure.story('My story 2') - @scenario("scenario.feature", "Simple passed example") - def test_scenario_passes(): - pass - - @given("the preconditions are satisfied") - def given_the_preconditions_are_satisfied(): - pass - - @when("the action is invoked") - def when_the_action_is_invoked(): - pass - - @then("the postconditions are held") - def then_the_postconditions_are_held(): - pass - """ - ) - - output = allure_pytest_bdd_runner.run_pytest( - ("scenario.feature", feature_content), - steps_content - ) - - assert_that( - output, - has_test_case( - "Simple passed example", - with_status("passed"), - has_step("Given the preconditions are satisfied"), - has_step("When the action is invoked"), - has_step("Then the postconditions are held"), - has_history_id(), + """, + ( has_epic("My epic 1"), has_epic("My epic 2"), has_feature("Basic allure-pytest-bdd usage"), @@ -142,19 +87,84 @@ def then_the_postconditions_are_held(): has_feature("My feature 3"), has_story("My story 1"), has_story("My story 2"), - ) - ) - - -def test_simple_passed_scenario_with_links(allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str): - steps_content = ( + ), + ), + ( + "id_label", + """ + @allure.id(123) + """, + ( + has_label("as_id", 123), + ), + ), + ( + "severity_label", + """ + @allure.severity('critical') + """, + ( + has_label("as_id", 123), + ), + ), + ( + "manual_label", + """ + @allure.severity('critical') + """, + ( + has_label("ALLURE_MANUAL", True), + ), + ), + ( + "tags", + """ + @pytest.mark.cool + @pytest.mark.stuff + @allure.tag('foo') + """, + ( + has_tag("cool"), + has_tag("stuff"), + has_tag("foo"), + ), + ), + ( + "custom_labels", # !!! + """ + @allure.label("Application", "desktop", "mobile") + """, + ( + has_label("Application", "desktop"), + has_label("Application", "mobile"), + ), + ), + ( + "links", """ - import allure - from pytest_bdd import scenario, given, when, then - @allure.link('https://example.org/simple-link') @allure.issue('https://example.org/issue') @allure.testcase('https://example.org/testcase') + """, + ( + has_link('https://example.org/simple-link'), + has_issue_link('https://example.org/issue'), + has_test_case_link('https://example.org/testcase'), + ), + ), +]) +def test_simple_passed_scenario_with_allure_tags( + test_name: str, + decorators: str, + checks: tuple[Any], + allure_pytest_bdd_runner: AllurePytestRunner, + feature_content: str, +): + steps_content = f""" + import allure + from pytest_bdd import scenario, given, when, then + +{decorators} @scenario("scenario.feature", "Simple passed example") def test_scenario_passes(): pass @@ -171,7 +181,6 @@ def when_the_action_is_invoked(): def then_the_postconditions_are_held(): pass """ - ) output = allure_pytest_bdd_runner.run_pytest( ("scenario.feature", feature_content), @@ -186,8 +195,7 @@ def then_the_postconditions_are_held(): has_step("Given the preconditions are satisfied"), has_step("When the action is invoked"), has_step("Then the postconditions are held"), - has_link('https://example.org/simple-link'), - has_issue_link('https://example.org/issue'), - has_test_case_link('https://example.org/testcase'), + has_history_id(), + *checks, ) ) From 031b3da09ae0eb3d0fdaf627cf4ec2790a1fa397 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 22:27:57 +0300 Subject: [PATCH 15/18] Unify the test --- .../acceptance/allure_tags_test.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 8e649333..172ca192 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -46,7 +46,7 @@ def feature_content(): @allure.suite('My Suite') @allure.parent_suite('My Parent Suite') @allure.sub_suite('My Sub Suite') - """, + """.strip(), ( has_parent_suite('My Parent Suite'), has_suite('My Suite'), @@ -60,7 +60,7 @@ def feature_content(): @allure.feature('My feature') @allure.story('My story') @allure.description('My description') - """, + """.strip(), ( has_feature("Basic allure-pytest-bdd usage"), # from the scenario has_epic("My epic"), @@ -78,7 +78,7 @@ def feature_content(): @allure.feature('My feature 3') @allure.story('My story 1') @allure.story('My story 2') - """, + """.strip(), ( has_epic("My epic 1"), has_epic("My epic 2"), @@ -93,7 +93,7 @@ def feature_content(): "id_label", """ @allure.id(123) - """, + """.strip(), ( has_label("as_id", 123), ), @@ -102,7 +102,7 @@ def feature_content(): "severity_label", """ @allure.severity('critical') - """, + """.strip(), ( has_label("as_id", 123), ), @@ -111,7 +111,7 @@ def feature_content(): "manual_label", """ @allure.severity('critical') - """, + """.strip(), ( has_label("ALLURE_MANUAL", True), ), @@ -122,7 +122,7 @@ def feature_content(): @pytest.mark.cool @pytest.mark.stuff @allure.tag('foo') - """, + """.strip(), ( has_tag("cool"), has_tag("stuff"), @@ -133,7 +133,7 @@ def feature_content(): "custom_labels", # !!! """ @allure.label("Application", "desktop", "mobile") - """, + """.strip(), ( has_label("Application", "desktop"), has_label("Application", "mobile"), @@ -145,7 +145,7 @@ def feature_content(): @allure.link('https://example.org/simple-link') @allure.issue('https://example.org/issue') @allure.testcase('https://example.org/testcase') - """, + """.strip(), ( has_link('https://example.org/simple-link'), has_issue_link('https://example.org/issue'), @@ -164,7 +164,7 @@ def test_simple_passed_scenario_with_allure_tags( import allure from pytest_bdd import scenario, given, when, then -{decorators} + {decorators} @scenario("scenario.feature", "Simple passed example") def test_scenario_passes(): pass From c54c38d5d96c1e85ee24668aa69b58342b020f63 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 22:30:40 +0300 Subject: [PATCH 16/18] Unify the test --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 172ca192..8de9363f 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Tuple import pytest from allure_commons_test.label import ( @@ -156,7 +156,7 @@ def feature_content(): def test_simple_passed_scenario_with_allure_tags( test_name: str, decorators: str, - checks: tuple[Any], + checks: Tuple[Any], allure_pytest_bdd_runner: AllurePytestRunner, feature_content: str, ): From 4bc7f25eef477aecc839cd343be70ae4b1dc1edd Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Thu, 23 May 2024 22:35:50 +0300 Subject: [PATCH 17/18] Unify the test --- tests/allure_pytest_bdd/acceptance/allure_tags_test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py index 8de9363f..4c5cee09 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_tags_test.py @@ -10,6 +10,7 @@ has_suite, has_sub_suite, has_tag, + has_severity, ) from allure_commons_test.report import has_test_case from allure_commons_test.result import ( @@ -104,13 +105,13 @@ def feature_content(): @allure.severity('critical') """.strip(), ( - has_label("as_id", 123), + has_severity("critical"), ), ), ( "manual_label", """ - @allure.severity('critical') + @allure.manual """.strip(), ( has_label("ALLURE_MANUAL", True), @@ -162,6 +163,7 @@ def test_simple_passed_scenario_with_allure_tags( ): steps_content = f""" import allure + import pytest from pytest_bdd import scenario, given, when, then {decorators} From cb5ed75562c3cbdc653a0f6081780ad06d780464 Mon Sep 17 00:00:00 2001 From: Anton Shvetsov Date: Mon, 3 Jun 2024 15:23:25 +0300 Subject: [PATCH 18/18] Fixed review issues --- .../{allure_tags_test.py => allure_decorators_test.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/allure_pytest_bdd/acceptance/{allure_tags_test.py => allure_decorators_test.py} (98%) diff --git a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py b/tests/allure_pytest_bdd/acceptance/allure_decorators_test.py similarity index 98% rename from tests/allure_pytest_bdd/acceptance/allure_tags_test.py rename to tests/allure_pytest_bdd/acceptance/allure_decorators_test.py index 4c5cee09..3ec595cc 100644 --- a/tests/allure_pytest_bdd/acceptance/allure_tags_test.py +++ b/tests/allure_pytest_bdd/acceptance/allure_decorators_test.py @@ -154,7 +154,7 @@ def feature_content(): ), ), ]) -def test_simple_passed_scenario_with_allure_tags( +def test_simple_passed_scenario_with_allure_decorators( test_name: str, decorators: str, checks: Tuple[Any],