Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't hardcode keywords #727

Merged
merged 5 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading