From e3e0cff4925f353e3e7461425375d20fffb139e1 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sat, 12 Mar 2022 12:41:16 +0100 Subject: [PATCH] Require "Scenario Outline" keyword for outlined scenario. This should fix https://github.com/pytest-dev/pytest-bdd/issues/447 --- pytest_bdd/new_parser.py | 16 +++++- pytest_bdd/parser_data/gherkin.grammar.lark | 9 ++-- tests/feature/test_cucumber_json.py | 2 +- tests/test_new_parser.py | 57 +++++++++++++++++++-- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/pytest_bdd/new_parser.py b/pytest_bdd/new_parser.py index 5c0dc22d..3bebb777 100644 --- a/pytest_bdd/new_parser.py +++ b/pytest_bdd/new_parser.py @@ -115,13 +115,25 @@ def tag_lines(self, value: list[Tree]) -> set[str]: return tags @v_args(inline=True) - def scenario( + def scenario(self, tag_lines: set[str] | None, scenario_line: Token, steps: list[Step] | None): + # TODO: Try to remove duplicated code with "scenario_outline" + scenario = ScenarioTemplate( + name=scenario_line.strip(), + line_number=scenario_line.line, + tags=tag_lines or set(), + feature=None, # added later + ) + for step in steps or []: + scenario.add_step(step) + return scenario + + @v_args(inline=True) + def scenario_outline( self, tag_lines: set[str] | None, scenario_line: Token, steps: list[Step] | None, examples: Examples | None ): scenario = ScenarioTemplate( name=scenario_line.strip(), line_number=scenario_line.line, - # example_converters=None, tags=tag_lines or set(), feature=None, # added later examples=examples, diff --git a/pytest_bdd/parser_data/gherkin.grammar.lark b/pytest_bdd/parser_data/gherkin.grammar.lark index e3b49839..54c8e20f 100644 --- a/pytest_bdd/parser_data/gherkin.grammar.lark +++ b/pytest_bdd/parser_data/gherkin.grammar.lark @@ -17,8 +17,9 @@ scenarios: scenario* feature_header: [tag_lines] feature_line: FEATURE STRING _NL? -scenario: [tag_lines] scenario_line [_INDENT [steps] [examples] _DEDENT] -scenario_line: (SCENARIO | SCENARIO_OUTLINE) STRING _NL? +scenario: [tag_lines] scenario_line{SCENARIO} [_INDENT [steps] _DEDENT] -> scenario + | [tag_lines] scenario_line{SCENARIO_OUTLINE} [_INDENT [steps] examples _DEDENT] -> scenario_outline +scenario_line{type}: type STRING _NL? examples: example_line example_table example_line: EXAMPLES [STRING] _NL? @@ -60,10 +61,10 @@ AND: "And " BUT: "But " SCENARIO: "Scenario:" -SCENARIO_OUTLINE: "Scenario Outline:" +SCENARIO_OUTLINE: "Scenario Outline:" | "Scenario Template:" BACKGROUND: "Background:" FEATURE: "Feature:" -EXAMPLES: "Examples:" +EXAMPLES: "Examples:" | "Scenarios:" // TODO: Probably the following does not need the negative lookbehind part //step_docstring: /"""\n.*?(? and value Examples: example1 diff --git a/tests/test_new_parser.py b/tests/test_new_parser.py index 00eb93a9..8aec5374 100644 --- a/tests/test_new_parser.py +++ b/tests/test_new_parser.py @@ -11,6 +11,7 @@ # - "Feature:" line is always required. # - Other changes. Check the modifications to the tests. # - Tags can only be "valid" identifiers, e.g. no spaces. +# - Must use "Scenario Outline" (or "Scenario Template") for outlined scenarios. "Scenario" will not work anymore def test_feature(): @@ -567,20 +568,43 @@ def test_empty_lines(src): @pytest.mark.parametrize( ["src", "expected"], [ - ( + pytest.param( """\ Feature: A feature - Scenario: A scenario + Scenario Outline: A scenario Examples: | foo | | bar | """, [{"foo": "bar"}], + id="basic", + ), + pytest.param( + """\ +Feature: A feature + Scenario Template: A scenario + Examples: + | foo | + | bar | +""", + [{"foo": "bar"}], + id="scenario_template_instead_of_scenario_outline_keyword", + ), + pytest.param( + """\ +Feature: A feature + Scenario Outline: A scenario + Scenarios: + | foo | + | bar | +""", + [{"foo": "bar"}], + id="scenarios_instead_of_examples_keyword", ), ( """\ Feature: A feature - Scenario: A scenario + Scenario Outline: A scenario Examples: | first name | last name | | Alessio | Bogon | @@ -594,14 +618,14 @@ def test_empty_lines(src): ( """\ Feature: A feature - Scenario: A scenario + Scenario Outline: A scenario """, [], ), ( """\ Feature: A feature - Scenario: A scenario + Scenario Outline: A scenario Examples: | pipe in the \\| middle | | foo | @@ -618,6 +642,29 @@ def test_scenario_examples(src, expected): assert list(scenario.examples.as_contexts()) == expected +@pytest.mark.parametrize( + ["src", "expected"], + [ + ( + """\ +Feature: A feature + Scenario: A scenario + Given foo is + Examples: + | foo | + | bar | +""", + [{"foo": "bar"}], + ), + ], +) +def test_examples_not_allowed_in_scenario(src, expected): + """Test that "Examples:" are not allowed in scenarios (only in scenario outlines)""" + with pytest.raises(Exception): + feature = parse(src) + # TODO: Test exception + + def test_comment(): src = """\ # feature comment