Skip to content

Commit c92f8a6

Browse files
authored
Merge pull request #727 from jsa34/propogate-keywords-from-parser
2 parents 00916ff + 961f98a commit c92f8a6

File tree

8 files changed

+90
-10
lines changed

8 files changed

+90
-10
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Unreleased
55
----------
66
- Dropped support for python 3.8. Supported python versions: 3.9, 3.10, 3.11, 3.12, 3.13.
77
- Text after the `#` character is no longer stripped from the Scenario and Feature name.
8+
- Gherkin keyword aliases can now be used and correctly reported in json and terminal output (see `Keywords <https://cucumber.io/docs/gherkin/reference/#keywords>` for permitted list).
89

910
8.0.0b2
1011
----------

src/pytest_bdd/cucumber_json.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def stepmap(step: dict[str, Any]) -> dict[str, Any]:
114114

115115
if scenario["feature"]["filename"] not in self.features:
116116
self.features[scenario["feature"]["filename"]] = {
117-
"keyword": "Feature",
117+
"keyword": scenario["feature"]["keyword"],
118118
"uri": scenario["feature"]["rel_filename"],
119119
"name": scenario["feature"]["name"] or scenario["feature"]["rel_filename"],
120120
"id": scenario["feature"]["rel_filename"].lower().replace(" ", "-"),
@@ -126,7 +126,7 @@ def stepmap(step: dict[str, Any]) -> dict[str, Any]:
126126

127127
self.features[scenario["feature"]["filename"]]["elements"].append(
128128
{
129-
"keyword": "Scenario",
129+
"keyword": scenario["keyword"],
130130
"id": report.item["name"],
131131
"name": scenario["name"],
132132
"line": scenario["line_number"],

src/pytest_bdd/gherkin_terminal_reporter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,20 @@ def pytest_runtest_logreport(self, report: TestReport) -> Any:
7272

7373
if self.verbosity == 1:
7474
self.ensure_newline()
75-
self._tw.write("Feature: ", **feature_markup)
75+
self._tw.write(f"{report.scenario['feature']['keyword']}: ", **feature_markup)
7676
self._tw.write(report.scenario["feature"]["name"], **feature_markup)
7777
self._tw.write("\n")
78-
self._tw.write(" Scenario: ", **scenario_markup)
78+
self._tw.write(f" {report.scenario['keyword']}: ", **scenario_markup)
7979
self._tw.write(report.scenario["name"], **scenario_markup)
8080
self._tw.write(" ")
8181
self._tw.write(word, **word_markup)
8282
self._tw.write("\n")
8383
elif self.verbosity > 1:
8484
self.ensure_newline()
85-
self._tw.write("Feature: ", **feature_markup)
85+
self._tw.write(f"{report.scenario['feature']['keyword']}: ", **feature_markup)
8686
self._tw.write(report.scenario["feature"]["name"], **feature_markup)
8787
self._tw.write("\n")
88-
self._tw.write(" Scenario: ", **scenario_markup)
88+
self._tw.write(f" {report.scenario['keyword']}: ", **scenario_markup)
8989
self._tw.write(report.scenario["name"], **scenario_markup)
9090
self._tw.write("\n")
9191
for step in report.scenario["steps"]:

src/pytest_bdd/parser.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Feature:
4040
scenarios: OrderedDict[str, ScenarioTemplate]
4141
filename: str
4242
rel_filename: str
43+
keyword: str
4344
name: str | None
4445
tags: set[str]
4546
background: Background | None
@@ -104,6 +105,7 @@ class ScenarioTemplate:
104105
105106
Attributes:
106107
feature (Feature): The feature to which this scenario belongs.
108+
keyword (str): The keyword used to define the scenario.
107109
name (str): The name of the scenario.
108110
line_number (int): The line number where the scenario starts in the file.
109111
templated (bool): Whether the scenario is templated.
@@ -114,6 +116,7 @@ class ScenarioTemplate:
114116
"""
115117

116118
feature: Feature
119+
keyword: str
117120
name: str
118121
line_number: int
119122
templated: bool
@@ -165,6 +168,7 @@ def render(self, context: Mapping[str, Any]) -> Scenario:
165168
steps = background_steps + scenario_steps
166169
return Scenario(
167170
feature=self.feature,
171+
keyword=self.keyword,
168172
name=self.name,
169173
line_number=self.line_number,
170174
steps=steps,
@@ -179,6 +183,7 @@ class Scenario:
179183
180184
Attributes:
181185
feature (Feature): The feature to which this scenario belongs.
186+
keyword (str): The keyword used to define the scenario.
182187
name (str): The name of the scenario.
183188
line_number (int): The line number where the scenario starts in the file.
184189
steps (List[Step]): The list of steps in the scenario.
@@ -187,6 +192,7 @@ class Scenario:
187192
"""
188193

189194
feature: Feature
195+
keyword: str
190196
name: str
191197
line_number: int
192198
steps: list[Step]
@@ -386,6 +392,7 @@ def parse_scenario(self, scenario_data: GherkinScenario, feature: Feature) -> Sc
386392
templated = bool(scenario_data.examples)
387393
scenario = ScenarioTemplate(
388394
feature=feature,
395+
keyword=scenario_data.keyword,
389396
name=scenario_data.name,
390397
line_number=scenario_data.location.line,
391398
templated=templated,
@@ -434,6 +441,7 @@ def parse(self) -> Feature:
434441
feature_data: GherkinFeature = gherkin_doc.feature
435442
feature = Feature(
436443
scenarios=OrderedDict(),
444+
keyword=feature_data.keyword,
437445
filename=self.abs_filename,
438446
rel_filename=self.rel_filename,
439447
name=feature_data.name,

src/pytest_bdd/reporting.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,12 @@ def serialize(self) -> dict[str, Any]:
110110

111111
return {
112112
"steps": [step_report.serialize() for step_report in self.step_reports],
113+
"keyword": scenario.keyword,
113114
"name": scenario.name,
114115
"line_number": scenario.line_number,
115116
"tags": sorted(scenario.tags),
116117
"feature": {
118+
"keyword": feature.keyword,
117119
"name": feature.name,
118120
"filename": feature.filename,
119121
"rel_filename": feature.rel_filename,

tests/feature/test_cucumber_json.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def test_passing_outline():
170170
},
171171
{
172172
"description": "",
173-
"keyword": "Scenario",
173+
"keyword": "Scenario Outline",
174174
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
175175
"steps": [
176176
{
@@ -188,7 +188,7 @@ def test_passing_outline():
188188
},
189189
{
190190
"description": "",
191-
"keyword": "Scenario",
191+
"keyword": "Scenario Outline",
192192
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
193193
"steps": [
194194
{
@@ -206,7 +206,7 @@ def test_passing_outline():
206206
},
207207
{
208208
"description": "",
209-
"keyword": "Scenario",
209+
"keyword": "Scenario Outline",
210210
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
211211
"steps": [
212212
{

tests/feature/test_gherkin_terminal_reporter.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,69 @@ def test_scenario_2():
231231

232232
result = pytester.runpytest("--gherkin-terminal-reporter", "-vv")
233233
result.assert_outcomes(passed=1, failed=0)
234-
result.stdout.fnmatch_lines("*Scenario: Scenario example 2")
234+
result.stdout.fnmatch_lines("*Scenario Outline: Scenario example 2")
235235
result.stdout.fnmatch_lines("*Given there are {start} cucumbers".format(**example))
236236
result.stdout.fnmatch_lines("*When I eat {eat} cucumbers".format(**example))
237237
result.stdout.fnmatch_lines("*Then I should have {left} cucumbers".format(**example))
238238
result.stdout.fnmatch_lines("*PASSED")
239+
240+
241+
def test_scenario_alias_keywords_are_accepted(pytester):
242+
"""
243+
Test that aliases for various keywords are accepted and reported correctly.
244+
see https://cucumber.io/docs/gherkin/reference/
245+
"""
246+
pytester.makefile(
247+
".feature",
248+
simple="""
249+
Feature: Simple feature
250+
Scenario: Simple scenario
251+
Given I have a <tag>
252+
Then pass
253+
254+
Example: Simple example
255+
Given I have a <tag>
256+
Then pass
257+
258+
Scenario Outline: Outlined scenario
259+
Given I have a templated <foo>
260+
Then pass
261+
262+
Examples:
263+
| foo |
264+
| bar |
265+
266+
Scenario Template: Templated scenario
267+
Given I have a templated <foo>
268+
Then pass
269+
270+
Scenarios:
271+
| foo |
272+
| bar |
273+
""",
274+
)
275+
pytester.makepyfile(
276+
"""
277+
from pytest_bdd import scenarios, given, then, parsers
278+
279+
scenarios("simple.feature")
280+
281+
@given("I have a <tag>")
282+
def _():
283+
return "tag"
284+
285+
@given(parsers.parse("I have a templated {foo}"))
286+
def _(foo):
287+
return "foo"
288+
289+
@then("pass")
290+
def _():
291+
pass
292+
"""
293+
)
294+
result = pytester.runpytest("--gherkin-terminal-reporter", "-vv")
295+
result.assert_outcomes(passed=4, failed=0)
296+
result.stdout.fnmatch_lines("*Feature: Simple feature*")
297+
result.stdout.fnmatch_lines("*Example: Simple example*")
298+
result.stdout.fnmatch_lines("*Scenario: Simple scenario*")
299+
result.stdout.fnmatch_lines("*Scenario Outline: Outlined scenario*")

tests/feature/test_report.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,14 @@ def _(cucumbers, left):
106106
expected = {
107107
"feature": {
108108
"description": "",
109+
"keyword": "Feature",
109110
"filename": str(feature),
110111
"line_number": 2,
111112
"name": "One passing scenario, one failing scenario",
112113
"rel_filename": str(relpath),
113114
"tags": ["feature-tag"],
114115
},
116+
"keyword": "Scenario",
115117
"line_number": 5,
116118
"name": "Passing",
117119
"steps": [
@@ -141,12 +143,14 @@ def _(cucumbers, left):
141143
expected = {
142144
"feature": {
143145
"description": "",
146+
"keyword": "Feature",
144147
"filename": str(feature),
145148
"line_number": 2,
146149
"name": "One passing scenario, one failing scenario",
147150
"rel_filename": str(relpath),
148151
"tags": ["feature-tag"],
149152
},
153+
"keyword": "Scenario",
150154
"line_number": 10,
151155
"name": "Failing",
152156
"steps": [
@@ -175,12 +179,14 @@ def _(cucumbers, left):
175179
expected = {
176180
"feature": {
177181
"description": "",
182+
"keyword": "Feature",
178183
"filename": str(feature),
179184
"line_number": 2,
180185
"name": "One passing scenario, one failing scenario",
181186
"rel_filename": str(relpath),
182187
"tags": ["feature-tag"],
183188
},
189+
"keyword": "Scenario Outline",
184190
"line_number": 14,
185191
"name": "Outlined",
186192
"steps": [
@@ -217,12 +223,14 @@ def _(cucumbers, left):
217223
expected = {
218224
"feature": {
219225
"description": "",
226+
"keyword": "Feature",
220227
"filename": str(feature),
221228
"line_number": 2,
222229
"name": "One passing scenario, one failing scenario",
223230
"rel_filename": str(relpath),
224231
"tags": ["feature-tag"],
225232
},
233+
"keyword": "Scenario Outline",
226234
"line_number": 14,
227235
"name": "Outlined",
228236
"steps": [

0 commit comments

Comments
 (0)