diff --git a/allure-pytest-bdd/src/attachment_worker.py b/allure-pytest-bdd/src/attachment_worker.py new file mode 100644 index 00000000..d2be475d --- /dev/null +++ b/allure-pytest-bdd/src/attachment_worker.py @@ -0,0 +1,61 @@ +import os.path +import os + + +class AttachmentWorker: + + def __init__(self, test_result, item): + self.test_result = test_result + self.attachments_dir = AttachmentWorker.get_path_to_attachments(item) + + def delete_duplicates(self): + if len(self.test_result.attachments) == 0: + return + + for step in self.test_result.steps: + for attach in step.attachments: + to_delete = self._find_duplicate(attach) + if to_delete is not None: + self.test_result.attachments.remove(to_delete) + os.remove(os.path.join(self.attachments_dir, to_delete.source)) + + @staticmethod + def get_path_to_attachments(item): + splitted_param = AttachmentWorker._get_allurdir_param(item).split('=') + + project_dir = str(item.config.invocation_params.dir) + if len(splitted_param) == 1: + return project_dir + + allure_dir = os.path.normpath(splitted_param[1]) + if not os.path.isabs(allure_dir): + allure_dir = os.path.join(project_dir, allure_dir.lstrip("\\")) + + return allure_dir + + def _find_duplicate(self, attachment_from_step): + for attachment in self.test_result.attachments: + if self._are_attachments_equal(attachment, attachment_from_step): + return attachment + + return None + + def _are_attachments_equal(self, first, second): + first_file = open(os.path.join(self.attachments_dir, first.source), 'br') + first_content = first_file.read() + first_file.close() + + second_file = open(os.path.join(self.attachments_dir, second.source), 'br') + second_content = second_file.read() + second_file.close() + + return \ + first.name == second.name and \ + first.type == second.type and \ + first_content == second_content + + @staticmethod + def _get_allurdir_param(item): + for param in item.config.invocation_params.args: + if param.startswith("--alluredir"): + return param diff --git a/allure-pytest-bdd/src/helper.py b/allure-pytest-bdd/src/helper.py new file mode 100644 index 00000000..73851f52 --- /dev/null +++ b/allure-pytest-bdd/src/helper.py @@ -0,0 +1,16 @@ +import allure_commons +import pytest +from .utils import ALLURE_LINK_MARK + + +class AllureTestHelper(object): + def __init__(self, config): + self.config = config + + @allure_commons.hookimpl + def decorate_as_link(self, url, link_type, name): + pattern = dict(self.config.option.allure_link_pattern).get(link_type, u'{}') + url = pattern.format(url) + 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) diff --git a/allure-pytest-bdd/src/plugin.py b/allure-pytest-bdd/src/plugin.py index a5620264..8bf7803d 100644 --- a/allure-pytest-bdd/src/plugin.py +++ b/allure-pytest-bdd/src/plugin.py @@ -2,6 +2,10 @@ import os from allure_commons.logger import AllureFileLogger from .pytest_bdd_listener import PytestBDDListener +from .helper import AllureTestHelper +from .utils import ALLURE_LINK_MARK + +import argparse def pytest_addoption(parser): @@ -17,6 +21,25 @@ def pytest_addoption(parser): dest="clean_alluredir", help="Clean alluredir folder if it exists") + 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 + + 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 cleanup_factory(plugin): def clean_up(): @@ -29,6 +52,10 @@ def pytest_configure(config): report_dir = config.option.allure_report_dir clean = config.option.clean_alluredir + test_helper = AllureTestHelper(config) + allure_commons.plugin_manager.register(test_helper) + config.add_cleanup(cleanup_factory(test_helper)) + if report_dir: report_dir = os.path.abspath(report_dir) @@ -40,3 +67,5 @@ def pytest_configure(config): file_logger = AllureFileLogger(report_dir, clean) allure_commons.plugin_manager.register(file_logger) config.add_cleanup(cleanup_factory(file_logger)) + + config.addinivalue_line("markers", "{mark}: allure link marker".format(mark=ALLURE_LINK_MARK)) diff --git a/allure-pytest-bdd/src/pytest_bdd_listener.py b/allure-pytest-bdd/src/pytest_bdd_listener.py index 0ed04f13..78fdbfca 100644 --- a/allure-pytest-bdd/src/pytest_bdd_listener.py +++ b/allure-pytest-bdd/src/pytest_bdd_listener.py @@ -2,7 +2,7 @@ import allure_commons from allure_commons.utils import now from allure_commons.utils import uuid4 -from allure_commons.model2 import Label +from allure_commons.model2 import Label, Link from allure_commons.model2 import Status from allure_commons.types import LabelType @@ -12,11 +12,14 @@ from .utils import get_step_name from .utils import get_status_details from .utils import get_pytest_report_status +from .utils import allure_links 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 .attachment_worker import AttachmentWorker + class PytestBDDListener(object): def __init__(self): @@ -46,6 +49,8 @@ def pytest_bdd_before_scenario(self, request, feature, scenario): 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.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) @@ -113,6 +118,9 @@ def pytest_runtest_makereport(self, item, call): test_result.status = status test_result.statusDetails = status_details + if test_result and test_result.status: + AttachmentWorker(test_result, item).delete_duplicates() + if report.when == 'teardown': self.lifecycle.write_test_case(uuid=uuid) diff --git a/allure-pytest-bdd/src/utils.py b/allure-pytest-bdd/src/utils.py index c17c1b88..d5a3ce93 100644 --- a/allure-pytest-bdd/src/utils.py +++ b/allure-pytest-bdd/src/utils.py @@ -7,6 +7,9 @@ from allure_commons.utils import format_exception +ALLURE_LINK_MARK = 'allure_link' + + def get_step_name(node, step): name = "{step_keyword} {step_name}".format(step_keyword=step.keyword, step_name=step.name) if hasattr(node, 'callspec'): @@ -50,3 +53,8 @@ def get_params(node): if hasattr(node, 'callspec'): params = node.callspec.params return [Parameter(name=name, value=value) for name, value in params.items()] + + +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/allure-pytest-bdd/test/acceptance.feature b/allure-pytest-bdd/test/acceptance.feature new file mode 100644 index 00000000..382af5e1 --- /dev/null +++ b/allure-pytest-bdd/test/acceptance.feature @@ -0,0 +1,16 @@ +# Created by denis at 11.01.2021 +Feature: test + # Enter feature description here + + Scenario: scenario + Given py file with name: example + And with imports: pytest, pytest_bdd, allure + And with func: + """ + @pytest_bdd.given("given_step") + def my_func(): + allure.attach("blah", ...) + raise Exception("message") + """ + And test for scenario_name from file.feature + And py file saved diff --git a/allure-pytest-bdd/test/attachments_tests/attachment_test.py b/allure-pytest-bdd/test/attachments_tests/attachment_test.py new file mode 100644 index 00000000..bd3f46a0 --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachment_test.py @@ -0,0 +1,42 @@ +from pytest_bdd import scenario + + +@scenario("attachments_features\\attachment_in_step.feature", "Attachment in Given") +def test_given_with_attachments(): + pass + + +@scenario("attachments_features\\attachment_in_step.feature", "Attachment in When") +def test_when_with_attachments(): + pass + + +@scenario("attachments_features\\attachment_in_step.feature", "Attachment in Then") +def test_then_with_attachments(): + pass + + +@scenario("attachments_features\\attachment_in_exception_step.feature", "Attachment and exception in step") +def test_step_with_attachments_and_exception(): + pass + + +@scenario("attachments_features\\attachment_in_many_steps.feature", "Attachment in many steps") +def test_attachment_in_many_steps(): + pass + + +@scenario("attachments_features\\attachment_in_failed_step.feature", "Attachment in failed step") +def test_failed_step_with_attachment(): + pass + + +@scenario("attachments_features\\attachment_in_called_fixture.feature", "Attachment in called fixture") +def test_attachment_in_called_fixture(): + pass + + +@scenario("attachments_features\\attachment_in_step-fixture.feature", + "attachment in a step, that is also a fixture") +def test_attachment_in_step_fixture(): + pass diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_called_fixture.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_called_fixture.feature new file mode 100644 index 00000000..6fe3b583 --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_called_fixture.feature @@ -0,0 +1,37 @@ +Feature: Attachments + + Scenario: Attachment in called fixture + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When when-step use fixture, that have attachment + Then passed step + """ + And py file with name: example_test + And with imports: pytest, pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest.fixture() + def fixture_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + """ + And with func: + """ + @pytest_bdd.when("when-step use fixture, that have attachment") + def step_with_attachment(fixture_with_attachment): + pass + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in attachments + And this attachment with content: + """ + Attachment content + """ + And attachments must not be in When when-step use fixture, that have attachment \ No newline at end of file diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_exception_step.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_exception_step.feature new file mode 100644 index 00000000..b0dabf85 --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_exception_step.feature @@ -0,0 +1,31 @@ +Feature: Attachments + + Scenario: Attachment and exception in step + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given step with attach and exception + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.given("step with attach and exception") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + raise Exception("Exception message") + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in Given step with attach and exception + And this attachment with content: + """ + Attachment content + """ diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_failed_step.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_failed_step.feature new file mode 100644 index 00000000..91c07ebc --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_failed_step.feature @@ -0,0 +1,31 @@ +Feature: Attachments + + Scenario: Attachment in failed step + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given failed step with attach + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.given("failed step with attach") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + assert False + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in Given failed step with attach + And this attachment with content: + """ + Attachment content + """ diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_many_steps.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_many_steps.feature new file mode 100644 index 00000000..a591261f --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_many_steps.feature @@ -0,0 +1,52 @@ +Feature: Attachments + + Scenario: Attachment in many steps + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given given step with attach + When when step with attach + Then then step with attach + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.given("given step with attach") + def given_step_with_attachment(): + allure.attach('Given Attachment content', 'given allure attachment', allure.attachment_type.TEXT) + """ + And with func: + """ + @pytest_bdd.when("when step with attach") + def when_step_with_attachment(): + allure.attach('When Attachment content', 'when allure attachment', allure.attachment_type.TEXT) + """ + And with func: + """ + @pytest_bdd.then("then step with attach") + def then_step_with_attachment(): + allure.attach('Then Attachment content', 'then allure attachment', allure.attachment_type.TEXT) + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment given allure attachment must be in Given given step with attach + And this attachment with content: + """ + Given Attachment content + """ + And attachment when allure attachment must be in When when step with attach + And this attachment with content: + """ + When Attachment content + """ + And attachment then allure attachment must be in Then then step with attach + And this attachment with content: + """ + Then Attachment content + """ diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step-fixture.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step-fixture.feature new file mode 100644 index 00000000..80370a6b --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step-fixture.feature @@ -0,0 +1,38 @@ +Feature: Attachment + + Scenario: attachment in a step, that is also a fixture + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When when-step is fixture with attachment + Then passed step using fixture + """ + And py file with name: example_test + And with imports: pytest, pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest.fixture() + @pytest_bdd.when("when-step is fixture with attachment") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + """ + And with func: + """ + @pytest_bdd.then("passed step using fixture") + def then_step(step_with_attachment): + pass + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in When when-step is fixture with attachment + And this attachment with content: + """ + Attachment content + """ + And attachments must not be in attachments \ No newline at end of file diff --git a/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step.feature b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step.feature new file mode 100644 index 00000000..6aafebf6 --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/attachments_features/attachment_in_step.feature @@ -0,0 +1,88 @@ +Feature: Attachments + + Scenario: Attachment in Given + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given step with attach + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.given("step with attach") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in Given step with attach + And this attachment with content: + """ + Attachment content + """ + + Scenario: Attachment in When + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When step with attach + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.when("step with attach") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in When step with attach + And this attachment with content: + """ + Attachment content + """ + + Scenario: Attachment in Then + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then step with attach + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @pytest_bdd.then("step with attach") + def step_with_attachment(): + allure.attach('Attachment content', 'allure attachment', allure.attachment_type.TEXT) + """ + And test for My Scenario Test from example.feature + And py file saved + + When run pytest-bdd with allure + + Then attachment allure attachment must be in Then step with attach + And this attachment with content: + """ + Attachment content + """ \ No newline at end of file diff --git a/allure-pytest-bdd/test/attachments_tests/conftest.py b/allure-pytest-bdd/test/attachments_tests/conftest.py new file mode 100644 index 00000000..2516dbed --- /dev/null +++ b/allure-pytest-bdd/test/attachments_tests/conftest.py @@ -0,0 +1,51 @@ +import os.path + +from pytest_bdd import then, parsers +import pytest + + +@pytest.fixture() +def test_context(): + return {"last checked attachment": None} + + +@then(parsers.re("this attachment with content:(?:\n)(?P[\\S|\\s]*)")) +def check_attachment_content(expected_content, test_context, testdir): + last_checked_attachment = test_context["last checked attachment"] + + file_path = os.path.join(testdir.tmpdir.strpath, last_checked_attachment["source"]) + with open(file_path, "r") as file: + actual_content = file.read() + + assert actual_content == expected_content + + +@then(parsers.parse("attachment {attachment_name} must be in {location_name}")) +def suitable_attachment(attachment_name, location_name, test_context, allure_report): + test_case_report = allure_report.test_cases[0] + + if location_name == "attachments": + attachments = test_case_report["attachments"] + else: + attachments = _get_step_report(test_case_report, location_name)["attachments"] + + suitable_attachments = [attachment for attachment in attachments + if attachment["name"] == attachment_name] + + assert len(suitable_attachments) == 1 + test_context["last checked attachment"] = suitable_attachments[0] + + +@then(parsers.parse("attachments must not be in {location_name}")) +def attachments_must_no_be_in(location_name, allure_report): + test_case_report = allure_report.test_cases[0] + + if location_name == "attachments": + assert "attachments" not in test_case_report.keys() + else: + assert "attachments" not in _get_step_report(test_case_report, location_name).keys() + + +def _get_step_report(test_case_report, step_name): + return next(step for step in test_case_report["steps"] + if step["name"] == step_name) diff --git a/allure-pytest-bdd/test/conftest.py b/allure-pytest-bdd/test/conftest.py index b925ac4d..0331298f 100644 --- a/allure-pytest-bdd/test/conftest.py +++ b/allure-pytest-bdd/test/conftest.py @@ -4,9 +4,13 @@ import allure_commons from allure_commons_test.report import AllureReport from allure_commons.logger import AllureFileLogger -from .steps import * # noqa F401 F403 +from .steps import * # noqa F401 F403 from pytest_bdd import given, when, parsers +from .py_file_builder import PyFileBuilder + +pytest_plugins = "pytester" + @contextmanager def fake_logger(path, logger): @@ -61,3 +65,61 @@ def feature_definition(name, extension, content, testdir): @when("run pytest-bdd with allure") def run(allured_testdir): allured_testdir.run_with_allure() + + +@pytest.fixture() +@given(parsers.parse("py file with name: {name}")) +def current_py_file_builder(name): + return PyFileBuilder(name) + + +@given(parsers.parse("with imports: {modules}")) +def add_imports_in_builder(modules, current_py_file_builder): + modules_names = [module.strip() for module in modules.split(",")] + current_py_file_builder.add_imports(*modules_names) + + +@given(parsers.re("with func:(?:\n)(?P[\\S|\\s]*)")) +def add_func_in_builder(content, current_py_file_builder): + current_py_file_builder.add_func(content) + + +@given("with passed steps") +def add_passed_steps(current_py_file_builder): + + passed_steps = '@pytest_bdd.given("passed step")\n' \ + '@pytest_bdd.when("passed step")\n' \ + '@pytest_bdd.then("passed step")\n' \ + 'def passed_step():\n' \ + ' pass' + + current_py_file_builder.add_func(passed_steps) + + +@given("with failed steps") +def add_failed_steps(current_py_file_builder): + + failed_steps = '@pytest_bdd.given("failed step")\n' \ + '@pytest_bdd.when("failed step")\n' \ + '@pytest_bdd.then("failed step")\n' \ + 'def failed_step():\n' \ + ' assert False' + + current_py_file_builder.add_func(failed_steps) + + +@given(parsers.parse("test for {scenario_name} from {feature_file}")) +def add_scenario_step(scenario_name, feature_file, current_py_file_builder): + + scenario_func = f'@pytest_bdd.scenario("{feature_file}", "{scenario_name}")\n' \ + 'def test_scenario():\n' \ + ' pass' + + current_py_file_builder.add_func(scenario_func) + + +@given(parsers.parse("py file saved")) +def save_py_file(current_py_file_builder, testdir): + testdir.makefile( + ".py", + **dict([(current_py_file_builder.name, current_py_file_builder.get_content())])) diff --git a/allure-pytest-bdd/features/background.feature b/allure-pytest-bdd/test/features/background.feature similarity index 100% rename from allure-pytest-bdd/features/background.feature rename to allure-pytest-bdd/test/features/background.feature diff --git a/allure-pytest-bdd/features/outline.feature b/allure-pytest-bdd/test/features/outline.feature similarity index 100% rename from allure-pytest-bdd/features/outline.feature rename to allure-pytest-bdd/test/features/outline.feature diff --git a/allure-pytest-bdd/features/scenario.feature b/allure-pytest-bdd/test/features/scenario.feature similarity index 100% rename from allure-pytest-bdd/features/scenario.feature rename to allure-pytest-bdd/test/features/scenario.feature diff --git a/allure-pytest-bdd/test/links_tests/conftest.py b/allure-pytest-bdd/test/links_tests/conftest.py new file mode 100644 index 00000000..fb7e6b69 --- /dev/null +++ b/allure-pytest-bdd/test/links_tests/conftest.py @@ -0,0 +1,23 @@ +from pytest_bdd import then, parsers + + +@then(parsers.parse( + "report has link type of {type_name} with url:\n{url}")) +def check_link(type_name, url, allure_report): + links = _get_links(allure_report) + desired_link = {"type": type_name, "url": url, "name": url} + + assert desired_link in links + + +@then(parsers.parse( + "report has link type of {type_name} with \"{link_name}\" name and url:\n{url}")) +def check_link_with_custom_name(type_name, link_name, url, allure_report): + links = _get_links(allure_report) + desired_link = {"type": type_name, "url": url, "name": link_name} + + assert desired_link in links + + +def _get_links(allure_report): + return allure_report.test_cases[0]["links"] diff --git a/allure-pytest-bdd/test/links_tests/links_features/all_links_type.feature b/allure-pytest-bdd/test/links_tests/links_features/all_links_type.feature new file mode 100644 index 00000000..f93aa4f3 --- /dev/null +++ b/allure-pytest-bdd/test/links_tests/links_features/all_links_type.feature @@ -0,0 +1,41 @@ +Feature: Links + + Scenario: All links type + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @allure.link('https://www.youtube.com/watch?v=dQw4w9WgXcQ', name='Link') + @allure.issue('140', 'Issue') + @allure.testcase( + 'https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637', + 'Test case title') + @pytest_bdd.scenario("example.feature", "My Scenario Test") + def test_scenario(): + pass + """ + And py file saved + + When run pytest-bdd with allure + + Then report has link type of link with "Link" name and url: + """ + https://www.youtube.com/watch?v=dQw4w9WgXcQ + """ + And report has link type of issue with "Issue" name and url: + """ + 140 + """ + And report has link type of test_case with "Test case title" name and url: + """ + https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637 + """ \ No newline at end of file diff --git a/allure-pytest-bdd/test/links_tests/links_features/link_issue_test_case_link.feature b/allure-pytest-bdd/test/links_tests/links_features/link_issue_test_case_link.feature new file mode 100644 index 00000000..dfb7a0d3 --- /dev/null +++ b/allure-pytest-bdd/test/links_tests/links_features/link_issue_test_case_link.feature @@ -0,0 +1,87 @@ +Feature: Links + + Scenario: Default link + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @allure.link('https://www.youtube.com/watch?v=dQw4w9WgXcQ', name='Link') + @pytest_bdd.scenario("example.feature", "My Scenario Test") + def test_scenario(): + pass + """ + And py file saved + + When run pytest-bdd with allure + + Then report has link type of link with "Link" name and url: + """ + https://www.youtube.com/watch?v=dQw4w9WgXcQ + """ + + Scenario: Issue link + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @allure.issue('140', 'Issue') + @pytest_bdd.scenario("example.feature", "My Scenario Test") + def test_scenario(): + pass + """ + And py file saved + + When run pytest-bdd with allure + + Then report has link type of issue with "Issue" name and url: + """ + 140 + """ + + Scenario: Test case link + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @allure.testcase( + 'https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637', + 'Test case title') + @pytest_bdd.scenario("example.feature", "My Scenario Test") + def test_scenario(): + pass + """ + And py file saved + + When run pytest-bdd with allure + + Then report has link type of test_case with "Test case title" name and url: + """ + https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637 + """ \ No newline at end of file diff --git a/allure-pytest-bdd/test/links_tests/links_features/link_without_name.feature b/allure-pytest-bdd/test/links_tests/links_features/link_without_name.feature new file mode 100644 index 00000000..d3c794be --- /dev/null +++ b/allure-pytest-bdd/test/links_tests/links_features/link_without_name.feature @@ -0,0 +1,29 @@ +Feature: Links + + Scenario: Link without name + Given example.feature with content: + """ + Feature: Feature Test + Scenario: My Scenario Test + Given passed step + When passed step + Then passed step + """ + And py file with name: example_test + And with imports: pytest_bdd, allure + And with passed steps + And with func: + """ + @allure.link('https://www.youtube.com/watch?v=dQw4w9WgXcQ') + @pytest_bdd.scenario("example.feature", "My Scenario Test") + def test_scenario(): + pass + """ + And py file saved + + When run pytest-bdd with allure + + Then report has link type of link with url: + """ + https://www.youtube.com/watch?v=dQw4w9WgXcQ + """ \ No newline at end of file diff --git a/allure-pytest-bdd/test/links_tests/links_test.py b/allure-pytest-bdd/test/links_tests/links_test.py new file mode 100644 index 00000000..c91c969a --- /dev/null +++ b/allure-pytest-bdd/test/links_tests/links_test.py @@ -0,0 +1,26 @@ +from pytest_bdd import scenario + + +@scenario("links_features\\link_issue_test_case_link.feature", "Default link") +def test_default_link(): + pass + + +@scenario("links_features\\link_issue_test_case_link.feature", "Issue link") +def test_issue_link(): + pass + + +@scenario("links_features\\link_issue_test_case_link.feature", "Test case link") +def test_test_case_link(): + pass + + +@scenario("links_features\\link_without_name.feature", "Link without name") +def test_link_without_name(): + pass + + +@scenario("links_features\\all_links_type.feature", "All links type") +def test_all_links_type(): + pass diff --git a/allure-pytest-bdd/test/py_file_builder.py b/allure-pytest-bdd/test/py_file_builder.py new file mode 100644 index 00000000..570bedd8 --- /dev/null +++ b/allure-pytest-bdd/test/py_file_builder.py @@ -0,0 +1,29 @@ +class PyFileBuilder: + + def __init__(self, name): + self._import_line = None + self._file_funcs = [] + self.name = name + + def add_imports(self, *modules_names): + import_list = [] + + for module in modules_names: + import_list.append("import " + module) + + if len(import_list) != 0: + self._import_line = "\n".join(import_list) + + def add_func(self, str_func): + self._file_funcs.append(str_func) + + def get_content(self): + if len(self._file_funcs) == 0: + raise Exception("There are no functions in this file") + + content = "\n\n\n".join(self._file_funcs) + + if self._import_line is not None: + content = self._import_line + "\n\n\n" + content + + return content diff --git a/allure-pytest-bdd/test/py_file_builder_test.py b/allure-pytest-bdd/test/py_file_builder_test.py new file mode 100644 index 00000000..a2154939 --- /dev/null +++ b/allure-pytest-bdd/test/py_file_builder_test.py @@ -0,0 +1,146 @@ +from .py_file_builder import PyFileBuilder +import pytest + + +def test_common_func(): + imports = ["pytest", "pytest_bdd", "allure"] + funcs = [ + """@given("given_step") +def given_func(): + allure.attach("blah", ...) + raise Exception("message")""", + """@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message")""", + """@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + ] + + expected_answer = """import pytest +import pytest_bdd +import allure + + +@given("given_step") +def given_func(): + allure.attach("blah", ...) + raise Exception("message") + + +@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message") + + +@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + + file_builder = PyFileBuilder("test") + + file_builder.add_imports(*imports) + + for func in funcs: + file_builder.add_func(func) + + assert file_builder.get_content() == expected_answer + + +def test_without_imports_func(): + funcs = [ + """@given("given_step") +def given_func(): + allure.attach("blah", ...) + raise Exception("message")""", + """@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message")""", + """@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + ] + + expected_answer = """@given("given_step") +def given_func(): + allure.attach("blah", ...) + raise Exception("message") + + +@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message") + + +@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + + file_builder = PyFileBuilder("test") + + file_builder.add_imports() + + for func in funcs: + file_builder.add_func(func) + + assert file_builder.get_content() == expected_answer + + +def test_empty_func_str(): + funcs = [ + "", + """@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message")""", + """@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + ] + + expected_answer = """ + + +@when("when_step") +def when_func(): + allure.attach("blah", ...) + raise Exception("message") + + +@then("then_step") +def then_func(): + allure.attach("blah", ...) + raise Exception("message")""" + + file_builder = PyFileBuilder("test") + + file_builder.add_imports() + + for func in funcs: + file_builder.add_func(func) + + assert file_builder.get_content() == expected_answer + + +def test_have_no_added_funcs(): + imports = ["pytest", "pytest_bdd", "allure"] + funcs = [] + + file_builder = PyFileBuilder("test") + + file_builder.add_imports(*imports) + + for func in funcs: + file_builder.add_func(func) + + with pytest.raises(Exception): + file_builder.get_content() diff --git a/allure-pytest-bdd/test/outline_test.py b/allure-pytest-bdd/test/tests/outline_test.py similarity index 100% rename from allure-pytest-bdd/test/outline_test.py rename to allure-pytest-bdd/test/tests/outline_test.py diff --git a/allure-pytest-bdd/test/scenario_test.py b/allure-pytest-bdd/test/tests/scenario_test.py similarity index 100% rename from allure-pytest-bdd/test/scenario_test.py rename to allure-pytest-bdd/test/tests/scenario_test.py