diff --git a/pytest_bdd/cucumber_json.py b/pytest_bdd/cucumber_json.py index fb09782..97c0116 100644 --- a/pytest_bdd/cucumber_json.py +++ b/pytest_bdd/cucumber_json.py @@ -67,7 +67,7 @@ class LogBDDCucumberJSON(object): def stepmap(step): return { - "keyword": step['type'].capitalize(), + "keyword": step['keyword'], "name": step['name'], "line": step['line_number'], "match": { @@ -80,7 +80,7 @@ class LogBDDCucumberJSON(object): self.features[scenario['feature']['filename']] = { "keyword": "Feature", "uri": scenario['feature']['rel_filename'], - "name": scenario['feature']['name'], + "name": scenario['feature']['name'] or scenario['feature']['rel_filename'], "id": scenario['feature']['rel_filename'].lower().replace(' ', '-'), "line": scenario['feature']['line_number'], "description": scenario['feature']['description'], diff --git a/pytest_bdd/feature.py b/pytest_bdd/feature.py index a01e91d..2ced1a2 100644 --- a/pytest_bdd/feature.py +++ b/pytest_bdd/feature.py @@ -96,18 +96,17 @@ def strip_comments(line): return line.strip() -def remove_prefix(line): - """Remove the step prefix (Scenario, Given, When, Then or And). +def parse_line(line): + """Parse step line to get the step prefix (Scenario, Given, When, Then or And) and the actual step name. :param line: Line of the Feature file. - :return: Line without the prefix. - + :return: `tuple` in form ('', ''). """ for prefix, _ in STEP_PREFIXES: if line.startswith(prefix): - return line[len(prefix):].strip() - return line + return prefix.strip(), line[len(prefix):].strip() + return '', line def _open_file(filename, encoding): @@ -187,7 +186,7 @@ class Feature(object): if mode == types.FEATURE: if prev_mode != types.FEATURE: - self.name = remove_prefix(clean_line) + _, self.name = parse_line(clean_line) self.line_number = line_number else: description.append(clean_line) @@ -195,7 +194,7 @@ class Feature(object): prev_mode = mode # Remove Feature, Given, When, Then, And - clean_line = remove_prefix(clean_line) + keyword, clean_line = parse_line(clean_line) if mode in [types.SCENARIO, types.SCENARIO_OUTLINE]: self.scenarios[clean_line] = scenario = Scenario(self, clean_line, line_number) elif mode == types.EXAMPLES: @@ -212,7 +211,8 @@ class Feature(object): scenario.add_example_row(clean_line[0], clean_line[1:]) elif mode and mode != types.FEATURE: step = scenario.add_step( - step_name=clean_line, step_type=mode, indent=line_indent, line_number=line_number) + step_name=clean_line, step_type=mode, indent=line_indent, line_number=line_number, + keyword=keyword) self.description = u'\n'.join(description) @@ -253,17 +253,20 @@ class Scenario(object): self.line_number = line_number self.example_converters = example_converters - def add_step(self, step_name, step_type, indent, line_number): + def add_step(self, step_name, step_type, indent, line_number, keyword): """Add step to the scenario. :param step_name: Step name. :param step_type: Step type. - + :param indent: `int` step text indent + :param line_number: `int` line number + :param keyword: `str` step keyword """ params = get_step_params(step_name) self.params.update(params) step = Step( - name=step_name, type=step_type, params=params, scenario=self, indent=indent, line_number=line_number) + name=step_name, type=step_type, params=params, scenario=self, indent=indent, line_number=line_number, + keyword=keyword) self.steps.append(step) return step @@ -340,8 +343,9 @@ class Step(object): """Step.""" - def __init__(self, name, type, params, scenario, indent, line_number): + def __init__(self, name, type, params, scenario, indent, line_number, keyword): self.name = name + self.keyword = keyword self.lines = [] self.indent = indent self.type = type diff --git a/pytest_bdd/plugin.py b/pytest_bdd/plugin.py index a9a38dd..04d689c 100644 --- a/pytest_bdd/plugin.py +++ b/pytest_bdd/plugin.py @@ -28,8 +28,9 @@ def pytest_runtest_makereport(item, call, __multicall__): else: rep.scenario = { 'steps': [{ - 'name': step._name, + 'name': step.name, 'type': step.type, + 'keyword': step.keyword, 'line_number': step.line_number } for step in scenario.steps], 'name': scenario.name, diff --git a/pytest_bdd/steps.py b/pytest_bdd/steps.py index a31c7c8..076b8c2 100644 --- a/pytest_bdd/steps.py +++ b/pytest_bdd/steps.py @@ -39,13 +39,14 @@ import sys # pragma: no cover import pytest # pragma: no cover -from pytest_bdd.feature import remove_prefix # pragma: no cover +from pytest_bdd.feature import parse_line # pragma: no cover from pytest_bdd.types import GIVEN, WHEN, THEN # pragma: no cover PY3 = sys.version_info[0] >= 3 # pragma: no cover class StepError(Exception): # pragma: no cover + """Step declaration error.""" RE_TYPE = type(re.compile('')) # pragma: no cover @@ -63,7 +64,6 @@ def given(name, fixture=None, converters=None): :note: Can't be used as a decorator when the fixture is specified. """ - if fixture is not None: module = get_caller_module() step_func = lambda request: request.getfuncargvalue(fixture) @@ -73,7 +73,8 @@ def given(name, fixture=None, converters=None): step_func.fixture = fixture func = pytest.fixture(lambda: step_func) func.__doc__ = 'Alias for the "{0}" fixture.'.format(fixture) - contribute_to_module(module, remove_prefix(name), func) + _, name = parse_line(name) + contribute_to_module(module, name, func) return _not_a_fixture_decorator return _step_decorator(GIVEN, name, converters=converters) diff --git a/tests/feature/test_cucumber_json.py b/tests/feature/test_cucumber_json.py index 41f23df..16b4fdd 100644 --- a/tests/feature/test_cucumber_json.py +++ b/tests/feature/test_cucumber_json.py @@ -35,6 +35,7 @@ def test_step_trace(testdir, equals_any): Scenario: Passing Given a passing step + And some other passing step Scenario: Failing Given a failing step @@ -47,6 +48,10 @@ def test_step_trace(testdir, equals_any): def a_passing_step(): return 'pass' + @given('some other passing step') + def some_other_passing_step(): + return 'pass' + @given('a failing step') def a_failing_step(): raise Exception('Error') @@ -82,7 +87,19 @@ def test_step_trace(testdir, equals_any): "result": { "status": "passed" } + }, + { + "keyword": "And", + "line": 5, + "match": { + "location": "" + }, + "name": "some other passing step", + "result": { + "status": "passed" + } } + ], "tags": [], "type": "scenario" @@ -91,12 +108,12 @@ def test_step_trace(testdir, equals_any): "description": "", "id": "test_failing", "keyword": "Scenario", - "line": 6, + "line": 7, "name": "Failing", "steps": [ { "keyword": "Given", - "line": 7, + "line": 8, "match": { "location": "" }, @@ -111,7 +128,7 @@ def test_step_trace(testdir, equals_any): "type": "scenario" } ], - "id": "one-passing-scenario,-one-failing-scenario", + "id": "test_step_trace0/test.feature", "keyword": "Feature", "line": 1, "name": "One passing scenario, one failing scenario",