gherkin 2.5.4 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,15 +11,18 @@ class Hash
11
11
  end
12
12
 
13
13
  Given /^a JSON formatter$/ do
14
- @io = StringIO.new
15
- @formatter = Gherkin::Formatter::JSONFormatter.new(@io)
14
+ @out = StringIO.new
15
+ @formatter = Gherkin::Formatter::JSONFormatter.new(@out)
16
16
  end
17
17
 
18
18
  Then /^the outputted JSON should be:$/ do |expected_json|
19
19
  require 'json'
20
- puts JSON.pretty_generate(JSON.parse(@io.string))
20
+ @formatter.close
21
+ actual_json = @out.string
22
+ puts actual_json
23
+ puts JSON.pretty_generate(JSON.parse(actual_json))
21
24
  expected = JSON.parse(expected_json)
22
- actual = JSON.parse(@io.string)
25
+ actual = JSON.parse(actual_json)
23
26
  actual.should == expected
24
27
  end
25
28
 
@@ -12,7 +12,7 @@ Given /^a JSON lexer$/ do
12
12
  end
13
13
 
14
14
  Given /^the following JSON is parsed:$/ do |text|
15
- @json_parser.parse(JSON.pretty_generate(JSON.parse(text)), "unknown.json", 0)
15
+ @json_parser.parse(JSON.pretty_generate(JSON.parse(text)))
16
16
  end
17
17
 
18
18
  Then /^the outputted text should be:$/ do |expected_text|
@@ -20,11 +20,12 @@ module PrettyPlease
20
20
  json_formatter = Gherkin::Formatter::JSONFormatter.new(json)
21
21
  gherkin_parser = Gherkin::Parser::Parser.new(json_formatter, true)
22
22
  parse(gherkin_parser, gherkin, feature_path)
23
+ json_formatter.close
23
24
 
24
25
  io = StringIO.new
25
26
  pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
26
27
  json_parser = Gherkin::JSONParser.new(pretty_formatter, pretty_formatter)
27
- json_parser.parse(json.string, "#{feature_path}.json", 0)
28
+ json_parser.parse(json.string)
28
29
 
29
30
  io.string
30
31
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "gherkin"
5
- s.version = "2.5.4"
5
+ # If bumping the major version here, comment out the cucumber dependency below and uncomment in the Gemfile to build against a local cucumber.
6
+ s.version = "2.6.0"
6
7
  s.authors = ["Mike Sassak", "Gregory Hnatiuk", "Aslak Hellesøy"]
7
8
  s.description = "A fast Gherkin lexer/parser based on the Ragel State Machine Compiler."
8
9
  s.summary = "#{s.name}-#{s.version}"
@@ -45,10 +46,10 @@ Gem::Specification.new do |s|
45
46
 
46
47
  s.add_runtime_dependency('json', '>= 1.4.6')
47
48
 
48
- s.add_development_dependency('cucumber', '>= 1.1.0')
49
+ s.add_development_dependency('cucumber', '>= 1.1.1')
49
50
  s.add_development_dependency('rake', '>= 0.9.2')
50
51
  s.add_development_dependency('bundler', '>= 1.0.21')
51
- s.add_development_dependency('rspec', '>= 2.6.0')
52
+ s.add_development_dependency('rspec', '>= 2.7.0')
52
53
 
53
54
  unless ENV['RUBY_CC_VERSION'] || defined?(JRUBY_VERSION)
54
55
  s.add_development_dependency('therubyracer', '>= 0.9.8') if ENV['GHERKIN_JS']
@@ -5,26 +5,31 @@ require 'base64'
5
5
 
6
6
  module Gherkin
7
7
  module Formatter
8
+ # This class doesn't really generate JSON - instead it populates an Array that can easily
9
+ # be turned into JSON.
8
10
  class JSONFormatter
9
11
  native_impl('gherkin')
10
12
 
11
13
  include Base64
12
- attr_reader :gherkin_object
13
14
 
14
- # Creates a new instance that writes the resulting JSON to +io+.
15
- # If +io+ is nil, the JSON will not be written, but instead a Ruby
16
- # object can be retrieved with #gherkin_object
17
15
  def initialize(io)
16
+ raise "Must be writeable" unless io.respond_to?(:write)
18
17
  @io = io
18
+ @feature_hashes = []
19
+ end
20
+
21
+ def close
22
+ @io.write(@feature_hashes.to_json)
19
23
  end
20
24
 
21
25
  def uri(uri)
22
- # We're ignoring the uri - we don't want it as part of the JSON
23
- # (The pretty formatter uses it just for visual niceness - comments)
26
+ @uri = uri
24
27
  end
