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

Python enable markdown acceptance tests #64

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e5dad4c
python: Enable markdown acceptance tests
mpkorstanje Nov 8, 2022
2085773
fix: markdown - match empty lines
temyers Nov 25, 2022
709372c
fix: fix typo in markdown token generation
temyers Nov 25, 2022
b8ce724
fix: ensure markdown table headers are matched correctly.
temyers Nov 28, 2022
c9c88a1
WIP - add scenario parsing manually - Markdown files make feature hea…
temyers Nov 28, 2022
0ef264e
Add token test data - approval testing
temyers Nov 28, 2022
a697916
Revert change to testdata/good/minimal.feature.md
temyers Nov 29, 2022
6387c51
fix: generate AST for markdown features. Fix AST acceptance test for…
temyers Dec 13, 2022
bbe4d21
test: Add test coverage to ensure consistency between javascript and …
temyers Dec 27, 2022
718d07c
test: update test data using python gherkin parser
temyers Dec 27, 2022
e6e1269
test: update markdown pickels using javascript implementation
temyers Dec 27, 2022
4fb5930
fix: Fix python pickle generation failure caused by missing matched_k…
temyers Dec 27, 2022
12a3a93
test: generate markdown pickles using python implementation to remove…
temyers Dec 27, 2022
c0b9467
feat: Python - implement mediaType for markdown features
temyers Dec 27, 2022
16fbb63
revert: re-generate parser.py using berp. No changes were necessary.
temyers Dec 27, 2022
7f1790c
chore: remove dead code
temyers Dec 27, 2022
6204f30
fix: update markdown test data using Python implementation following …
temyers Dec 27, 2022
18b1cf0
test: add tests to demonstrate inconsistency between Javascript and P…
temyers Dec 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ SOURCE_FILES = $(shell find . -name "*.py" | grep -v $(GHERKIN_PARSER))
GHERKIN = bin/gherkin
GHERKIN_GENERATE_TOKENS = bin/gherkin-generate-tokens

GOOD_FEATURE_FILES = $(shell find ../testdata/good -name "*.feature")
BAD_FEATURE_FILES = $(shell find ../testdata/bad -name "*.feature")
GOOD_FEATURE_FILES = $(shell find ../testdata/good -name "*.feature" -o -name "*.feature.md")
BAD_FEATURE_FILES = $(shell find ../testdata/bad -name "*.feature" -o -name "*.feature.md")

TOKENS = $(patsubst ../testdata/%,acceptance/testdata/%.tokens,$(GOOD_FEATURE_FILES))
ASTS = $(patsubst ../testdata/%,acceptance/testdata/%.ast.ndjson,$(GOOD_FEATURE_FILES))
Expand Down
8 changes: 7 additions & 1 deletion python/bin/gherkin_generate_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
from gherkin.token_scanner import TokenScanner
from gherkin.token_formatter_builder import TokenFormatterBuilder
from gherkin.parser import Parser
from gherkin.token_matcher_markdown import GherkinInMarkdownTokenMatcher

files = sys.argv[1:]
if sys.version_info < (3, 0) and os.name != 'nt': # for Python2 unless on Windows native
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)

parser = Parser(TokenFormatterBuilder())
for file in files:
scanner = TokenScanner(file)
print(parser.parse(scanner))

if(file.endswith('.md')):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is now spread across a few files (see source_events.py)

Is there a better way to encapsulate this behaviour?

Caveat from CONTRIBUTING.md:

TL;DR anyone who only knows one of the supported programming languages should be
able to fix a bug or add a feature in all the other implementations. - By virtue of
finding their way around a consistently organised codebase.

print(parser.parse(scanner, GherkinInMarkdownTokenMatcher()) )
else:
print(parser.parse(scanner))
5 changes: 5 additions & 0 deletions python/gherkin/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ def match_token_at_0(self, token, context):
if self.match_Empty(context, token):
self.build(context, token)
return 0
# FIXME - move into gherkin-python.razor
if self.match_ScenarioLine(context,token):
temyers marked this conversation as resolved.
Show resolved Hide resolved
self.build(context, token)
return 12


state_comment = "State: 0 - Start"
token.detach
Expand Down
3 changes: 2 additions & 1 deletion python/gherkin/token_matcher_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ def match_Comment(self, token):
if(token.line.startswith('|')):
table_cells = token.line.table_cells
if(self._is_gfm_table_separator(table_cells)):
self._set_token_matched(token,"Comment")
return True
return self._set_token_matched(token,None,False)
return self._set_token_matched(token,None)

def match_Empty(self, token):

Expand Down
24 changes: 22 additions & 2 deletions python/test/gherkin_in_markdown_token_matcher_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
from gherkin.gherkin_line import GherkinLine
location = { 'line': 1, 'column': 1 }

def test_it_matches_FeatureLine():
def test_it_matches_FeatureLineH1():
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside the scope of this PR, but I recently found pytest-describe that would make it easier to match the describe style of the javascript variant

I really prefer it - it enables me to structure my specifications more logically.

One to think about...

tm = GherkinInMarkdownTokenMatcher('en')
line = GherkinLine('''# Feature: hello''',location['line'])
token = Token(gherkin_line=line, location=location)
assert tm.match_FeatureLine(token)
assert token.matched_type == 'FeatureLine'
assert token.matched_keyword == 'Feature'
assert token.matched_text == 'hello'

