Skip to content

Commit

Permalink
fix(GherkinParser): steps with arguments now works correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
d-biehl committed Feb 5, 2024
1 parent 07ad49c commit 0c6ee44
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 10 deletions.
32 changes: 32 additions & 0 deletions examples/simple/features/doc_strings.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Feature: Parser Should Support DocStrings

Scenario: A scenario with a docstring
Given a blog post named "Random" with Markdown body
"""
Some Title, Eh?
===============
Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
"""

Scenario: A scenario with a docstring and multiple whitespace and vars
Given a blog post named "Random" with Markdown body
"""
😂🚲🚓
(❁´◡`❁)
(*/ω\*)
(^///^)
this text contains spaces
and ${TEST NAME}
"""

Scenario: A scenario with a backtick in the docstring
Given a blog post named "Random" with Markdown body
```python
😂🚲🚓
(❁´◡`❁)
(*/ω\*)
(^///^)
this text contains spaces
and ${TEST NAME}
```
12 changes: 12 additions & 0 deletions examples/simple/features/documentation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Feature: Scenario Outline with a docstring

Scenario Outline: Greetings come in many forms
Given this file:
"""<type>
Greeting:<content>
"""

Examples:
| type | content |
| en | Hello |
| fr | Bonjour |
30 changes: 27 additions & 3 deletions examples/simple/features/steps/simple.resource
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
*** Settings ***
Library step_impls.py
Library step_impls.py


*** Keywords ***
the minimalism
Log yeah

Table with example
[Arguments] ${blah}
[Tags] gherkin:step
[Arguments] ${rows}

FOR ${index} ${row} IN ENUMERATE @{rows}
Log ${index} ${{", ".join((c["value"] for c in $row["cells"]))}}
END

FOR ${row} IN @{rows}
FOR ${cell} IN @{row}[cells]
Log ${cell["value"]}
END
END

a blog post named "${name}" with Markdown body
[Tags] gherkin:step
[Arguments] ${body}

Log ${name}
Log ${body}

this file:
[Tags] gerkin:step
[Arguments] ${content} ${mediaType}=

log ${blah}
Log ${content}
Log ${mediaType}
6 changes: 4 additions & 2 deletions src/GherkinParser/Library.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,10 @@ def start_test(self, data: running.TestCase, result: result.TestCase) -> None:
self._create_setup_and_teardown(data, ("before-test", "before-test"))

# def start_keyword(self, data: running.Keyword, result: result.Keyword) -> None:
# # self.call_hooks(("before-keyword", "before-step"))
# pass
# # self.call_hooks(("before-keyword", "before-step"))
# # if result.tags.match("gerkin:step:docstring"):
# # data.args = (1,2)


# def end_keyword(self, data: running.Keyword, result: result.Keyword) -> None:
# # self.call_hooks(("after-keyword", "after-step"))
Expand Down
36 changes: 31 additions & 5 deletions src/GherkinParser/gherkin_builder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ast
import re
from os import PathLike
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
Expand Down Expand Up @@ -28,6 +29,26 @@

from .glob_path import iter_files

_CONTROL_WORDS = frozenset(("ELSE", "ELSE IF", "AND"))
_SEQUENCES_TO_BE_ESCAPED = ("=",)


def escape_whitespace(match: re.Match) -> str:
return "\\".join(match.group(0))


def escape(item: str) -> str:
if item in _CONTROL_WORDS:
return "\\" + item

item = repr(item)[1:-1]

for seq in _SEQUENCES_TO_BE_ESCAPED:
if seq in item:
item = item.replace(seq, "\\" + seq)

return re.sub(r"\s+", escape_whitespace, item)


def find_ast_node_id(
node: Any, id: str, parent: Any = None
Expand Down Expand Up @@ -85,12 +106,17 @@ def build_gherkin_model(source: PathLike[str], content: Optional[str] = None) ->
if node is None:
continue

datatable = node.get("dataTable")
args: Tuple[str, ...] = ()
step_argument = step.get("argument", None)
if step_argument is not None:
doc_string = step_argument.get("docString", None)
if doc_string is not None:
args = (f"&{{{{{escape(repr(doc_string))}}}}}",)

if datatable:
rows = [[v.get("value") for v in r.get("cells")] for r in datatable["rows"]]
args = (f"${{{{{rows!r}}}}}",)
datatable = step_argument.get("dataTable", None)
if datatable is not None:
#rows = [[v.get("value") for v in r.get("cells")] for r in datatable["rows"]]
args = (f"&{{{{{escape(repr(datatable))}}}}}",)

keyword_call = KeywordCall.from_params(
f"{node['keyword'] if step['type']!='Unknown' else ''}{step['text']}", args=args
Expand Down Expand Up @@ -164,7 +190,7 @@ def build_gherkin_model(source: PathLike[str], content: Optional[str] = None) ->
source=str(path),
)

# file.save(path.with_suffix(".robot").with_stem("_" + path.name))
file.save(path.with_suffix(".robot").with_stem("_" + path.name))

return file, gherkin_document["feature"]["name"]
except (SystemExit, KeyboardInterrupt):
Expand Down

0 comments on commit 0c6ee44

Please sign in to comment.