25
28
 
26
29
  def feature(feature)
27
- @gherkin_object = feature.to_hash
30
+ @feature_hash = feature.to_hash
31
+ @feature_hash['uri'] = @uri
32
+ @feature_hashes << @feature_hash
28
33
  end
29
34
 
30
35
  def background(background)
@@ -68,13 +73,12 @@ module Gherkin
68
73
  end
69
74
 
70
75
  def eof
71
- @io.write(@gherkin_object.to_json) if @io
72
76
  end
73
77
 
74
78
  private
75
79
 
76
80
  def feature_elements
77
- @gherkin_object['elements'] ||= []
81
+ @feature_hash['elements'] ||= []
78
82
  end
79
83
 
80
84
  def feature_element
@@ -31,11 +31,12 @@ module Gherkin
31
31
  end
32
32
 
33
33
  class TagStatement < DescribedStatement
34
- attr_reader :tags
34
+ attr_reader :tags, :id
35
35
 
36
- def initialize(comments, tags, keyword, name, description, line)
36
+ def initialize(comments, tags, keyword, name, description, line, id)
37
37
  super(comments, keyword, name, description, line)
38
38
  @tags = tags
39
+ @id = id
39
40
  end
40
41
 
41
42
  def first_non_comment_line
@@ -46,10 +47,6 @@ module Gherkin
46
47
  class Feature < TagStatement
47
48
  native_impl('gherkin')
48
49
 
49
- def initialize(comments, tags, keyword, name, description, line)
50
- super(comments, tags, keyword, name, description, line)
51
- end
52
-
53
50
  def replay(formatter)
54
51
  formatter.feature(self)
55
52
  end
@@ -71,8 +68,8 @@ module Gherkin
71
68
  class Scenario < TagStatement
72
69
  native_impl('gherkin')
73
70
 
74
- def initialize(comments, tags, keyword, name, description, line)
75
- super(comments, tags, keyword, name, description, line)
71
+ def initialize(comments, tags, keyword, name, description, line, id)
72
+ super(comments, tags, keyword, name, description, line, id)
76
73
  @type = "scenario"
77
74
  end
78
75
 
@@ -84,8 +81,8 @@ module Gherkin
84
81
  class ScenarioOutline < TagStatement
85
82
  native_impl('gherkin')
86
83
 
87
- def initialize(comments, tags, keyword, name, description, line)
88
- super(comments, tags, keyword, name, description, line)
84
+ def initialize(comments, tags, keyword, name, description, line, id)
85
+ super(comments, tags, keyword, name, description, line, id)
89
86
  @type = "scenario_outline"
90
87
  end
91
88
 
@@ -97,23 +94,47 @@ module Gherkin
97
94
  class Examples < TagStatement
98
95
  native_impl('gherkin')
99
96
 
100
- attr_accessor :rows
97
+ attr_accessor :rows # needs to remain mutable for filters
101
98
 
102
- def initialize(comments, tags, keyword, name, description, line, rows)
103
- super(comments, tags, keyword, name, description, line)
99
+ def initialize(comments, tags, keyword, name, description, line, id, rows)
100
+ super(comments, tags, keyword, name, description, line, id)
104
101
  @rows = rows
105
102
  end
106
103
 
107
104
  def replay(formatter)
108
105
  formatter.examples(self)
109
106
  end
107
+
108
+ class Builder
109
+ def initialize(*args)
110
+ @args = *args
111
+ @rows = nil
112
+ end
113
+
114
+ def row(comments, cells, line, id)
115
+ @rows ||= []
116
+ @rows << ExamplesTableRow.new(comments, cells, line, id)
117
+ end
118
+
119
+ def replay(formatter)
120
+ build.replay(formatter)
121
+ end
122
+
123
+ def build
124
+ Examples.new(*(@args << @rows))
125
+ end
126
+ end
110
127
  end
111
128
 
112
129
  class Step < BasicStatement
113
130
  native_impl('gherkin')
114
131
 
115
- attr_accessor :rows
116
- attr_accessor :doc_string
132
+ attr_reader :rows, :doc_string
133
+
134
+ def initialize(comments, keyword, name, line, rows, doc_string)
135
+ super(comments, keyword, name, line)
136
+ @rows, @doc_string = rows, doc_string
137
+ end
117
138
 
118
139
  def line_range
119
140
  range = super
@@ -136,6 +157,31 @@ module Gherkin
136
157
  Argument.new(offset, val)
137
158
  end
138
159
  end
