gherkin 1.0.30 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.rspec +1 -0
  2. data/History.txt +19 -0
  3. data/Rakefile +4 -4
  4. data/VERSION.yml +2 -2
  5. data/features/feature_parser.feature +11 -0
  6. data/features/json_formatter.feature +238 -0
  7. data/features/pretty_formatter.feature +9 -0
  8. data/features/step_definitions/gherkin_steps.rb +1 -1
  9. data/features/step_definitions/json_formatter_steps.rb +32 -0
  10. data/features/step_definitions/pretty_formatter_steps.rb +24 -23
  11. data/features/support/env.rb +3 -3
  12. data/lib/gherkin/formatter/json_formatter.rb +82 -0
  13. data/lib/gherkin/formatter/pretty_formatter.rb +73 -78
  14. data/lib/gherkin/i18n.rb +22 -18
  15. data/lib/gherkin/i18n.yml +9 -9
  16. data/lib/gherkin/i18n_lexer.rb +2 -2
  17. data/lib/gherkin/parser/event.rb +6 -6
  18. data/lib/gherkin/parser/filter_listener.rb +5 -1
  19. data/lib/gherkin/parser/formatter_listener.rb +113 -0
  20. data/lib/gherkin/parser/json_parser.rb +102 -0
  21. data/lib/gherkin/parser/parser.rb +10 -2
  22. data/lib/gherkin/parser/row.rb +15 -0
  23. data/lib/gherkin/rubify.rb +2 -0
  24. data/lib/gherkin/tools/files.rb +1 -1
  25. data/lib/gherkin/tools/reformat.rb +1 -2
  26. data/lib/gherkin/tools/stats.rb +1 -1
  27. data/lib/gherkin/tools/stats_listener.rb +5 -5
  28. data/ragel/lexer.c.rl.erb +41 -12
  29. data/ragel/lexer.java.rl.erb +26 -17
  30. data/ragel/lexer.rb.rl.erb +10 -5
  31. data/ragel/lexer_common.rl.erb +6 -6
  32. data/spec/gherkin/c_lexer_spec.rb +2 -2
  33. data/spec/gherkin/fixtures/complex.js +105 -0
  34. data/spec/gherkin/formatter/argument_spec.rb +3 -3
  35. data/spec/gherkin/formatter/colors_spec.rb +3 -4
  36. data/spec/gherkin/formatter/pretty_formatter_spec.rb +21 -50
  37. data/spec/gherkin/i18n_lexer_spec.rb +6 -6
  38. data/spec/gherkin/i18n_spec.rb +16 -9
  39. data/spec/gherkin/java_lexer_spec.rb +1 -2
  40. data/spec/gherkin/output_stream_string_io.rb +24 -0
  41. data/spec/gherkin/parser/filter_listener_spec.rb +16 -9
  42. data/spec/gherkin/parser/formatter_listener_spec.rb +134 -0
  43. data/spec/gherkin/parser/json_parser_spec.rb +129 -0
  44. data/spec/gherkin/parser/parser_spec.rb +9 -9
  45. data/spec/gherkin/parser/tag_expression_spec.rb +8 -8
  46. data/spec/gherkin/rb_lexer_spec.rb +1 -1
  47. data/spec/gherkin/sexp_recorder.rb +21 -1
  48. data/spec/gherkin/shared/{lexer_spec.rb → lexer_group.rb} +172 -102
  49. data/spec/gherkin/shared/{py_string_spec.rb → py_string_group.rb} +21 -17
  50. data/spec/gherkin/shared/{row_spec.rb → row_group.rb} +36 -19
  51. data/spec/gherkin/shared/{tags_spec.rb → tags_group.rb} +13 -9
  52. data/spec/spec_helper.rb +18 -38
  53. data/tasks/bench.rake +3 -3
  54. data/tasks/compile.rake +13 -14
  55. data/tasks/rspec.rake +6 -11
  56. metadata +42 -28
  57. data/features/pretty_printer.feature +0 -14
  58. 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=false)
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 tag(name, line)
25
- @tags ||= []
26
- @tags << name
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 comment(content, line)
30
- @comments ||= []
31
- @comments << content
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 feature(keyword, name, line)
35
- @io.puts "#{grab_comments!('')}#{grab_tags!('')}#{keyword}: #{indent(name, ' ')}"
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 background(keyword, name, line)
39
- @io.puts "\n#{grab_comments!(' ')} #{keyword}: #{indent(name, ' ')}"
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 scenario(keyword, name, line, location=nil)
43
- flush_table
44
- @io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}#{indented_scenario_location!(keyword, name, location)}"
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 scenario_outline(keyword, name, line)
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}#{indent(name, ' ')}"
62
+ step = "#{keyword}#{name}"
64
63
  step = self.__send__(status, step, @monochrome) if status
