Skip to content

Commit 37388cc

Browse files
committed
Refactor gherkin terminal reporter
1 parent 9ff6980 commit 37388cc

File tree

2 files changed

+82
-62
lines changed

2 files changed

+82
-62
lines changed

src/pytest_bdd/gherkin_terminal_reporter.py

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@ def configure(config: Config) -> None:
3131
raise Exception(
3232
"gherkin-terminal-reporter is not compatible with any other terminal reporter."
3333
"You can use only one terminal reporter."
34-
"Currently '{0}' is used."
35-
"Please decide to use one by deactivating {0} or gherkin-terminal-reporter.".format(
36-
current_reporter.__class__
37-
)
34+
f" Currently '{current_reporter.__class__}' is used."
35+
f" Please decide to use one by deactivating {current_reporter.__class__} or gherkin-terminal-reporter."
3836
)
3937
gherkin_reporter = GherkinTerminalReporter(config)
4038
config.pluginmanager.unregister(current_reporter)
@@ -49,72 +47,96 @@ def __init__(self, config: Config) -> None:
4947
self.current_rule: str | None = None
5048

5149
def pytest_runtest_logreport(self, report: TestReport) -> None:
52-
rep = report
53-
res = self.config.hook.pytest_report_teststatus(report=rep, config=self.config)
54-
cat, letter, word = res
50+
result = self.config.hook.pytest_report_teststatus(report=report, config=self.config)
51+
result_category, result_letter, result_word = result
5552

56-
if not letter and not word:
57-
# probably passed setup/teardown
53+
if not result_letter and not result_word:
5854
return None
5955

60-
if isinstance(word, tuple):
61-
word, word_markup = word
62-
elif rep.passed:
63-
word_markup = {"green": True}
64-
elif rep.failed:
65-
word_markup = {"red": True}
66-
elif rep.skipped:
67-
word_markup = {"yellow": True}
68-
feature_markup = {"blue": True}
69-
scenario_markup = word_markup
70-
rule_markup = {"purple": True}
71-
7256
try:
7357
scenario = test_report_context_registry[report].scenario
7458
except KeyError:
7559
scenario = None
7660

7761
if self.verbosity <= 0 or scenario is None:
78-
return super().pytest_runtest_logreport(rep)
62+
return super().pytest_runtest_logreport(report)
7963

80-
rule = scenario.get("rule")
81-
indent = " " if rule else ""
64+
report_renderer = ReportRenderer(self, report, scenario, result_word)
8265