160
+
161
+ class Builder
162
+ def initialize(*args)
163
+ @args = *args
164
+ @rows = nil
165
+ @doc_string = nil
166
+ end
167
+
168
+ def row(comments, cells, line, id)
169
+ @rows ||= []
170
+ @rows << DataTableRow.new(comments, cells, line)
171
+ end
172
+
173
+ def doc_string(string, content_type, line)
174
+ @doc_string = Formatter::Model::DocString.new(string, content_type, line)
175
+ end
176
+
177
+ def replay(formatter)
178
+ build.replay(formatter)
179
+ end
180
+
181
+ def build
182
+ Step.new(*(@args << @rows << @doc_string))
183
+ end
184
+ end
139
185
  end
140
186
 
141
187
  class Comment < Hashable
@@ -169,10 +215,10 @@ module Gherkin
169
215
  class DocString < Hashable
170
216
  native_impl('gherkin')
171
217
 
172
- attr_reader :content_type, :value, :line
218
+ attr_reader :value, :content_type, :line
173
219
 
174
- def initialize(content_type, value, line)
175
- @content_type, @value, @line = content_type, value, line
220
+ def initialize(value, content_type, line)
221
+ @value, @content_type, @line = value, content_type, line
176
222
  end
177
223
 
178
224
  def line_range
@@ -182,8 +228,6 @@ module Gherkin
182
228
  end
183
229
 
184
230
  class Row < Hashable
185
- native_impl('gherkin')
186
-
187
231
  attr_reader :comments, :cells, :line
188
232
 
189
233
  def initialize(comments, cells, line)
@@ -191,6 +235,20 @@ module Gherkin
191
235
  end
192
236
  end
193
237
 
238
+ class DataTableRow < Row
239
+ native_impl('gherkin')
240
+ end
241
+
242
+ class ExamplesTableRow < Row
243
+ native_impl('gherkin')
244
+ attr_reader :id
245
+
246
+ def initialize(comments, cells, line, id)
247
+ super(comments, cells, line)
248
+ @id = id
249
+ end
250
+ end
251
+
194
252
  class Match < Hashable
195
253
  native_impl('gherkin')
196
254
 
@@ -177,7 +177,7 @@ module Gherkin
177
177
 
178
178
  private
179
179
 
180
- def doc_string(doc_string)
180
+ def doc_string(doc_string)
181
181
  @io.puts " \"\"\"" + doc_string.content_type + "\n" + escape_triple_quotes(indent(doc_string.value, ' ')) + "\n \"\"\""
182
182
  end
183
183
 
@@ -1,12 +1,11 @@
1
1
  # encoding: UTF-8
2
2
  #
3
- # We use ISO 639-1 (language) and ISO 3166 alpha-2 (region - if appliccable):
3
+ # We use ISO 639-1 (language) and ISO 3166 alpha-2 (region - if applicable):
4
4
  # http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
5
5
  # http://en.wikipedia.org/wiki/ISO_3166-1
6
6
  #
7
7
  # If you want several aliases for a keyword, just separate them
8
- # with a | character. Make sure there are no ambiguities in the
9
- # keywords.
8
+ # with a | character. The * is a step keyword alias for all translations.
10
9
  #
11
10
  # If you do *not* want a trailing space after a keyword, end it with a < character.
12
11
  # (See Chinese for examples).
@@ -16,21 +16,23 @@ module Gherkin
16
16
 
17
17
  # Parse a gherkin object +o+, which can either be a JSON String,
18
18
  # or a Hash (from a parsed JSON String).
19
- def parse(o, feature_uri='unknown.json', line_offset=0)
19
+ def parse(o)
20
20
  o = JSON.parse(o) if String === o
21
- @formatter.uri(feature_uri)
22
-
23
- Formatter::Model::Feature.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o)).replay(@formatter)
24
- (o["elements"] || []).each do |feature_element|
25
- feature_element(feature_element).replay(@formatter)
26
- (feature_element["steps"] || []).each do |step|
27
- step(step).replay(@formatter)
28
- match(step)
29
- result(step)
30
- embeddings(step)
31
- end
32
- (feature_element["examples"] || []).each do |eo|
33
- Formatter::Model::Examples.new(comments(eo), tags(eo), keyword(eo), name(eo), description(eo), line(eo), rows(eo['rows'])).replay(@formatter)
21
+
22
+ o.each do |f|
23
+ @formatter.uri(f['uri'])
24
+ Formatter::Model::Feature.new(comments(f), tags(f), keyword(f), name(f), description(f), line(f), id(f)).replay(@formatter)
25
+ (f["elements"] || []).each do |feature_element|
26
+ feature_element(feature_element).replay(@formatter)
27
+ (feature_element["steps"] || []).each do |step|
28
+ step(step).replay(@formatter)
29
+ match(step)
30
+ result(step)
31
+ embeddings(step)
32
+ end
33
+ (feature_element["examples"] || []).each do |eo|
34
+ Formatter::Model::Examples.new(comments(eo), tags(eo), keyword(eo), name(eo), description(eo), line(eo), id(eo), examples_rows(eo['rows'])).replay(@formatter)
35
+ end
34
36
  end
