gherkin 1.0.30 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/History.txt +19 -0
- data/Rakefile +4 -4
- data/VERSION.yml +2 -2
- data/features/feature_parser.feature +11 -0
- data/features/json_formatter.feature +238 -0
- data/features/pretty_formatter.feature +9 -0
- data/features/step_definitions/gherkin_steps.rb +1 -1
- data/features/step_definitions/json_formatter_steps.rb +32 -0
- data/features/step_definitions/pretty_formatter_steps.rb +24 -23
- data/features/support/env.rb +3 -3
- data/lib/gherkin/formatter/json_formatter.rb +82 -0
- data/lib/gherkin/formatter/pretty_formatter.rb +73 -78
- data/lib/gherkin/i18n.rb +22 -18
- data/lib/gherkin/i18n.yml +9 -9
- data/lib/gherkin/i18n_lexer.rb +2 -2
- data/lib/gherkin/parser/event.rb +6 -6
- data/lib/gherkin/parser/filter_listener.rb +5 -1
- data/lib/gherkin/parser/formatter_listener.rb +113 -0
- data/lib/gherkin/parser/json_parser.rb +102 -0
- data/lib/gherkin/parser/parser.rb +10 -2
- data/lib/gherkin/parser/row.rb +15 -0
- data/lib/gherkin/rubify.rb +2 -0
- data/lib/gherkin/tools/files.rb +1 -1
- data/lib/gherkin/tools/reformat.rb +1 -2
- data/lib/gherkin/tools/stats.rb +1 -1
- data/lib/gherkin/tools/stats_listener.rb +5 -5
- data/ragel/lexer.c.rl.erb +41 -12
- data/ragel/lexer.java.rl.erb +26 -17
- data/ragel/lexer.rb.rl.erb +10 -5
- data/ragel/lexer_common.rl.erb +6 -6
- data/spec/gherkin/c_lexer_spec.rb +2 -2
- data/spec/gherkin/fixtures/complex.js +105 -0
- data/spec/gherkin/formatter/argument_spec.rb +3 -3
- data/spec/gherkin/formatter/colors_spec.rb +3 -4
- data/spec/gherkin/formatter/pretty_formatter_spec.rb +21 -50
- data/spec/gherkin/i18n_lexer_spec.rb +6 -6
- data/spec/gherkin/i18n_spec.rb +16 -9
- data/spec/gherkin/java_lexer_spec.rb +1 -2
- data/spec/gherkin/output_stream_string_io.rb +24 -0
- data/spec/gherkin/parser/filter_listener_spec.rb +16 -9
- data/spec/gherkin/parser/formatter_listener_spec.rb +134 -0
- data/spec/gherkin/parser/json_parser_spec.rb +129 -0
- data/spec/gherkin/parser/parser_spec.rb +9 -9
- data/spec/gherkin/parser/tag_expression_spec.rb +8 -8
- data/spec/gherkin/rb_lexer_spec.rb +1 -1
- data/spec/gherkin/sexp_recorder.rb +21 -1
- data/spec/gherkin/shared/{lexer_spec.rb → lexer_group.rb} +172 -102
- data/spec/gherkin/shared/{py_string_spec.rb → py_string_group.rb} +21 -17
- data/spec/gherkin/shared/{row_spec.rb → row_group.rb} +36 -19
- data/spec/gherkin/shared/{tags_spec.rb → tags_group.rb} +13 -9
- data/spec/spec_helper.rb +18 -38
- data/tasks/bench.rake +3 -3
- data/tasks/compile.rake +13 -14
- data/tasks/rspec.rake +6 -11
- metadata +42 -28
- data/features/pretty_printer.feature +0 -14
- data/spec/gherkin/csharp_lexer_spec.rb +0 -20
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'json/pure' # Needed to make JSON.generate work.
|
3
|
+
require 'gherkin/rubify'
|
4
|
+
|
5
|
+
module Gherkin
|
6
|
+
module Formatter
|
7
|
+
class JSONFormatter
|
8
|
+
include Rubify
|
9
|
+
|
10
|
+
def initialize(io)
|
11
|
+
@io = io
|
12
|
+
end
|
13
|
+
|
14
|
+
def feature(comments, tags, keyword, name, description, uri)
|
15
|
+
@json_hash = {
|
16
|
+
'comments' => comments.to_a,
|
17
|
+
'tags' => tags.to_a,
|
18
|
+
'keyword' => keyword,
|
19
|
+
'name' => name,
|
20
|
+
'description' => description,
|
21
|
+
'uri' => uri
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def scenario(comments, tags, keyword, name, description, line)
|
26
|
+
add_step_container(comments, tags, keyword, name, description, line)
|
27
|
+
end
|
28
|
+
|
29
|
+
def scenario_outline(comments, tags, keyword, name, description, line)
|
30
|
+
add_step_container(comments, tags, keyword, name, description, line)
|
31
|
+
end
|
32
|
+
|
33
|
+
def examples(comments, tags, keyword, name, description, line, table)
|
34
|
+
@table_container = add_element(comments, tags, keyword, name, description, line)
|
35
|
+
@table_container['examples_table'] = to_hash_array(table)
|
36
|
+
end
|
37
|
+
|
38
|
+
def step(comments, keyword, name, line, multiline_arg, status, exception, arguments, stepdef_location)
|
39
|
+
multiline_arg = rubify(multiline_arg)
|
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}
|
42
|
+
last_element['steps'] ||= []
|
43
|
+
last_element['steps'] << @table_container
|
44
|
+
end
|
45
|
+
|
46
|
+
def eof
|
47
|
+
@io.write(@json_hash.to_json)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def add_element(comments, tags, keyword, name, description, line)
|
53
|
+
element = {
|
54
|
+
'comments' => comments.to_a,
|
55
|
+
'tags' => tags.to_a,
|
56
|
+
'keyword' => keyword,
|
57
|
+
'name' => name,
|
58
|
+
'description' => description,
|
59
|
+
'line' => line
|
60
|
+
}
|
61
|
+
@json_hash['elements'] ||= []
|
62
|
+
@json_hash['elements'] << element
|
63
|
+
element
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_step_container(comments, tags, keyword, name, description, line)
|
67
|
+
add_element(comments, tags, keyword, name, description, line)
|
68
|
+
last_element['steps'] = []
|
69
|
+
end
|
70
|
+
|
71
|
+
def last_element
|
72
|
+
@json_hash['elements'][-1]
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_hash_array(rows)
|
76
|
+
rows.map do |row|
|
77
|
+
{"cells" => row.cells.to_a, "comments" => row.comments.to_a, "line" => row.line}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -13,66 +13,66 @@ module Gherkin
|
|
13
13
|
include Colors
|
14
14
|
include Escaping
|
15
15
|
|
16
|
-
def initialize(io, monochrome
|
16
|
+
def initialize(io, monochrome)
|
17
17
|
@io = io
|
18
18
|
@monochrome = monochrome
|
19
19
|
@format = MonochromeFormat.new #@monochrome ? MonochromeFormat.new : AnsiColorFormat.new
|
20
|
-
@tags = nil
|
21
|
-
@comments = nil
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
@
|
26
|
-
|
22
|
+
def feature(comments, tags, keyword, name, description, uri)
|
23
|
+
@uri = uri
|
24
|
+
print_comments(comments, '')
|
25
|
+
print_tags(tags, '')
|
26
|
+
@io.puts "#{keyword}: #{name}"
|
27
|
+
@io.puts indent(description, ' ') unless description == ""
|
27
28
|
end
|
28
29
|
|
29
|
-
def
|
30
|
-
@
|
31
|
-
|
30
|
+
def background(comments, keyword, name, description, line)
|
31
|
+
@io.puts
|
32
|
+
print_comments(comments, ' ')
|
33
|
+
@io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
|
34
|
+
@io.puts indent(description, ' ') unless description == ""
|
32
35
|
end
|
33
36
|
|
34
|
-
def
|
35
|
-
@io.puts
|
37
|
+
def scenario(comments, tags, keyword, name, description, line)
|
38
|
+
@io.puts
|
39
|
+
print_comments(comments, ' ')
|
40
|
+
print_tags(tags, ' ')
|
41
|
+
@io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
|
42
|
+
@io.puts indent(description, ' ') unless description == ""
|
36
43
|
end
|
37
44
|
|
38
|
-
def
|
39
|
-
|
45
|
+
def scenario_outline(comments, tags, keyword, name, description, line)
|
46
|
+
scenario(comments, tags, keyword, name, description, line)
|
40
47
|
end
|
41
48
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
49
|
+
def examples(comments, tags, keyword, name, description, line, examples_table)
|
50
|
+
@io.puts
|
51
|
+
print_comments(comments, ' ')
|
52
|
+
print_tags(tags, ' ')
|
53
|
+
@io.puts " #{keyword}: #{name}"
|
54
|
+
@io.puts indent(description, ' ') unless description == ""
|
55
|
+
table(examples_table)
|
45
56
|
end
|
46
57
|
|
47
|
-
def
|
48
|
-
flush_table
|
49
|
-
@io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
|
50
|
-
end
|
51
|
-
|
52
|
-
def examples(keyword, name, line)
|
53
|
-
flush_table
|
54
|
-
@io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
|
55
|
-
end
|
56
|
-
|
57
|
-
def step(keyword, name, line, status=nil, exception=nil, arguments=nil, location=nil)
|
58
|
-
flush_table
|
58
|
+
def step(comments, keyword, name, line, multiline_arg, status, exception, arguments, stepdef_location)
|
59
59
|
status_param = "#{status}_param" if status
|
60
60
|
name = Gherkin::Formatter::Argument.format(name, @format, (arguments || []))
|
61
|
-
#{|arg| status_param ? self.__send__(status_param, arg, @monochrome) : arg} if arguments
|
62
61
|
|
63
|
-
step = "#{keyword}#{
|
62
|
+
step = "#{keyword}#{name}"
|
64
63
|
step = self.__send__(status, step, @monochrome) if status
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
65
|
+
print_comments(comments, ' ')
|
66
|
+
@io.puts(" #{step}#{indented_step_location!(stepdef_location)}")
|
67
|
+
case multiline_arg
|
68
|
+
when String
|
69
|
+
py_string(multiline_arg)
|
70
|
+
when Array
|
71
|
+
table(multiline_arg)
|
72
|
+
when NilClass
|
73
|
+
else
|
74
|
+
raise "Bad multiline_arg: #{multiline_arg.inspect}"
|
75
|
+
end
|
76
76
|
end
|
77
77
|
|
78
78
|
def syntax_error(state, event, legal_events, line)
|
@@ -80,7 +80,6 @@ module Gherkin
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def eof
|
83
|
-
flush_table
|
84
83
|
end
|
85
84
|
|
86
85
|
# This method can be invoked before a #scenario, to ensure location arguments are aligned
|
@@ -90,79 +89,75 @@ module Gherkin
|
|
90
89
|
@step_index = -1
|
91
90
|
end
|
92
91
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
return if @rows.nil?
|
100
|
-
cell_lengths = @rows.map { |col| col.map { |cell| cell.unpack("U*").length }}
|
92
|
+
def table(rows)
|
93
|
+
cell_lengths = rows.map do |row|
|
94
|
+
row.cells.map do |cell|
|
95
|
+
escape_cell(cell).unpack("U*").length
|
96
|
+
end
|
97
|
+
end
|
101
98
|
max_lengths = cell_lengths.transpose.map { |col_lengths| col_lengths.max }.flatten
|
102
99
|
|
103
|
-
|
100
|
+
rows.each_with_index do |row, i|
|
104
101
|
j = -1
|
105
|
-
@io.puts ' | ' + row.zip(max_lengths).map { |cell, max_length|
|
102
|
+
@io.puts ' | ' + row.cells.zip(max_lengths).map { |cell, max_length|
|
106
103
|
j += 1
|
107
|
-
color(cell,
|
104
|
+
color(cell, nil, j) + ' ' * (max_length - cell_lengths[i][j])
|
108
105
|
}.join(' | ') + ' |'
|
109
|
-
exception(exception) if exception
|
110
106
|
end
|
111
|
-
@rows = nil
|
112
107
|
end
|
113
108
|
|
114
|
-
|
109
|
+
private
|
110
|
+
|
111
|
+
def py_string(string)
|
112
|
+
@io.puts " \"\"\"\n" + string.gsub(START, ' ').gsub(/"""/,'\"\"\"') + "\n \"\"\""
|
113
|
+
end
|
114
|
+
|
115
|
+
def exception(exception)
|
116
|
+
exception_text = "#{exception.message} (#{exception.class})\n#{(exception.backtrace || []).join("\n")}".gsub(/^/, ' ')
|
117
|
+
@io.puts(failed(exception_text, @monochrome))
|
118
|
+
end
|
115
119
|
|
116
120
|
def color(cell, statuses, col)
|
117
121
|
if statuses
|
118
|
-
self.__send__(statuses[col], cell, @monochrome) + (@monochrome ? '' : reset)
|
122
|
+
self.__send__(statuses[col], escape_cell(cell), @monochrome) + (@monochrome ? '' : reset)
|
119
123
|
else
|
120
|
-
cell
|
124
|
+
escape_cell(cell)
|
121
125
|
end
|
122
126
|
end
|
123
127
|
|
124
128
|
if(RUBY_VERSION =~ /^1\.9/)
|
125
129
|
START = /#{"^".encode('UTF-8')}/
|
126
|
-
|
130
|
+
CRLF = Regexp.new("\r\n".encode('UTF-8'))
|
127
131
|
else
|
128
132
|
START = /^/
|
129
|
-
|
133
|
+
CRLF = /\r\n/n
|
130
134
|
end
|
131
135
|
|
132
136
|
def indent(string, indentation)
|
133
|
-
|
134
|
-
string.split(NL).map do |l|
|
135
|
-
s = "#{indent}#{l}"
|
136
|
-
indent = indentation
|
137
|
-
s
|
138
|
-
end.join("\n")
|
137
|
+
string.gsub(/^/, indentation)
|
139
138
|
end
|
140
139
|
|
141
|
-
def
|
142
|
-
|
143
|
-
@tags = nil
|
144
|
-
tags
|
140
|
+
def print_tags(tags, indent)
|
141
|
+
@io.write(tags.empty? ? '' : indent + tags.join(' ') + "\n")
|
145
142
|
end
|
146
143
|
|
147
|
-
def
|
148
|
-
|
149
|
-
@comments = nil
|
150
|
-
comments
|
144
|
+
def print_comments(comments, indent)
|
145
|
+
@io.write(comments.empty? ? '' : indent + comments.join("\n#{indent}") + "\n")
|
151
146
|
end
|
152
147
|
|
153
|
-
def
|
154
|
-
return
|
148
|
+
def indented_element_uri!(keyword, name, line)
|
149
|
+
return '' if @max_step_length.nil?
|
155
150
|
l = (keyword+name).unpack("U*").length
|
156
151
|
@max_step_length = [@max_step_length, l].max
|
157
152
|
indent = @max_step_length - l
|
158
|
-
' ' * indent + ' ' + comments("# #{
|
153
|
+
' ' * indent + ' ' + comments("# #{@uri}:#{line}", @monochrome)
|
159
154
|
end
|
160
155
|
|
161
156
|
def indented_step_location!(location)
|
162
|
-
return
|
157
|
+
return '' if location.nil?
|
163
158
|
indent = @max_step_length - @step_lengths[@step_index+=1]
|
164
159
|
' ' * indent + ' ' + comments("# #{location}", @monochrome)
|
165
160
|
end
|
166
161
|
end
|
167
162
|
end
|
168
|
-
end
|
163
|
+
end
|
data/lib/gherkin/i18n.rb
CHANGED
@@ -34,14 +34,14 @@ module Gherkin
|
|
34
34
|
unique_keywords = all.map do |i18n|
|
35
35
|
keywords.map do |keyword|
|
36
36
|
if keyword.to_s == 'step'
|
37
|
-
i18n.step_keywords
|
37
|
+
i18n.step_keywords.to_a
|
38
38
|
else
|
39
|
-
i18n.keywords(keyword)
|
39
|
+
i18n.keywords(keyword).to_a
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
unique_keywords.flatten.compact.sort.reverse.uniq.join('|').gsub(/\*/, '\*')
|
44
|
+
unique_keywords.flatten.compact.map{|kw| kw.to_s}.sort.reverse.uniq.join('|').gsub(/\*/, '\*')
|
45
45
|
end
|
46
46
|
|
47
47
|
def code_keywords
|
@@ -55,15 +55,17 @@ module Gherkin
|
|
55
55
|
def language_table
|
56
56
|
require 'stringio'
|
57
57
|
require 'gherkin/formatter/pretty_formatter'
|
58
|
+
require 'gherkin/parser/row'
|
58
59
|
io = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
|
59
60
|
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
60
|
-
all.
|
61
|
-
|
61
|
+
table = all.map do |i18n|
|
62
|
+
Parser::Row.new([i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], [], nil)
|
63
|
+
end
|
64
|
+
pf.table(table)
|
62
65
|
if defined?(JRUBY_VERSION)
|
63
66
|
io.getBuffer.toString
|
64
67
|
else
|
65
|
-
io.
|
66
|
-
io.read
|
68
|
+
io.string
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
@@ -136,37 +138,39 @@ module Gherkin
|
|
136
138
|
result
|
137
139
|
end
|
138
140
|
|
139
|
-
def keywords(
|
140
|
-
|
141
|
-
raise "No #{
|
142
|
-
@keywords[
|
141
|
+
def keywords(key)
|
142
|
+
key = key.to_s
|
143
|
+
raise "No #{key.inspect} in #{@keywords.inspect}" if @keywords[key].nil?
|
144
|
+
@keywords[key].split('|').map{|keyword| real_keyword(key, keyword)}
|
143
145
|
end
|
144
146
|
|
145
147
|
def keyword_table
|
146
148
|
require 'stringio'
|
147
149
|
require 'gherkin/formatter/pretty_formatter'
|
150
|
+
require 'gherkin/parser/row'
|
148
151
|
io = StringIO.new
|
149
152
|
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
150
153
|
|
151
|
-
KEYWORD_KEYS.
|
152
|
-
|
154
|
+
gherkin_keyword_table = KEYWORD_KEYS.map do |key|
|
155
|
+
Parser::Row.new([key, keywords(key).map{|keyword| %{"#{keyword}"}}.join(', ')], [], nil)
|
153
156
|
end
|
154
|
-
|
157
|
+
|
158
|
+
code_keyword_table = STEP_KEYWORD_KEYS.map do |key|
|
155
159
|
code_keywords = keywords(key).reject{|keyword| keyword == '* '}.map do |keyword|
|
156
160
|
%{"#{self.class.code_keyword_for(keyword)}"}
|
157
161
|
end.join(', ')
|
158
|
-
|
162
|
+
Parser::Row.new(["#{key} (code)", code_keywords], [], nil)
|
159
163
|
end
|
160
164
|
|
161
|
-
pf.
|
165
|
+
pf.table(gherkin_keyword_table + code_keyword_table)
|
162
166
|
io.rewind
|
163
167
|
io.read
|
164
168
|
end
|
165
169
|
|
166
170
|
private
|
167
171
|
|
168
|
-
def real_keyword(
|
169
|
-
if(STEP_KEYWORD_KEYS.index(
|
172
|
+
def real_keyword(key, keyword)
|
173
|
+
if(STEP_KEYWORD_KEYS.index(key))
|
170
174
|
(keyword + ' ').sub(/< $/, '')
|
171
175
|
else
|
172
176
|
keyword
|
data/lib/gherkin/i18n.yml
CHANGED
@@ -269,8 +269,8 @@
|
|
269
269
|
scenario: Forgatókönyv
|
270
270
|
scenario_outline: Forgatókönyv vázlat
|
271
271
|
examples: Példák
|
272
|
-
given: "*|
|
273
|
-
when: "*|Majd"
|
272
|
+
given: "*|Amennyiben|Adott"
|
273
|
+
when: "*|Majd|Ha|Amikor"
|
274
274
|
then: "*|Akkor"
|
275
275
|
and: "*|És"
|
276
276
|
but: "*|De"
|
@@ -451,9 +451,9 @@
|
|
451
451
|
scenario: Сценарий
|
452
452
|
scenario_outline: Структура сценария
|
453
453
|
examples: Значения
|
454
|
-
given: "
|
455
|
-
when: "
|
456
|
-
then: "
|
454
|
+
given: "*|Допустим|Дано|Пусть"
|
455
|
+
when: "*|Если|Когда"
|
456
|
+
then: "*|То|Тогда"
|
457
457
|
and: "*|И|К тому же"
|
458
458
|
but: "*|Но|А"
|
459
459
|
"sv":
|
@@ -529,10 +529,10 @@
|
|
529
529
|
scenario: Сценарій
|
530
530
|
scenario_outline: Структура сценарію
|
531
531
|
examples: Приклади
|
532
|
-
given: "*|Припустимо|Припустимо,
|
533
|
-
when: "
|
534
|
-
then: "
|
535
|
-
and: "
|
532
|
+
given: "*|Припустимо|Припустимо, що|Нехай|Дано"
|
533
|
+
when: "*|Якщо|Коли"
|
534
|
+
then: "*|То|Тоді"
|
535
|
+
and: "*|І|А також|Та"
|
536
536
|
but: "*|Але"
|
537
537
|
"uz":
|
538
538
|
name: Uzbek
|
data/lib/gherkin/i18n_lexer.rb
CHANGED