Skip to content

Commit

Permalink
Merge pull request #727 from jsa34/propogate-keywords-from-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
youtux authored Oct 20, 2024
2 parents 00916ff + 961f98a commit c92f8a6
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Unreleased
----------
- Dropped support for python 3.8. Supported python versions: 3.9, 3.10, 3.11, 3.12, 3.13.
- Text after the `#` character is no longer stripped from the Scenario and Feature name.
- 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).

8.0.0b2
----------
Expand Down
4 changes: 2 additions & 2 deletions src/pytest_bdd/cucumber_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def stepmap(step: dict[str, Any]) -> dict[str, Any]:

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

self.features[scenario["feature"]["filename"]]["elements"].append(
{
"keyword": "Scenario",
"keyword": scenario["keyword"],
"id": report.item["name"],
"name": scenario["name"],
"line": scenario["line_number"],
Expand Down
8 changes: 4 additions & 4 deletions src/pytest_bdd/gherkin_terminal_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,20 @@ def pytest_runtest_logreport(self, report: TestReport) -> Any:

if self.verbosity == 1:
self.ensure_newline()
self._tw.write("Feature: ", **feature_markup)
self._tw.write(f"{report.scenario['feature']['keyword']}: ", **feature_markup)
self._tw.write(report.scenario["feature"]["name"], **feature_markup)
self._tw.write("\n")
self._tw.write(" Scenario: ", **scenario_markup)
self._tw.write(f" {report.scenario['keyword']}: ", **scenario_markup)
self._tw.write(report.scenario["name"], **scenario_markup)
self._tw.write(" ")
self._tw.write(word, **word_markup)
self._tw.write("\n")
elif self.verbosity > 1:
self.ensure_newline()
self._tw.write("Feature: ", **feature_markup)
self._tw.write(f"{report.scenario['feature']['keyword']}: ", **feature_markup)
self._tw.write(report.scenario["feature"]["name"], **feature_markup)
self._tw.write("\n")
self._tw.write(" Scenario: ", **scenario_markup)
self._tw.write(f" {report.scenario['keyword']}: ", **scenario_markup)
self._tw.write(report.scenario["name"], **scenario_markup)
self._tw.write("\n")
for step in report.scenario["steps"]:
Expand Down
8 changes: 8 additions & 0 deletions src/pytest_bdd/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Feature:
scenarios: OrderedDict[str, ScenarioTemplate]
filename: str
rel_filename: str
keyword: str
name: str | None
tags: set[str]
background: Background | None
Expand Down Expand Up @@ -104,6 +105,7 @@ class ScenarioTemplate:
Attributes:
feature (Feature): The feature to which this scenario belongs.
keyword (str): The keyword used to define the scenario.
name (str): The name of the scenario.
line_number (int): The line number where the scenario starts in the file.
templated (bool): Whether the scenario is templated.
Expand All @@ -114,6 +116,7 @@ class ScenarioTemplate:
"""

feature: Feature
keyword: str
name: str
line_number: int
templated: bool
Expand Down Expand Up @@ -165,6 +168,7 @@ def render(self, context: Mapping[str, Any]) -> Scenario:
steps = background_steps + scenario_steps
return Scenario(
feature=self.feature,
keyword=self.keyword,
name=self.name,
line_number=self.line_number,
steps=steps,
Expand All @@ -179,6 +183,7 @@ class Scenario:
Attributes:
feature (Feature): The feature to which this scenario belongs.
keyword (str): The keyword used to define the scenario.
name (str): The name of the scenario.
line_number (int): The line number where the scenario starts in the file.
steps (List[Step]): The list of steps in the scenario.
Expand All @@ -187,6 +192,7 @@ class Scenario:
"""

feature: Feature
keyword: str
name: str
line_number: int
steps: list[Step]
Expand Down Expand Up @@ -386,6 +392,7 @@ def parse_scenario(self, scenario_data: GherkinScenario, feature: Feature) -> Sc
templated = bool(scenario_data.examples)
scenario = ScenarioTemplate(
feature=feature,
keyword=scenario_data.keyword,
name=scenario_data.name,
line_number=scenario_data.location.line,
templated=templated,
Expand Down Expand Up @@ -434,6 +441,7 @@ def parse(self) -> Feature:
feature_data: GherkinFeature = gherkin_doc.feature
feature = Feature(
scenarios=OrderedDict(),
keyword=feature_data.keyword,
filename=self.abs_filename,
rel_filename=self.rel_filename,
name=feature_data.name,
Expand Down
2 changes: 2 additions & 0 deletions src/pytest_bdd/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ def serialize(self) -> dict[str, Any]:

return {
"steps": [step_report.serialize() for step_report in self.step_reports],
"keyword": scenario.keyword,
"name": scenario.name,
"line_number": scenario.line_number,
"tags": sorted(scenario.tags),
"feature": {
"keyword": feature.keyword,
"name": feature.name,
"filename": feature.filename,
"rel_filename": feature.rel_filename,
Expand Down
6 changes: 3 additions & 3 deletions tests/feature/test_cucumber_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def test_passing_outline():
},
{
"description": "",
"keyword": "Scenario",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"steps": [
{
Expand All @@ -188,7 +188,7 @@ def test_passing_outline():
},
{
"description": "",
"keyword": "Scenario",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"steps": [
{
Expand All @@ -206,7 +206,7 @@ def test_passing_outline():
},
{
"description": "",
"keyword": "Scenario",
"keyword": "Scenario Outline",
"tags": [{"line": 14, "name": "scenario-outline-passing-tag"}],
"steps": [
{
Expand Down
63 changes: 62 additions & 1 deletion tests/feature/test_gherkin_terminal_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,69 @@ def test_scenario_2():

result = pytester.runpytest("--gherkin-terminal-reporter", "-vv")
result.assert_outcomes(passed=1, failed=0)
result.stdout.fnmatch_lines("*Scenario: Scenario example 2")
result.stdout.fnmatch_lines("*Scenario Outline: Scenario example 2")
result.stdout.fnmatch_lines("*Given there are {start} cucumbers".format(**example))
result.stdout.fnmatch_lines("*When I eat {eat} cucumbers".format(**example))
result.stdout.fnmatch_lines("*Then I should have {left} cucumbers".format(**example))
result.stdout.fnmatch_lines("*PASSED")


def test_scenario_alias_keywords_are_accepted(pytester):
"""
Test that aliases for various keywords are accepted and reported correctly.
see https://cucumber.io/docs/gherkin/reference/
"""
pytester.makefile(
".feature",
simple="""
Feature: Simple feature
Scenario: Simple scenario
Given I have a <tag>
Then pass
Example: Simple example
Given I have a <tag>
Then pass
Scenario Outline: Outlined scenario
Given I have a templated <foo>
Then pass
Examples:
| foo |
| bar |
Scenario Template: Templated scenario
Given I have a templated <foo>
Then pass
Scenarios:
| foo |
| bar |
""",
)
pytester.makepyfile(
"""
from pytest_bdd import scenarios, given, then, parsers
scenarios("simple.feature")
@given("I have a <tag>")
def _():
return "tag"
@given(parsers.parse("I have a templated {foo}"))
def _(foo):
return "foo"
@then("pass")
def _():
pass
"""
)
result = pytester.runpytest("--gherkin-terminal-reporter", "-vv")
result.assert_outcomes(passed=4, failed=0)
result.stdout.fnmatch_lines("*Feature: Simple feature*")
result.stdout.fnmatch_lines("*Example: Simple example*")
result.stdout.fnmatch_lines("*Scenario: Simple scenario*")
result.stdout.fnmatch_lines("*Scenario Outline: Outlined scenario*")
8 changes: 8 additions & 0 deletions tests/feature/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,14 @@ def _(cucumbers, left):
expected = {
"feature": {
"description": "",
"keyword": "Feature",
"filename": str(feature),
"line_number": 2,
"name": "One passing scenario, one failing scenario",
"rel_filename": str(relpath),
"tags": ["feature-tag"],
},
"keyword": "Scenario",
"line_number": 5,
"name": "Passing",
"steps": [
Expand Down Expand Up @@ -141,12 +143,14 @@ def _(cucumbers, left):
expected = {
"feature": {
"description": "",
"keyword": "Feature",
"filename": str(feature),
"line_number": 2,
"name": "One passing scenario, one failing scenario",
"rel_filename": str(relpath),
"tags": ["feature-tag"],
},
"keyword": "Scenario",
"line_number": 10,
"name": "Failing",
"steps": [
Expand Down Expand Up @@ -175,12 +179,14 @@ def _(cucumbers, left):
expected = {
"feature": {
"description": "",
"keyword": "Feature",
"filename": str(feature),
"line_number": 2,
"name": "One passing scenario, one failing scenario",
"rel_filename": str(relpath),
"tags": ["feature-tag"],
},
"keyword": "Scenario Outline",
"line_number": 14,
"name": "Outlined",
"steps": [
Expand Down Expand Up @@ -217,12 +223,14 @@ def _(cucumbers, left):
expected = {
"feature": {
"description": "",
"keyword": "Feature",
"filename": str(feature),
"line_number": 2,
"name": "One passing scenario, one failing scenario",
"rel_filename": str(relpath),
"tags": ["feature-tag"],
},
"keyword": "Scenario Outline",
"line_number": 14,
"name": "Outlined",
"steps": [
Expand Down

0 comments on commit c92f8a6

Please sign in to comment.