35
37
  end
36
38
 
@@ -44,23 +46,25 @@ module Gherkin
44
46
  when 'background'
45
47
  Formatter::Model::Background.new(comments(o), keyword(o), name(o), description(o), line(o))
46
48
  when 'scenario'
47
- Formatter::Model::Scenario.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o))
49
+ Formatter::Model::Scenario.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o), id(o))
48
50
  when 'scenario_outline'
49
- Formatter::Model::ScenarioOutline.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o))
51
+ Formatter::Model::ScenarioOutline.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o), id(o))
50
52
  end
51
53
  end
52
54
 
53
55
  def step(o)
54
- step = Formatter::Model::Step.new(comments(o), keyword(o), name(o), line(o))
56
+ builder = Formatter::Model::Step::Builder.new(comments(o), keyword(o), name(o), line(o))
57
+
58
+ (o['rows'] || []).each do |row|
59
+ builder.row comments(row), row['cells'], row['line'], nil
60
+ end
55
61
 
56
- if(o['rows'])
57
- step.rows = rows(o['rows'])
58
- elsif(o['doc_string'])
62
+ if(o['doc_string'])
59
63
  ds = o['doc_string']
60
- step.doc_string = Formatter::Model::DocString.new(ds['content_type'].to_s, ds['value'], ds['line'])
64
+ builder.doc_string ds['value'], ds['content_type'].to_s, ds['line']
61
65
  end
62
66
 
63
- step
67
+ builder.build
64
68
  end
65
69
 
66
70
  def match(o)
@@ -81,8 +85,8 @@ module Gherkin
81
85
  end
82
86
  end
83
87
 
84
- def rows(o)
85
- o.map{|row| Formatter::Model::Row.new(comments(row), row['cells'], row['line'])}
88
+ def examples_rows(o)
89
+ o.map{|row| Formatter::Model::ExamplesTableRow.new(comments(row), row['cells'], row['line'], row['id'])}
86
90
  end
87
91
 
88
92
  def comments(o)
@@ -113,6 +117,10 @@ module Gherkin
113
117
  o['line']
114
118
  end
115
119
 
120
+ def id(o)
121
+ o['id']
122
+ end
123
+
116
124
  def arguments(m)
117
125
  m['arguments'].map{|a| Formatter::Argument.new(a['offset'], a['val'])}
118
126
  end
@@ -11,54 +11,65 @@ module Gherkin
11
11
 
12
12
  def initialize(formatter)
13
13
  @formatter = formatter
14
- @comments = []
15
- @tags = []
16
- @table = nil
14
+ @stash = Stash.new
17
15
  end
18
16
 
19
17
  def comment(value, line)
20
- @comments << Formatter::Model::Comment.new(value, line)
18
+ @stash.comment Formatter::Model::Comment.new(value, line)
21
19
  end
22
20
 
23
21
  def tag(name, line)
24
- @tags << Formatter::Model::Tag.new(name, line)
22
+ @stash.tag Formatter::Model::Tag.new(name, line)
25
23
  end
26
24
 
27
25
  def feature(keyword, name, description, line)
28
- @formatter.feature(Formatter::Model::Feature.new(grab_comments!, grab_tags!, keyword, name, description, line))
26
+ @stash.feature(name) do |comments, tags, id|
27
+ replay Formatter::Model::Feature.new(comments, tags, keyword, name, description, line, id)
28
+ end
29
29
  end
30
30
 
31
31
  def background(keyword, name, description, line)
32
- @formatter.background(Formatter::Model::Background.new(grab_comments!, keyword, name, description, line))
32
+ @stash.feature_element(name) do |comments, tags, id|
33
+ replay Formatter::Model::Background.new(comments, keyword, name, description, line)
34
+ end
33
35
  end
34
36
 
35
37
  def scenario(keyword, name, description, line)
36
38
  replay_step_or_examples
37
- @formatter.scenario(Formatter::Model::Scenario.new(grab_comments!, grab_tags!, keyword, name, description, line))
39
+ @stash.feature_element(name) do |comments, tags, id|
40
+ replay Formatter::Model::Scenario.new(comments, tags, keyword, name, description, line, id)
41
+ end
38
42
  end
