-
-
Notifications
You must be signed in to change notification settings - Fork 68
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
base: main
Are you sure you want to change the base?
Changes from 15 commits
e5dad4c
2085773
709372c
b8ce724
c9c88a1
0ef264e
a697916
6387c51
bbe4d21
718d07c
e6e1269
4fb5930
12a3a93
c0b9467
16fbb63
7f1790c
6204f30
18b1cf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,35 +34,58 @@ describe('GherkinInMarkdownTokenMatcher', function () { | |
assert.strictEqual(token.matchedText, 'hello') | ||
}) | ||
|
||
it('matches FeatureLine without the Feature: keyword', () => { | ||
const line = new GherkinLine('# hello', location.line) | ||
const token = new Token(line, location) | ||
assert(tm.match_FeatureLine(token)) | ||
assert.strictEqual(token.matchedType, TokenType.FeatureLine) | ||
assert.strictEqual(token.matchedKeyword, undefined) | ||
assert.strictEqual(token.matchedText, '# hello') | ||
}) | ||
|
||
it('matches bullet Step', () => { | ||
const line = new GherkinLine(' * Given I have 3 cukes', location.line) | ||
const token = new Token(line, location) | ||
assert(tm.match_StepLine(token)) | ||
assert.strictEqual(token.matchedType, TokenType.StepLine) | ||
assert.strictEqual(token.matchedKeyword, 'Given ') | ||
assert.strictEqual(token.matchedKeywordType, 'Context') | ||
assert.strictEqual(token.matchedText, 'I have 3 cukes') | ||
assert.strictEqual(token.location.column, 6) | ||
}) | ||
|
||
it('matches plus Step', () => { | ||
const line = new GherkinLine(' + Given I have 3 cukes', location.line) | ||
const token = new Token(line, location) | ||
assert(tm.match_StepLine(token)) | ||
assert.strictEqual(token.matchedType, TokenType.StepLine) | ||
assert.strictEqual(token.matchedKeyword, 'Given ') | ||
assert.strictEqual(token.matchedKeywordType, 'Context') | ||
assert.strictEqual(token.matchedText, 'I have 3 cukes') | ||
assert.strictEqual(token.location.column, 6) | ||
}) | ||
|
||
it('matches hyphen Step', () => { | ||
const line = new GherkinLine(' - Given I have 3 cukes', location.line) | ||
const token = new Token(line, location) | ||
assert(tm.match_StepLine(token)) | ||
assert.strictEqual(token.matchedType, TokenType.StepLine) | ||
assert.strictEqual(token.matchedKeyword, 'Given ') | ||
assert.strictEqual(token.matchedKeywordType, 'Context') | ||
assert.strictEqual(token.matchedText, 'I have 3 cukes') | ||
assert.strictEqual(token.location.column, 6) | ||
}) | ||
|
||
it('matches a when Step', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this test as I had a bug in my Python parser. Is this an appropriate location to be testing If these are too low level (white-box / implementation detail), then the tests should be moved/removed |
||
const line = new GherkinLine(' - When I do something', location.line) | ||
const token = new Token(line, location) | ||
assert(tm.match_StepLine(token)) | ||
assert.strictEqual(token.matchedType, TokenType.StepLine) | ||
assert.strictEqual(token.matchedKeyword, 'When ') | ||
assert.strictEqual(token.matchedKeywordType, 'Action') | ||
assert.strictEqual(token.matchedText, 'I do something') | ||
assert.strictEqual(token.location.column, 6) | ||
}) | ||
|
||
it('matches arbitrary text as Other', () => { | ||
const line = new GherkinLine('Whatever', location.line) | ||
|
@@ -186,4 +209,21 @@ describe('GherkinInMarkdownTokenMatcher', function () { | |
] | ||
assert.deepStrictEqual(t.matchedItems, expectedItems) | ||
}) | ||
|
||
it('matches arbitrary text as Empty after the FeatureLine has already been matched', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is testing implementation detail, but I find it useful documentation for the token matcher - there is inherent state management here that I found confusing to begin with (resulting in a bug in my Python translation) This behaviour is captured under the acceptance tests, but was useful for me for understanding where differences between implementations were. Thoughts - is this useful to keep, or preferable to remove? |
||
// White Box testing - implementation detail... | ||
// Given the FeatureLine has already been matched | ||
const tFeatureLine = new Token(new GherkinLine('# something arbitrary', location.line), location); | ||
assert(tm.match_FeatureLine(tFeatureLine)) | ||
|
||
|
||
const t = new Token(new GherkinLine('arbitrary text', location.line), location); | ||
// (tm as any).matchedFeatureLine = true | ||
assert(tm.match_Empty(t)) | ||
assert.strictEqual(t.matchedType, TokenType.Empty) | ||
const expectedItems: Item[] =undefined | ||
assert.deepStrictEqual(t.matchedItems, expectedItems) | ||
assert.strictEqual(t.matchedKeyword, undefined) | ||
assert.strictEqual(t.matchedText, undefined) | ||
} ) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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')): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic is now spread across a few files (see Is there a better way to encapsulate this behaviour? Caveat from
|
||
print(parser.parse(scanner, GherkinInMarkdownTokenMatcher()) ) | ||
else: | ||
print(parser.parse(scanner)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
from gherkin.pickles.compiler import Compiler | ||
from gherkin.errors import ParserError, CompositeParserException | ||
from gherkin.stream.id_generator import IdGenerator | ||
from gherkin.token_matcher_markdown import GherkinInMarkdownTokenMatcher | ||
|
||
def create_errors(errors, uri): | ||
for error in errors: | ||
|
@@ -28,7 +29,10 @@ def enum(self, source_event): | |
source = source_event['source']['data'] | ||
|
||
try: | ||
gherkin_document = self.parser.parse(source) | ||
matcher=None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment above - DRY |
||
if(uri.endswith('.md')): | ||
matcher=GherkinInMarkdownTokenMatcher() | ||
gherkin_document = self.parser.parse(source, matcher) | ||
gherkin_document['uri'] = uri | ||
|
||
if (self.options.print_source): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,11 +5,18 @@ def source_event(path): | |
'source': { | ||
'uri': path, | ||
'data': io.open(path, 'r', encoding='utf8', newline='').read(), | ||
'mediaType': 'text/x.cucumber.gherkin+plain' | ||
'mediaType': _media_type(path) | ||
} | ||
} | ||
return event | ||
|
||
|
||
def _media_type(path): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment above - DRY |
||
if(path.endswith(".feature")): | ||
return 'text/x.cucumber.gherkin+plain' | ||
if(path.endswith(".feature.md")): | ||
return 'text/x.cucumber.gherkin+markdown' | ||
|
||
class SourceEvents: | ||
def __init__(self, paths): | ||
self.paths = paths | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ def match_FeatureLine(self, token): | |
|
||
if(self.matched_feature_line): | ||
self._set_token_matched(token,None) | ||
return False | ||
Comment on lines
27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note based on CONTRIBUTING.md:
There is a difference in behaviour for This was a cause for confusion for me, and a source of some of the issues here. |
||
|
||
# We first try to match "# Feature: blah" | ||
result = self._match_title_line(KEYWORD_PREFIX_HEADER, self.dialect.feature_keywords, ':', token, 'FeatureLine') | ||
|
@@ -34,6 +35,7 @@ def match_FeatureLine(self, token): | |
|
||
if not result: | ||
self._set_token_matched(token,'FeatureLine',token.line.get_line_text()) | ||
result=True | ||
self.matched_feature_line=result | ||
return result | ||
|
||
|
@@ -82,8 +84,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): | ||
|
||
|
@@ -180,11 +183,16 @@ def _match_title_line(self, prefix, keywords, keywordSuffix, token, token_type): | |
match = re.search(u'{}({}){}(.*)'.format(prefix, keywords_or_list, keywordSuffix), token.line.get_line_text()) | ||
indent = token.line.indent | ||
result = False | ||
|
||
matchedKeywordType=None | ||
if(match): | ||
matchedKeyword = match.group(2) | ||
indent += len(match.group(1)) | ||
self._set_token_matched(token, token_type, match.group(3).strip(), matchedKeyword, indent=indent) | ||
|
||
# only set the keyword type if this is a step keyword | ||
if( matchedKeyword in self.keyword_types ): | ||
matchedKeywordType = self.keyword_types[matchedKeyword][0] | ||
|
||
self._set_token_matched(token, token_type, match.group(3).strip(), matchedKeyword, keyword_type=matchedKeywordType, indent=indent) | ||
return True | ||
return False | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
@@ -24,6 +33,15 @@ def test_it_matches_FeatureLine_in_French(): | |
assert token.matched_keyword == u'Fonctionnalité' | ||
assert token.matched_text == 'hello' | ||
|
||
def test_it_matches_FeatureLine_without_the_Feature_keyword(): | ||
tm = GherkinInMarkdownTokenMatcher('en') | ||
line = GherkinLine('''# hello''',location['line']) | ||
token = Token(gherkin_line=line, location=location) | ||
assert tm.match_FeatureLine(token) | ||
assert token.matched_type == 'FeatureLine' | ||
assert token.matched_keyword == None | ||
assert token.matched_text == '# hello' | ||
|
||
def test_it_matches_bullet_Step(): | ||
tm = GherkinInMarkdownTokenMatcher('en') | ||
line = GherkinLine(''' * Given I have 3 cukes''',location['line']) | ||
|
@@ -41,6 +59,7 @@ def test_it_matches_plus_Step(): | |
assert tm.match_StepLine(token) | ||
assert token.matched_type == 'StepLine' | ||
assert token.matched_keyword == 'Given ' | ||
assert token.matched_keyword_type == 'Context' | ||
assert token.matched_text == 'I have 3 cukes' | ||
assert token.location['column'] == 6 | ||
|
||
|
@@ -51,9 +70,22 @@ def test_it_matches_hyphen_Step(): | |
assert tm.match_StepLine(token) | ||
assert token.matched_type == 'StepLine' | ||
assert token.matched_keyword == 'Given ' | ||
assert token.matched_keyword_type == 'Context' | ||
assert token.matched_text == 'I have 3 cukes' | ||
assert token.location['column'] == 6 | ||
|
||
def test_it_matches_a_when_Step(): | ||
tm = GherkinInMarkdownTokenMatcher('en') | ||
line = GherkinLine(''' - When I do something''',location['line']) | ||
token = Token(gherkin_line=line, location=location) | ||
assert tm.match_StepLine(token) | ||
assert token.matched_type == 'StepLine' | ||
assert token.matched_keyword == 'When ' | ||
assert token.matched_keyword_type == 'Action' | ||
assert token.matched_text == 'I do something' | ||
assert token.location['column'] == 6 | ||
|
||
|
||
def test_it_matches_arbitrary_text_as_Other(): | ||
tm = GherkinInMarkdownTokenMatcher('en') | ||
line = GherkinLine('''Whatever''',location['line']) | ||
|
@@ -151,11 +183,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') | ||
|
@@ -229,4 +263,32 @@ 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 | ||
|
||
def test_it_matches_arbitrary_text_as_Empty_after_the_FeatureLine_has_already_been_matched(): | ||
# White Box testing - implementation detail... | ||
# Given the FeatureLine has already been matched | ||
tm = GherkinInMarkdownTokenMatcher('en') | ||
|
||
line = GherkinLine('''# something arbitrary''',location['line']) | ||
token = Token(gherkin_line=line, location=location) | ||
assert(tm.match_FeatureLine(token)) | ||
|
||
line = GherkinLine('''arbitrary text''',location['line']) | ||
token=Token(gherkin_line=line, location=location) | ||
|
||
assert(tm.match_Empty(token)) | ||
assert token.matched_type == 'Empty' | ||
assert token.matched_items == [] | ||
assert token.matched_keyword == None | ||
assert token.matched_text == None | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added checks here since I had a bug when generating pickles.
Is this an appropriate place to be checking
matchedKeywordType