diff --git a/allure-pytest/src/utils.py b/allure-pytest/src/utils.py index 31ffb63f..cc536cd4 100644 --- a/allure-pytest/src/utils.py +++ b/allure-pytest/src/utils.py @@ -31,21 +31,31 @@ class ParsedPytestNodeId: - def __init__(self, nodeid): - filepath, *class_names, function_segment = ensure_len(nodeid.split("::"), 2) + def __init__(self, item: pytest.Item): + chain = item.listchain() + filepath = next( + ( + node.nodeid + for node in reversed(chain) + if isinstance(node, pytest.File) + ), + item.nodeid.split("::", 1)[0], + ) self.filepath = filepath self.path_segments = filepath.split("/") *parent_dirs, filename = ensure_len(self.path_segments, 1) self.parent_package = ".".join(parent_dirs) self.module = filename.rsplit(".", 1)[0] self.package = ".".join(filter(None, [self.parent_package, self.module])) - self.class_names = class_names - self.test_function = function_segment.split("[", 1)[0] + self.class_names = [node.name for node in chain if isinstance(node, pytest.Class)] + self.test_function = ( + item.originalname if isinstance(item, pytest.Function) else item.name + ) @stashed def parse_nodeid(item): - return ParsedPytestNodeId(item.nodeid) + return ParsedPytestNodeId(item) def get_marker_value(item, keyword): @@ -140,7 +150,7 @@ def allure_name(item, parameters, param_id=None): def allure_full_name(item: pytest.Item): nodeid = parse_nodeid(item) class_part = ("." + ".".join(nodeid.class_names)) if nodeid.class_names else "" - test = item.originalname if isinstance(item, pytest.Function) else nodeid.test_function + test = nodeid.test_function full_name = f"{nodeid.package}{class_part}#{test}" return full_name diff --git a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py index 2e7a754b..3a7561b9 100644 --- a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py +++ b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py @@ -1,8 +1,11 @@ -from hamcrest import assert_that, has_entry, ends_with, all_of +from hamcrest import assert_that, has_entry, ends_with, all_of, anything, not_ from tests.allure_pytest.pytest_runner import AllurePytestRunner +from allure_commons_test.label import has_sub_suite from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_full_name from allure_commons_test.result import has_parameter +from allure_commons_test.result import has_title_path from allure_commons_test.result import with_excluded from allure_commons_test.result import with_mode @@ -277,3 +280,45 @@ def test_fullname_with_braces(allure_pytest_runner: AllurePytestRunner): has_parameter("param1", "'qwe]['") ) ) + + +def test_parameter_id_with_double_colon(allure_pytest_runner: AllurePytestRunner): + """ + >>> import pytest + + >>> @pytest.mark.parametrize("value", ["foo::bar"]) + ... def test_function(value): + ... pass + + >>> class TestOuter: + ... class TestInner: + ... @pytest.mark.parametrize("value", ["foo::bar"]) + ... def test_method(self, value): + ... pass + """ + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + all_of( + has_test_case( + "test_function[foo::bar]", + has_full_name(ends_with("#test_function")), + has_title_path("test_parameter_id_with_double_colon.py"), + not_(has_sub_suite(anything())), + has_parameter("value", "'foo::bar'") + ), + has_test_case( + "test_method[foo::bar]", + has_full_name(ends_with(".TestOuter.TestInner#test_method")), + has_title_path( + "test_parameter_id_with_double_colon.py", + "TestOuter", + "TestInner", + ), + has_sub_suite("TestOuter > TestInner"), + has_parameter("value", "'foo::bar'") + ), + ) + ) diff --git a/tests/allure_pytest/acceptance/titlepath/titlepath_test.py b/tests/allure_pytest/acceptance/titlepath/titlepath_test.py index f731cedd..a1762d3b 100644 --- a/tests/allure_pytest/acceptance/titlepath/titlepath_test.py +++ b/tests/allure_pytest/acceptance/titlepath/titlepath_test.py @@ -2,7 +2,10 @@ from hamcrest import assert_that from tests.allure_pytest.pytest_runner import AllurePytestRunner +from allure_commons_test.label import has_package +from allure_commons_test.label import has_suite from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_full_name from allure_commons_test.result import has_title_path @@ -73,3 +76,27 @@ def test_nested_class_method_title_path(allure_pytest_runner: AllurePytestRunner has_title_path(*path_segments, "TestBar", "TestBaz"), ) ) + + +def test_file_path_with_double_colon( + allure_pytest_runner: AllurePytestRunner, +): + allure_pytest_runner.pytester.makepyfile(**{ + "test_module::part.py": """ + def test_fn(): + pass + """ + }) + + allure_results = allure_pytest_runner.run_pytest() + + assert_that( + allure_results, + has_test_case( + "test_fn", + has_full_name("test_module::part#test_fn"), + has_title_path("test_module::part.py"), + has_package("test_module::part"), + has_suite("test_module::part"), + ) + ) diff --git a/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py b/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py index 92945cba..7ebdb87a 100644 --- a/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py +++ b/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py @@ -3,7 +3,7 @@ import allure from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status +from allure_commons_test.result import has_full_name, with_status @allure.feature("Integration") @@ -61,3 +61,21 @@ def test_pytest_doctest_broken(allure_pytest_runner: AllurePytestRunner): "test_pytest_doctest_broken.some_func", with_status("broken") )) + + +@allure.feature("Integration") +def test_pytest_doctest_text_filename_preserves_brackets(allure_pytest_runner: AllurePytestRunner): + """ + >>> assert True + """ + + output = allure_pytest_runner.run_pytest( + ("spec[foo].txt", ">>> assert True\n"), + cli_args=("--doctest-glob=*.txt",), + ) + + assert_that(output, has_test_case( + "spec[foo]#spec[foo].txt", + has_full_name("spec[foo]#spec[foo].txt"), + with_status("passed"), + ))