8366
if self.verbosity == 1:
84-
self.ensure_newline()
85-
self._tw.write(f"{scenario['feature']['keyword']}: ", **feature_markup)
86-
self._tw.write(scenario["feature"]["name"], **feature_markup)
87-
self._tw.write("\n")
88-
89-
if rule and rule["name"] != self.current_rule:
90-
self._tw.write(f" {rule['keyword']}: ", **rule_markup)
91-
self._tw.write(rule["name"], **rule_markup)
92-
self._tw.write("\n")
93-
self.current_rule = rule["name"]
94-
95-
self._tw.write(f"{indent} {scenario['keyword']}: ", **scenario_markup)
96-
self._tw.write(scenario["name"], **scenario_markup)
97-
self._tw.write(" ")
98-
self._tw.write(word, **word_markup)
99-
self._tw.write("\n")
67+
report_renderer.write_scenario_summary()
10068
elif self.verbosity > 1:
101-
self.ensure_newline()
102-
self._tw.write(f"{scenario['feature']['keyword']}: ", **feature_markup)
103-
self._tw.write(scenario["feature"]["name"], **feature_markup)
104-
self._tw.write("\n")
105-
106-
if rule and rule["name"] != self.current_rule:
107-
self._tw.write(f" {rule['keyword']}: ", **rule_markup)
108-
self._tw.write(rule["name"], **rule_markup)
109-
self._tw.write("\n")
110-
self.current_rule = rule["name"]
111-
112-
self._tw.write(f"{indent} {scenario['keyword']}: ", **scenario_markup)
113-
self._tw.write(scenario["name"], **scenario_markup)
114-
self._tw.write("\n")
115-
for step in scenario["steps"]:
116-
self._tw.write(f"{indent} {step['keyword']} {step['name']}\n", **scenario_markup)
117-
self._tw.write(f"{indent} {word}", **word_markup)
118-
self._tw.write("\n\n")
119-
120-
self.stats.setdefault(cat, []).append(rep)
69+
report_renderer.write_detailed_scenario_with_steps()
70+
71+
self.stats.setdefault(result_category, []).append(report)
72+
73+
74+
class ReportRenderer:
75+
def __init__(
76+
self, reporter: GherkinTerminalReporter, report: TestReport, scenario: dict, result_outcome: str
77+
) -> None:
78+
self.reporter = reporter
79+
self.report = report
80+
self.scenario = scenario
81+
self.rule = scenario.get("rule")
82+
if isinstance(result_outcome, tuple):
83+
self.result_outcome, self.feature_markup = result_outcome
84+
else:
85+
self.result_outcome = result_outcome
86+
self.feature_markup = {"blue": True}
87+
self.rule_markup = {"purple": True}
88+
self.tw = self.reporter._tw
89+
self.current_indentation_index = 0
90+
91+
def get_outcome_markup(self) -> dict:
92+
if self.report.passed:
93+
return {"green": True}
94+
elif self.report.failed:
95+
return {"red": True}
96+
elif self.report.skipped:
97+
return {"yellow": True}
98+
99+
def write_scenario_summary(self, has_steps: bool = False) -> None:
100+
"""Write the feature and scenario header to the terminal."""
101+
self.tw.write("\n")
102+
self.tw.write(f"{self.scenario['feature']['keyword']}: ", **self.feature_markup)
103+
self.tw.write(self.scenario["feature"]["name"], **self.feature_markup)
104+
self.tw.write("\n")
105+
106+
if self.rule and self.rule["name"] != self.reporter.current_rule:
107+
self.current_indentation_index = 1
108+
self.tw.write(
109+
f"{self._get_indent(self.current_indentation_index)}{self.rule['keyword']}: ", **self.rule_markup
110+
)
111+
self.tw.write(self.rule["name"], **self.rule_markup)
112+
self.tw.write("\n")
113+
self.reporter.current_rule = self.rule["name"]
114+
115+
self.current_indentation_index += 1
116+
self.tw.write(
117+
f"{self._get_indent(self.current_indentation_index)}{self.scenario['keyword']}: ",
118+
**self.get_outcome_markup(),
119+
)
120+
self.tw.write(self.scenario["name"], **self.get_outcome_markup())
121+
if not has_steps:
122+
self.tw.write(" ", **self.get_outcome_markup())
123+
self.tw.write(self.result_outcome, **self.get_outcome_markup())
124+
self.tw.write("\n")
125+
126+
def write_detailed_scenario_with_steps(self) -> None:
127+
"""Write the full details of the scenario including the steps."""
128+
self.write_scenario_summary(has_steps=True)
129+
130+
for step in self.scenario["steps"]:
131+
self.current_indentation_index += 1
132+
self.tw.write(
133+
f"{self._get_indent(self.current_indentation_index)}{step['keyword']} {step['name']}\n",
134+
**self.get_outcome_markup(),
135+
)
136+
137+
self.tw.write(f"{self._get_indent(2)}{self.result_outcome}", **self.get_outcome_markup())
138+
self.tw.write("\n\n")
139+
140+
@staticmethod
141+
def _get_indent(level: int) -> str:
142+
return " " * level

tests/feature/test_gherkin_terminal_reporter.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from __future__ import annotations
2-
31
import textwrap
42

53
import pytest

0 commit comments

Comments
 (0)