def test_it_matches_FeatureLineH2():
tm = GherkinInMarkdownTokenMatcher('en')
line = GherkinLine('''## Feature: hello''',location['line'])
token = Token(gherkin_line=line, location=location)
Expand Down Expand Up @@ -151,11 +160,13 @@ def test_it_matches_table_separator_row_as_comment():
l1 = GherkinLine(' | h1 | h2 |',location['line'])
t1 = Token(l1,location)
assert tm.match_TableRow(t1)
assert t1.location['column'] == 3

l2 = GherkinLine(' | --- | --- |',location['line'])
t2 = Token(l2,location)
assert not tm.match_TableRow(t2)
assert tm.match_Comment(t2)
assert t2.location['column'] == 3

def test_it_matches_indented_tags():
tm = GherkinInMarkdownTokenMatcher('en')
Expand Down Expand Up @@ -229,4 +240,13 @@ def test_it_matches_ExamplesLine():
assert tm.match_ExamplesLine(token)
assert token.matched_type == 'ExamplesLine'
assert token.matched_keyword == 'Examples'
assert token.matched_text == ''
assert token.matched_text == ''

def test_it_matches_Empty():
tm = GherkinInMarkdownTokenMatcher('en')
line = GherkinLine('''''',location['line'])
token = Token(gherkin_line=line, location=location)
assert tm.match_Empty(token)
assert token.matched_type == 'Empty'
assert token.matched_keyword == None
assert token.matched_text == None
9 changes: 9 additions & 0 deletions testdata/good/datatables.feature.md.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(1:4)FeatureLine:()Feature/DataTables/
(2:1)Empty://
(3:5)ScenarioLine:()Scenario/minimalistic/
(4:1)Empty://
(5:3)StepLine:()Given /a simple data table/
(6:3)TableRow:()|//5:foo,11:bar
(7:3)Comment://
(8:3)TableRow:()|//5:boz,11:boo
EOF
9 changes: 9 additions & 0 deletions testdata/good/docstrings.feature.md.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(1:4)FeatureLine:()Feature/DocString variations/
(2:1)Empty://
(3:5)ScenarioLine:()Scenario/minimalistic/
(4:1)Empty://
(5:3)StepLine:()And /a DocString with an implicitly escaped separator inside/
(6:1)DocStringSeparator:()````//
(7:1)Other:/```/
(8:1)DocStringSeparator:()````//
EOF
2 changes: 1 addition & 1 deletion testdata/good/minimal.feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

## Scenario: minimalistic

* Given the minimalism
* Given the minimalism
temyers marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions testdata/good/minimal.feature.md.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(1:3)FeatureLine:()Feature/Minimal/
(2:1)Empty://
(3:4)ScenarioLine:()Scenario/minimalistic/
(4:1)Empty://
(5:3)StepLine:()Given /the minimalism/
EOF
26 changes: 26 additions & 0 deletions testdata/good/misc.feature.md.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(1:1)Empty://
(2:1)Empty://
(3:1)Empty://
(4:1)Empty://
(5:1)Empty://
(6:1)Empty://
(7:1)Empty://
(8:1)Empty://
(9:1)Empty://
(10:1)Empty://
(11:1)Empty://
(12:3)ScenarioLine:()Scenario/Something about math/
(13:3)StepLine:()Given /step one/
(14:3)StepLine:()When /step two/
(15:3)StepLine:()Then /step three/
(16:1)Empty://
(17:3)ScenarioLine:()Scenario/Something about gravity/
(18:4)StepLine:()Given /step one/
(19:4)StepLine:()When /step two/
(20:4)StepLine:()Then /step three/
(21:1)Empty://
(22:1)Empty://
(23:1)Empty://
(24:1)Empty://
(25:1)Empty://
EOF
43 changes: 43 additions & 0 deletions testdata/good/tags.feature.md.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
(1:1)TagLine://2:@feature_tag1,18:@feature_tag2
(2:3)TagLine://4:@feature_tag3
(3:3)FeatureLine:()Feature/Minimal Scenario Outline/
(4:1)Empty://
(5:1)TagLine://2:@scenario_tag1,19:@scenario_tag2
(6:3)TagLine://4:@scenario_tag3
(7:4)ScenarioLine:()Scenario/minimalistic/
(8:3)StepLine:()Given /the minimalism/
(9:1)Empty://
(10:1)TagLine://2:@so_tag1,14:@so_tag2
(11:3)TagLine://4:@so_tag3
(12:4)ScenarioLine:()Scenario Outline/minimalistic outline/
(13:3)StepLine:()Given /the <what>/
(14:1)Empty://
(15:1)TagLine://2:@ex_tag1,13:@ex_tag2
(16:3)TagLine://4:@ex_tag3
(17:5)ExamplesLine:()Examples//
(18:3)TableRow:()|//5:what
(19:3)Comment://
(20:3)TableRow:()|//5:minimalism
(21:1)Empty://
(22:1)TagLine://2:@ex_tag4,13:@ex_tag5
(23:3)TagLine://4:@ex_tag6
(24:5)ExamplesLine:()Examples//
(25:3)TableRow:()|//5:what
(26:3)Comment://
(27:3)TableRow:()|//5:more minimalism
(28:1)Empty://
(29:1)TagLine://2:@comment_tag1
(30:4)ScenarioLine:()Scenario/comments/
(31:1)Empty://
(32:1)Empty://
(33:1)TagLine://2:@comment_tag#2
(34:4)ScenarioLine:()Scenario/hash in tags/
(35:1)Empty://
(36:1)Empty://
(37:1)TagLine://2:@rule_tag
(38:4)RuleLine:()Rule//
(39:1)Empty://
(40:1)TagLine://2:@joined_tag3,16:@joined_tag4
(41:5)ScenarioLine:()Scenario/joined tags/
(42:1)Empty://
EOF