Skip to content

Commit e3e0cff

Browse files
committed
Require "Scenario Outline" keyword for outlined scenario.
This should fix #447
1 parent a10e93a commit e3e0cff

File tree

4 files changed

+72
-12
lines changed

4 files changed

+72
-12
lines changed

Diff for: pytest_bdd/new_parser.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,25 @@ def tag_lines(self, value: list[Tree]) -> set[str]:
115115
return tags
116116

117117
@v_args(inline=True)
118-
def scenario(
118+
def scenario(self, tag_lines: set[str] | None, scenario_line: Token, steps: list[Step] | None):
119+
# TODO: Try to remove duplicated code with "scenario_outline"
120+
scenario = ScenarioTemplate(
121+
name=scenario_line.strip(),
122+
line_number=scenario_line.line,
123+
tags=tag_lines or set(),
124+
feature=None, # added later
125+
)
126+
for step in steps or []:
127+
scenario.add_step(step)
128+
return scenario
129+
130+
@v_args(inline=True)
131+
def scenario_outline(
119132
self, tag_lines: set[str] | None, scenario_line: Token, steps: list[Step] | None, examples: Examples | None
120133
):
121134
scenario = ScenarioTemplate(
122135
name=scenario_line.strip(),
123136
line_number=scenario_line.line,
124-
# example_converters=None,
125137
tags=tag_lines or set(),
126138
feature=None, # added later
127139
examples=examples,

Diff for: pytest_bdd/parser_data/gherkin.grammar.lark

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ scenarios: scenario*
1717
feature_header: [tag_lines]
1818
feature_line: FEATURE STRING _NL?
1919

20-
scenario: [tag_lines] scenario_line [_INDENT [steps] [examples] _DEDENT]
21-
scenario_line: (SCENARIO | SCENARIO_OUTLINE) STRING _NL?
20+
scenario: [tag_lines] scenario_line{SCENARIO} [_INDENT [steps] _DEDENT] -> scenario
21+
| [tag_lines] scenario_line{SCENARIO_OUTLINE} [_INDENT [steps] examples _DEDENT] -> scenario_outline
22+
scenario_line{type}: type STRING _NL?
2223

2324
examples: example_line example_table
2425
example_line: EXAMPLES [STRING] _NL?
@@ -60,10 +61,10 @@ AND: "And "
6061
BUT: "But "
6162

6263
SCENARIO: "Scenario:"
63-
SCENARIO_OUTLINE: "Scenario Outline:"
64+
SCENARIO_OUTLINE: "Scenario Outline:" | "Scenario Template:"
6465
BACKGROUND: "Background:"
6566
FEATURE: "Feature:"
66-
EXAMPLES: "Examples:"
67+
EXAMPLES: "Examples:" | "Scenarios:"
6768

6869
// TODO: Probably the following does not need the negative lookbehind part
6970
//step_docstring: /"""\n.*?(?<!\\)(\\\\)*?\n\s*"""/is

Diff for: tests/feature/test_cucumber_json.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_step_trace(testdir):
6262
And a failing step
6363
6464
@scenario-outline-passing-tag
65-
Scenario: Passing outline
65+
Scenario Outline: Passing outline
6666
Given type <type> and value <value>
6767
6868
Examples: example1

Diff for: tests/test_new_parser.py

+52-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# - "Feature:" line is always required.
1212
# - Other changes. Check the modifications to the tests.
1313
# - Tags can only be "valid" identifiers, e.g. no spaces.
14+
# - Must use "Scenario Outline" (or "Scenario Template") for outlined scenarios. "Scenario" will not work anymore
1415

1516

1617
def test_feature():
@@ -567,20 +568,43 @@ def test_empty_lines(src):
567568
@pytest.mark.parametrize(
568569
["src", "expected"],
569570
[
570-
(
571+
pytest.param(
571572
"""\
572573
Feature: A feature
573-
Scenario: A scenario
574+
Scenario Outline: A scenario
574575
Examples:
575576
| foo |
576577
| bar |
577578
""",
578579
[{"foo": "bar"}],
580+
id="basic",
581+
),
582+
pytest.param(
583+
"""\
584+
Feature: A feature
585+
Scenario Template: A scenario
586+
Examples:
587+
| foo |
588+
| bar |
589+
""",
590+
[{"foo": "bar"}],
591+
id="scenario_template_instead_of_scenario_outline_keyword",
592+
),
593+
pytest.param(
594+
"""\
595+
Feature: A feature
596+
Scenario Outline: A scenario
597+
Scenarios:
598+
| foo |
599+
| bar |
600+
""",
601+
[{"foo": "bar"}],
602+
id="scenarios_instead_of_examples_keyword",
579603
),
580604
(
581605
"""\
582606
Feature: A feature
583-
Scenario: A scenario
607+
Scenario Outline: A scenario
584608
Examples:
585609
| first name | last name |
586610
| Alessio | Bogon |
@@ -594,14 +618,14 @@ def test_empty_lines(src):
594618
(
595619
"""\
596620
Feature: A feature
597-
Scenario: A scenario
621+
Scenario Outline: A scenario
598622
""",
599623
[],
600624
),
601625
(
602626
"""\
603627
Feature: A feature
604-
Scenario: A scenario
628+
Scenario Outline: A scenario
605629
Examples:
606630
| pipe in the \\| middle |
607631
| foo |
@@ -618,6 +642,29 @@ def test_scenario_examples(src, expected):
618642
assert list(scenario.examples.as_contexts()) == expected
619643

620644

645+
@pytest.mark.parametrize(
646+
["src", "expected"],
647+
[
648+
(
649+
"""\
650+
Feature: A feature
651+
Scenario: A scenario
652+
Given foo is <foo>
653+
Examples:
654+
| foo |
655+
| bar |
656+
""",
657+
[{"foo": "bar"}],
658+
),
659+
],
660+
)
661+
def test_examples_not_allowed_in_scenario(src, expected):
662+
"""Test that "Examples:" are not allowed in scenarios (only in scenario outlines)"""
663+
with pytest.raises(Exception):
664+
feature = parse(src)
665+
# TODO: Test exception
666+
667+
621668
def test_comment():
622669
src = """\
623670
# feature comment

0 commit comments

Comments
 (0)