65
64
 
66
- @io.puts("#{grab_comments!(' ')} #{step}#{indented_step_location!(location)}")
67
- end
68
-
69
- def row(row, line)
70
- @rows ||= []
71
- @rows << row.map{|cell| escape_cell(cell)}
72
- end
73
-
74
- def py_string(string, line)
75
- @io.puts " \"\"\"\n" + string.gsub(START, ' ').gsub(/"""/,'\"\"\"') + "\n \"\"\""
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 exception(exception)
94
- exception_text = "#{exception.message} (#{exception.class})\n#{(exception.backtrace || []).join("\n")}".gsub(/^/, ' ')
95
- @io.puts(failed(exception_text, @monochrome))
96
- end
97
-
98
- def flush_table(exception=nil, statuses=nil)
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
- @rows.each_with_index do |row, i|
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, statuses, j) + ' ' * (max_length - cell_lengths[i][j])
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
- private
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
- NL = Regexp.new("\n".encode('UTF-8'))
130
+ CRLF = Regexp.new("\r\n".encode('UTF-8'))
127
131
  else
128
132
  START = /^/
129
- NL = /\n/n
133
+ CRLF = /\r\n/n
130
134
  end
131
135
 
132
136
  def indent(string, indentation)
133
- indent = ""
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 grab_tags!(indent)
142
- tags = @tags ? indent + @tags.join(' ') + "\n" : ''
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 grab_comments!(indent)
148
- comments = @comments ? indent + @comments.join("\n#{indent}") + "\n" : ''
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 indented_scenario_location!(keyword, name, location)
154
- return "" if location.nil?
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("# #{location}", @monochrome)
153
+ ' ' * indent + ' ' + comments("# #{@uri}:#{line}", @monochrome)
159
154
  end
160
155
 
161
156
  def indented_step_location!(location)
162
- return "" if location.nil?
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
@@ -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.each{|i18n| pf.row([i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], 0)}
61
- pf.flush_table
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.rewind
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(iso_code)
140
- iso_code = iso_code.to_s
141
- raise "No #{iso_code.inspect} in #{@keywords.inspect}" if @keywords[iso_code].nil?
142
- @keywords[iso_code].split('|').map{|keyword| real_keyword(iso_code, keyword)}
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.each do |key|
152
- pf.row([key, keywords(key).map{|keyword| %{"#{keyword}"}}.join(', ')], 0)
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
- STEP_KEYWORD_KEYS.each do |key|
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
- pf.row(["#{key} (code)", code_keywords], 0)
162
+ Parser::Row.new(["#{key} (code)", code_keywords], [], nil)
159
163
  end
160
164
 
161
- pf.flush_table
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(iso_code, keyword)
169
- if(STEP_KEYWORD_KEYS.index(iso_code))
172
+ def real_keyword(key, keyword)
173
+ if(STEP_KEYWORD_KEYS.index(key))
170
174
  (keyword + ' ').sub(/< $/, '')
171
175
  else
172
176
  keyword
@@ -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: "*|Ha"
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
@@ -17,8 +17,8 @@ module Gherkin
17
17
  @force_ruby = force_ruby
18
18
  end
19
19
 
20
- def scan(source)
21
- create_delegate(source).scan(source)
20
+ def scan(source, uri, offset)
21
+ create_delegate(source).scan(source, uri, offset)
22
22
  end
23
23
 
24
24
  private