39
43
 
40
44
  def scenario_outline(keyword, name, description, line)
41
45
  replay_step_or_examples
42
- @formatter.scenario_outline(Formatter::Model::ScenarioOutline.new(grab_comments!, grab_tags!, keyword, name, description, line))
46
+ @stash.feature_element(name) do |comments, tags, id|
47
+ replay Formatter::Model::ScenarioOutline.new(comments, tags, keyword, name, description, line, id)
48
+ end
43
49
  end
44
50
 
45
51
  def examples(keyword, name, description, line)
46
52
  replay_step_or_examples
47
- @examples_statement = Formatter::Model::Examples.new(grab_comments!, grab_tags!, keyword, name, description, line, nil)
53
+ @stash.examples(name) do |comments, tags, id|
54
+ @current_builder = Formatter::Model::Examples::Builder.new(comments, tags, keyword, name, description, line, id)
55
+ end
48
56
  end
49
57
 
50
58
  def step(keyword, name, line)
51
59
  replay_step_or_examples
52
- @step_statement = Formatter::Model::Step.new(grab_comments!, keyword, name, line)
60
+ @stash.basic_statement do |comments|
61
+ @current_builder = Formatter::Model::Step::Builder.new(comments, keyword, name, line)
62
+ end
53
63
  end
54
64
 
55
65
  def row(cells, line)
56
- @table ||= []
57
- @table << Formatter::Model::Row.new(grab_comments!, cells, line)
66
+ @stash.basic_statement do |comments, id|
67
+ @current_builder.row(comments, cells, line, id)
68
+ end
58
69
  end
59
70
 
60
- def doc_string(string, content_type, line)
61
- @doc_string = Formatter::Model::DocString.new(string, content_type, line)
71
+ def doc_string(content_type, value, line)
72
+ @current_builder.doc_string(value, content_type, line)
62
73
  end
63
74
 
64
75
  def eof
@@ -71,46 +82,61 @@ module Gherkin
71
82
  end
72
83
 
73
84
  private
85
+
86
+ def replay(element)
87
+ element.replay(@formatter)
88
+ end
89
+
90
+ class Stash
91
+ attr_reader :comments, :tags, :ids
92
+
93
+ def initialize
94
+ @comments, @tags, @ids = [], [], []
95
+ @row_index = 0
96
+ end
97
+
98
+ def comment(comment)
99
+ @comments << comment
100
+ end
101
+
102
+ def feature(name)
103
+ @feature_id = id(name)
104
+ yield @comments, @tags, @feature_id
105
+ @comments, @tags = [], []
106
+ end
74
107
 
75
- def grab_comments!
76
- comments = @comments
77
- @comments = []
78
- comments
79
- end
80
-
81
- def grab_tags!
82
- tags = @tags
83
- @tags = []
84
- tags
85
- end
86
-
87
- def grab_rows!
88
- table = @table
89
- @table = nil
90
- table
91
- end
108
+ def feature_element(name)
109
+ @feature_element_id = "#{@feature_id};#{id(name)}"
110
+ yield @comments, @tags, @feature_element_id
111
+ @comments, @tags = [], []
112
+ end
113
+
114
+ def examples(name)
115
+ @examples_id = "#{@feature_element_id};#{id(name)}"
116
+ @row_index = 0
117
+ yield @comments, @tags, @examples_id
118
+ @comments, @tags = [], []
119
+ end
120
+
121
+ def basic_statement
122
+ @row_index += 1
123
+ yield @comments, "#{@examples_id};#{@row_index}"
124
+ @comments = []
125
+ end
126
+
127
+ def tag(tag)
128
+ @tags << tag
129
+ end
92
130
 
93
- def grab_doc_string!
94
- doc_string = @doc_string
95
- @doc_string = nil
96
- doc_string
131
+ def id(name)
132
+ (name || '').gsub(/[\s_]/, '-').downcase
133
+ end
97
134
  end
98
135
 
99
136
  def replay_step_or_examples
100
- if(@step_statement)
101
- if(doc_string = grab_doc_string!)
102
- @step_statement.doc_string = doc_string
103
- elsif(rows = grab_rows!)
104
- @step_statement.rows = rows
105
- end
106
- @formatter.step(@step_statement)
107
- @step_statement = nil
108
- end
109
- if(@examples_statement)
110
- @examples_statement.rows = grab_rows!
111
- @formatter.examples(@examples_statement)
112
- @examples_statement = nil
113
- end
137
+ return unless @current_builder
138
+ replay(@current_builder)
139
+ @current_builder = nil
114
140
  end
115
141
  end
116
142
  end