gherkin 2.0.1-universal-dotnet → 2.0.2-universal-dotnet
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +12 -0
- data/VERSION.yml +1 -1
- data/features/json_formatter.feature +108 -48
- data/features/json_parser.feature +307 -0
- data/features/pretty_formatter.feature +9 -3
- data/features/step_definitions/json_lexer_steps.rb +20 -0
- data/features/step_definitions/pretty_formatter_steps.rb +44 -14
- data/lib/gherkin/cli/main.rb +1 -1
- data/lib/gherkin/formatter/json_formatter.rb +50 -13
- data/lib/gherkin/formatter/pretty_formatter.rb +23 -9
- data/lib/gherkin/i18n.rb +1 -1
- data/lib/gherkin/json_lexer.rb +103 -0
- data/lib/gherkin/tools/reformat.rb +6 -3
- data/lib/gherkin/tools/stats_listener.rb +3 -0
- data/ragel/lexer.java.rl.erb +2 -2
- data/ragel/lexer_common.rl.erb +1 -1
- data/spec/gherkin/fixtures/complex.json +124 -0
- data/spec/gherkin/formatter/pretty_formatter_spec.rb +3 -0
- data/spec/gherkin/json_lexer_spec.rb +97 -0
- data/spec/gherkin/shared/lexer_group.rb +12 -0
- data/spec/spec_helper.rb +2 -8
- data/tasks/compile.rake +16 -2
- metadata +10 -8
- data/lib/gherkin/parser/json_parser.rb +0 -102
- data/spec/gherkin/fixtures/complex.js +0 -105
- data/spec/gherkin/parser/json_parser_spec.rb +0 -129
@@ -4,6 +4,12 @@ Feature: Pretty Formatter
|
|
4
4
|
|
5
5
|
Scenario: Parse all the features in Cucumber
|
6
6
|
Given I have Cucumber's source code next to Gherkin's
|
7
|
-
|
8
|
-
|
9
|
-
Then
|
7
|
+
And I find all of the .feature files
|
8
|
+
When I send each prettified original through the "pretty" machinery
|
9
|
+
Then the machinery output should be identical to the prettified original
|
10
|
+
|
11
|
+
Scenario: Parse all the features in Cucumber with JSON
|
12
|
+
Given I have Cucumber's source code next to Gherkin's
|
13
|
+
And I find all of the .feature files
|
14
|
+
When I send each prettified original through the "json" machinery
|
15
|
+
Then the machinery output should be identical to the prettified original
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'gherkin/formatter/pretty_formatter'
|
3
|
+
require 'gherkin/json_lexer'
|
4
|
+
|
5
|
+
Given /^a PrettyFormatter$/ do
|
6
|
+
@io = StringIO.new
|
7
|
+
@formatter = Gherkin::Parser::FormatterListener.new(Gherkin::Formatter::PrettyFormatter.new(@io, true))
|
8
|
+
end
|
9
|
+
|
10
|
+
Given /^a JSON lexer$/ do
|
11
|
+
@json_parser = Gherkin::JSONLexer.new(@formatter)
|
12
|
+
end
|
13
|
+
|
14
|
+
Given /^the following JSON is parsed:$/ do |text|
|
15
|
+
@json_parser.scan(JSON.pretty_generate(JSON.parse(text)))
|
16
|
+
end
|
17
|
+
|
18
|
+
Then /^the outputted text should be:$/ do |expected_text|
|
19
|
+
expected_text.should == @io.string.strip
|
20
|
+
end
|
@@ -1,17 +1,50 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
require 'fileutils'
|
2
3
|
require 'gherkin'
|
3
4
|
require 'gherkin/formatter/pretty_formatter'
|
5
|
+
require 'gherkin/formatter/json_formatter'
|
6
|
+
require 'gherkin/json_lexer'
|
4
7
|
|
5
8
|
module PrettyPlease
|
6
|
-
def
|
9
|
+
def pretty_machinery(gherkin, feature_path)
|
7
10
|
io = StringIO.new
|
8
11
|
formatter = Gherkin::Formatter::PrettyFormatter.new(io, false)
|
9
12
|
listener = Gherkin::Parser::FormatterListener.new(formatter)
|
10
13
|
parser = Gherkin::Parser::Parser.new(listener, true)
|
11
14
|
lexer = Gherkin::I18nLexer.new(parser)
|
12
|
-
|
15
|
+
scan(lexer, gherkin, feature_path)
|
13
16
|
io.string
|
14
17
|
end
|
18
|
+
|
19
|
+
def json_machinery(gherkin, feature_path)
|
20
|
+
json = StringIO.new
|
21
|
+
json_formatter = Gherkin::Formatter::JSONFormatter.new(json)
|
22
|
+
formatter_listener = Gherkin::Parser::FormatterListener.new(json_formatter)
|
23
|
+
gherkin_lexer = Gherkin::I18nLexer.new(formatter_listener)
|
24
|
+
scan(gherkin_lexer, gherkin, feature_path)
|
25
|
+
|
26
|
+
result = StringIO.new
|
27
|
+
pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(result, false)
|
28
|
+
formatter_listener = Gherkin::Parser::FormatterListener.new(pretty_formatter)
|
29
|
+
json_lexer = Gherkin::JSONLexer.new(formatter_listener)
|
30
|
+
json_lexer.scan(json.string)
|
31
|
+
|
32
|
+
result.string
|
33
|
+
end
|
34
|
+
|
35
|
+
def scan(lexer, gherkin, feature_path)
|
36
|
+
begin
|
37
|
+
lexer.scan(gherkin, feature_path, 0)
|
38
|
+
rescue => e
|
39
|
+
if e.message =~ /Lexing error/
|
40
|
+
FileUtils.mkdir "tmp" unless File.directory?("tmp")
|
41
|
+
written_path = "tmp/#{File.basename(feature_path)}"
|
42
|
+
File.open(written_path, "w") {|io| io.write(gherkin)}
|
43
|
+
e.message << "\nSee #{written_path}"
|
44
|
+
end
|
45
|
+
raise e
|
46
|
+
end
|
47
|
+
end
|
15
48
|
end
|
16
49
|
|
17
50
|
World(PrettyPlease)
|
@@ -21,36 +54,33 @@ Given /^I have Cucumber's source code next to Gherkin's$/ do
|
|
21
54
|
raise "No Cucumber source in #{@cucumber_home}" unless File.file?(@cucumber_home + '/bin/cucumber')
|
22
55
|
end
|
23
56
|
|
24
|
-
|
57
|
+
Given /^I find all of the \.feature files$/ do
|
25
58
|
@feature_paths = Dir["#{@cucumber_home}/**/*.feature"].sort
|
26
59
|
end
|
27
60
|
|
28
|
-
When /^I
|
61
|
+
When /^I send each prettified original through the "([^"]*)" machinery$/ do |machinery|
|
29
62
|
@error = false
|
30
63
|
@feature_paths.each do |feature_path|
|
31
|
-
pretty1 = nil
|
32
|
-
pretty2 = nil
|
33
64
|
begin
|
34
|
-
|
35
|
-
|
36
|
-
|
65
|
+
original = pretty_machinery(IO.read(feature_path), feature_path)
|
66
|
+
via_machinery = self.__send__("#{machinery}_machinery", original, feature_path)
|
67
|
+
via_machinery.should == original
|
37
68
|
rescue RSpec::Expectations::ExpectationNotMetError => e
|
38
69
|
announce "=========="
|
39
70
|
announce feature_path
|
40
71
|
if(e.message =~ /(@@.*)/m)
|
41
72
|
announce $1
|
42
73
|
@error = true
|
43
|
-
File.open("p1.feature", "wb") {|io| io.write(pretty1)}
|
44
|
-
File.open("p2.feature", "wb") {|io| io.write(pretty2)}
|
45
74
|
else
|
46
75
|
announce "Identical, except for newlines"
|
47
76
|
end
|
48
77
|
rescue => e
|
49
|
-
e.message << "\nFatal error happened when parsing #{feature_path}"
|
78
|
+
e.message << "\nFatal error happened when parsing #{feature_path}."
|
79
|
+
raise e
|
50
80
|
end
|
51
81
|
end
|
52
82
|
end
|
53
83
|
|
54
|
-
Then /^
|
55
|
-
raise "Some features didn't
|
84
|
+
Then /^the machinery output should be identical to the prettified original$/ do
|
85
|
+
raise "Some features didn't make it through the machinery" if @error
|
56
86
|
end
|
data/lib/gherkin/cli/main.rb
CHANGED
@@ -22,23 +22,36 @@ module Gherkin
|
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
25
|
+
def background(comments, keyword, name, description, line)
|
26
|
+
background = {
|
27
|
+
'comments' => comments.to_a,
|
28
|
+
'keyword' => keyword,
|
29
|
+
'name' => name,
|
30
|
+
'description' => description,
|
31
|
+
'line' => line,
|
32
|
+
'steps' => [],
|
33
|
+
}
|
34
|
+
@json_hash['background'] = background
|
35
|
+
@in_background = true
|
36
|
+
end
|
37
|
+
|
25
38
|
def scenario(comments, tags, keyword, name, description, line)
|
26
|
-
|
39
|
+
@in_background = false
|
40
|
+
add_step_container(comments, tags, keyword, name, description, line, 'scenario')
|
27
41
|
end
|
28
42
|
|
29
43
|
def scenario_outline(comments, tags, keyword, name, description, line)
|
30
|
-
|
44
|
+
@in_background = false
|
45
|
+
add_step_container(comments, tags, keyword, name, description, line, 'scenario_outline')
|
31
46
|
end
|
32
47
|
|
33
48
|
def examples(comments, tags, keyword, name, description, line, table)
|
34
|
-
@table_container =
|
35
|
-
@table_container['
|
49
|
+
@table_container = add_examples(comments, tags, keyword, name, description, line)
|
50
|
+
@table_container['table'] = to_hash_array(table)
|
36
51
|
end
|
37
52
|
|
38
53
|
def step(comments, keyword, name, line, multiline_arg, status, exception, arguments, stepdef_location)
|
39
|
-
|
40
|
-
multiline_arg = to_hash_array(multiline_arg) if Array === multiline_arg
|
41
|
-
@table_container = {'comments' => comments.to_a, 'keyword' => keyword, 'name' => name, 'line' => line, 'multiline_arg' => multiline_arg}
|
54
|
+
@table_container = {'comments' => comments.to_a, 'keyword' => keyword, 'name' => name, 'line' => line}.merge(step_arg_to_hash(multiline_arg))
|
42
55
|
last_element['steps'] ||= []
|
43
56
|
last_element['steps'] << @table_container
|
44
57
|
end
|
@@ -49,27 +62,44 @@ module Gherkin
|
|
49
62
|
|
50
63
|
private
|
51
64
|
|
52
|
-
def
|
65
|
+
def element_hash(comments, tags, keyword, name, description, line, type=nil)
|
53
66
|
element = {
|
54
67
|
'comments' => comments.to_a,
|
55
68
|
'tags' => tags.to_a,
|
56
69
|
'keyword' => keyword,
|
57
70
|
'name' => name,
|
58
71
|
'description' => description,
|
59
|
-
'line' => line
|
72
|
+
'line' => line,
|
60
73
|
}
|
74
|
+
element['type'] = type if type
|
75
|
+
element
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_element(comments, tags, keyword, name, description, line, type)
|
79
|
+
element = element_hash(comments, tags, keyword, name, description, line, type)
|
61
80
|
@json_hash['elements'] ||= []
|
62
81
|
@json_hash['elements'] << element
|
63
82
|
element
|
64
83
|
end
|
65
84
|
|
66
|
-
def
|
67
|
-
|
85
|
+
def add_examples(comments, tags, keyword, name, description, line)
|
86
|
+
element = element_hash(comments, tags, keyword, name, description, line)
|
87
|
+
last_element['examples'] ||= []
|
88
|
+
last_element['examples'] << element
|
89
|
+
element
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_step_container(type, comments, tags, keyword, name, description, line)
|
93
|
+
add_element(type, comments, tags, keyword, name, description, line)
|
68
94
|
last_element['steps'] = []
|
69
95
|
end
|
70
96
|
|
71
97
|
def last_element
|
72
|
-
@
|
98
|
+
if @in_background
|
99
|
+
@json_hash['background']
|
100
|
+
else
|
101
|
+
@json_hash['elements'][-1]
|
102
|
+
end
|
73
103
|
end
|
74
104
|
|
75
105
|
def to_hash_array(rows)
|
@@ -77,6 +107,13 @@ module Gherkin
|
|
77
107
|
{"cells" => row.cells.to_a, "comments" => row.comments.to_a, "line" => row.line}
|
78
108
|
end
|
79
109
|
end
|
110
|
+
|
111
|
+
def step_arg_to_hash(multiline_arg)
|
112
|
+
return {} if multiline_arg.nil?
|
113
|
+
multiline_arg = rubify(multiline_arg)
|
114
|
+
Array === multiline_arg ? {"table" => to_hash_array(multiline_arg) } : { "py_string" => multiline_arg }
|
115
|
+
end
|
80
116
|
end
|
81
117
|
end
|
82
|
-
end
|
118
|
+
end
|
119
|
+
|
@@ -24,14 +24,14 @@ module Gherkin
|
|
24
24
|
print_comments(comments, '')
|
25
25
|
print_tags(tags, '')
|
26
26
|
@io.puts "#{keyword}: #{name}"
|
27
|
-
|
27
|
+
print_description(description, ' ', false)
|
28
28
|
end
|
29
29
|
|
30
30
|
def background(comments, keyword, name, description, line)
|
31
31
|
@io.puts
|
32
32
|
print_comments(comments, ' ')
|
33
33
|
@io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
|
34
|
-
|
34
|
+
print_description(description, ' ')
|
35
35
|
end
|
36
36
|
|
37
37
|
def scenario(comments, tags, keyword, name, description, line)
|
@@ -39,7 +39,7 @@ module Gherkin
|
|
39
39
|
print_comments(comments, ' ')
|
40
40
|
print_tags(tags, ' ')
|
41
41
|
@io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
|
42
|
-
|
42
|
+
print_description(description, ' ')
|
43
43
|
end
|
44
44
|
|
45
45
|
def scenario_outline(comments, tags, keyword, name, description, line)
|
@@ -51,7 +51,7 @@ module Gherkin
|
|
51
51
|
print_comments(comments, ' ')
|
52
52
|
print_tags(tags, ' ')
|
53
53
|
@io.puts " #{keyword}: #{name}"
|
54
|
-
|
54
|
+
print_description(description, ' ')
|
55
55
|
table(examples_table)
|
56
56
|
end
|
57
57
|
|
@@ -98,6 +98,9 @@ module Gherkin
|
|
98
98
|
max_lengths = cell_lengths.transpose.map { |col_lengths| col_lengths.max }.flatten
|
99
99
|
|
100
100
|
rows.each_with_index do |row, i|
|
101
|
+
row.comments.each do |comment|
|
102
|
+
@io.puts " #{comment}"
|
103
|
+
end
|
101
104
|
j = -1
|
102
105
|
@io.puts ' | ' + row.cells.zip(max_lengths).map { |cell, max_length|
|
103
106
|
j += 1
|
@@ -109,7 +112,7 @@ module Gherkin
|
|
109
112
|
private
|
110
113
|
|
111
114
|
def py_string(string)
|
112
|
-
@io.puts " \"\"\"\n" + string
|
115
|
+
@io.puts " \"\"\"\n" + escape_triple_quotes(indent(string, ' ')) + "\n \"\"\""
|
113
116
|
end
|
114
117
|
|
115
118
|
def exception(exception)
|
@@ -126,15 +129,19 @@ module Gherkin
|
|
126
129
|
end
|
127
130
|
|
128
131
|
if(RUBY_VERSION =~ /^1\.9/)
|
129
|
-
START = /#{
|
130
|
-
|
132
|
+
START = /#{'^'.encode('UTF-8')}/
|
133
|
+
TRIPLE_QUOTES = /#{'"""'.encode('UTF-8')}/
|
131
134
|
else
|
132
135
|
START = /^/
|
133
|
-
|
136
|
+
TRIPLE_QUOTES = /"""/
|
134
137
|
end
|
135
138
|
|
136
139
|
def indent(string, indentation)
|
137
|
-
string.gsub(
|
140
|
+
string.gsub(START, indentation)
|
141
|
+
end
|
142
|
+
|
143
|
+
def escape_triple_quotes(s)
|
144
|
+
s.gsub(TRIPLE_QUOTES, '\"\"\"')
|
138
145
|
end
|
139
146
|
|
140
147
|
def print_tags(tags, indent)
|
@@ -145,6 +152,13 @@ module Gherkin
|
|
145
152
|
@io.write(comments.empty? ? '' : indent + comments.join("\n#{indent}") + "\n")
|
146
153
|
end
|
147
154
|
|
155
|
+
def print_description(description, indent, newline=true)
|
156
|
+
if description != ""
|
157
|
+
@io.puts indent(description, indent)
|
158
|
+
@io.puts if newline
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
148
162
|
def indented_element_uri!(keyword, name, line)
|
149
163
|
return '' if @max_step_length.nil?
|
150
164
|
l = (keyword+name).unpack("U*").length
|
data/lib/gherkin/i18n.rb
CHANGED
@@ -58,7 +58,7 @@ module Gherkin
|
|
58
58
|
require 'gherkin/parser/row'
|
59
59
|
io = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
|
60
60
|
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
61
|
-
table = all.map do |i18n|
|
61
|
+
table = all.map do |i18n|
|
62
62
|
Parser::Row.new([i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], [], nil)
|
63
63
|
end
|
64
64
|
pf.table(table)
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Gherkin
|
4
|
+
class JSONLexer
|
5
|
+
|
6
|
+
def initialize(listener)
|
7
|
+
@listener = listener
|
8
|
+
end
|
9
|
+
|
10
|
+
def scan(src, uri='unknown.json', offset=0)
|
11
|
+
feature = JSON.parse(src)
|
12
|
+
|
13
|
+
comments_for(feature)
|
14
|
+
tags_for(feature)
|
15
|
+
multiline_event(:feature, feature)
|
16
|
+
|
17
|
+
if feature["background"]
|
18
|
+
comments_for(feature["background"])
|
19
|
+
multiline_event(:background, feature["background"])
|
20
|
+
steps_for(feature["background"])
|
21
|
+
end
|
22
|
+
|
23
|
+
feature["elements"].each do |feature_element|
|
24
|
+
parse_element(feature_element)
|
25
|
+
end if feature["elements"]
|
26
|
+
|
27
|
+
@listener.eof
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse_element(feature_element)
|
33
|
+
comments_for(feature_element)
|
34
|
+
tags_for(feature_element)
|
35
|
+
case feature_element["type"]
|
36
|
+
when "scenario" then parse_scenario(feature_element)
|
37
|
+
when "scenario_outline" then parse_outline(feature_element)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_outline(scenario_outline)
|
42
|
+
multiline_event(:scenario_outline, scenario_outline)
|
43
|
+
steps_for(scenario_outline)
|
44
|
+
scenario_outline["examples"].each do |examples|
|
45
|
+
comments_for(examples)
|
46
|
+
tags_for(examples)
|
47
|
+
multiline_event(:examples, examples)
|
48
|
+
rows_for(examples)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_scenario(scenario)
|
53
|
+
multiline_event(:scenario, scenario)
|
54
|
+
steps_for(scenario)
|
55
|
+
end
|
56
|
+
|
57
|
+
def comments_for(element)
|
58
|
+
element["comments"].each do |comment|
|
59
|
+
@listener.comment(comment, line_for(comment))
|
60
|
+
end if element["comments"]
|
61
|
+
end
|
62
|
+
|
63
|
+
def tags_for(element)
|
64
|
+
element["tags"].each do |tag|
|
65
|
+
@listener.tag(tag, line_for(tag))
|
66
|
+
end if element["tags"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def steps_for(element)
|
70
|
+
element["steps"].each do |step|
|
71
|
+
comments_for(step)
|
72
|
+
@listener.step(step["keyword"], step["name"], line_for(step))
|
73
|
+
py_string_for(step)
|
74
|
+
rows_for(step)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def py_string_for(element)
|
79
|
+
@listener.py_string(element["py_string"], 0) if element["py_string"]
|
80
|
+
end
|
81
|
+
|
82
|
+
def rows_for(element)
|
83
|
+
element["table"].each do |row|
|
84
|
+
comments_for(row)
|
85
|
+
@listener.row(cells_for(row), 0)
|
86
|
+
end if element["table"]
|
87
|
+
end
|
88
|
+
|
89
|
+
def cells_for(row)
|
90
|
+
row["cells"]
|
91
|
+
end
|
92
|
+
|
93
|
+
def line_for(element)
|
94
|
+
element["line"].to_i || 0
|
95
|
+
end
|
96
|
+
|
97
|
+
def multiline_event(type, element)
|
98
|
+
if element["keyword"]
|
99
|
+
@listener.__send__(type, element["keyword"], element["name"] || "", element["description"] || "", line_for(element))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|