gherkin 2.0.1-universal-dotnet → 2.0.2-universal-dotnet
Sign up to get free protection for your applications and to get access to all the features.
- 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
|