cucumber-core 1.2.0 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53a124c933b4697ed6128b46c307617ea4399559
4
- data.tar.gz: e80a381d06f95b5416035f91a004d00cbd68057e
3
+ metadata.gz: 0f56999cf96221b9de93c823a5559efeb56bfb34
4
+ data.tar.gz: 1e955adc2d60f414a53e6775d0e6adc44b4eafa5
5
5
  SHA512:
6
- metadata.gz: 0b1e3a4359e8ccaeef234bfa1492e678145bf1f07320ffbf1e01814b2efd405da9ae10abb31347ac10642e49935183032b1f7a094631219c48d7a4b984c5e6cf
7
- data.tar.gz: e7d0c05ae5e57937e6858c91485157175e69d32332457db403a69d1e8b44e9a99bf95cc68e2bdb1b265ad5decaa69086fbca3a0d48e6cf97ca6cf64b4b54e855
6
+ metadata.gz: 529edb34199a3bea271b93d0f09de3e4b2c24dc7f4c198ff553fe029b7d0b5c1a7a4a18d744586725351d5e1f9795c6caec1e8e35b64e2c77eab858ad3d8052e
7
+ data.tar.gz: 42aa0c463801d3aaba42d95c2bbdd0519a8d5344da0ef6c594d7a6bd704329947c5265aea47e3eee601e20c043fac8ca31b25afc5e75a9d1630b85ead562bd80
data/HISTORY.md CHANGED
@@ -1,6 +1,14 @@
1
1
  ## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v1.2.0...master)
2
2
 
3
- ...
3
+ ### New Features
4
+
5
+ * Add factory method to Cucumber::Core::Ast::Location that uses the output from Proc#source_location (@brasmusson)
6
+ * Integrate Gherkin3 parser (@brassmusson)
7
+
8
+ ### Bugfixes
9
+
10
+ * Make sure that `after_test_step` is sent also when a test step is interrupted by (a timeout in) an around hook ([cucumber/cucumber-ruby#909](https://github.com/cucumber/cucumber-ruby/issues/909) @brasmusson)
11
+ * Improve the check that a test_step quacks like a Cucumber::Core::Test::Step ([95](https://github.com/cucumber/cucumber-ruby-core/issues/95) @brasmusson)
4
12
 
5
13
  ## [v1.2.0](https://github.com/cucumber/cucumber-ruby-core/compare/v1.1.3...v1.2.0)
6
14
 
@@ -68,7 +76,7 @@
68
76
 
69
77
  ### Bugfixes
70
78
 
71
- * Handle empty feature files (#[77](https://github.com/cucumber/cucumber-ruby-core/pull/77), [cucumber/cucumber#771](https://github.com/cucumber/cucumber/issues/771) [@brasmusson](https://github.com/brasmusson))
79
+ * Handle empty feature files (#[77](https://github.com/cucumber/cucumber-ruby-core/pull/77), [cucumber/cucumber-ruby#771](https://github.com/cucumber/cucumber-ruby/issues/771) [@brasmusson](https://github.com/brasmusson))
72
80
  * Run after hooks in reverse order (#[69](https://github.com/cucumber/cucumber-ruby-core/pull/69) [@erran](https://github.com/erran))
73
81
 
74
82
  ## [v1.0.0.beta.3](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.2...v1.0.0.beta.3)
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.license = "MIT"
15
15
  s.required_ruby_version = ">= 1.9.3"
16
16
 
17
- s.add_dependency 'gherkin', '~> 2.12.0'
17
+ s.add_dependency 'gherkin3', '~> 3.1.0'
18
18
 
19
19
  s.add_development_dependency 'bundler', '>= 1.3.5'
20
20
  s.add_development_dependency 'rake', '>= 0.9.2'
@@ -10,9 +10,7 @@ module Cucumber
10
10
  include HasLocation
11
11
  include DescribesItself
12
12
 
13
- def initialize(gherkin_statement, language, location, comments, keyword, name, description, raw_steps)
14
- @gherkin_statement = gherkin_statement
15
- @language = language
13
+ def initialize(location, comments, keyword, name, description, raw_steps)
16
14
  @location = location
17
15
  @comments = comments
18
16
  @keyword = keyword
@@ -21,11 +19,10 @@ module Cucumber
21
19
  @raw_steps = raw_steps
22
20
  end
23
21
 
24
- attr_reader :language, :description, :raw_steps
25
- private :language, :raw_steps
22
+ attr_reader :description, :raw_steps
23
+ private :raw_steps
26
24
 
27
25
  attr_reader :comments, :keyword, :location
28
- attr_reader :gherkin_statement
29
26
 
30
27
  def children
31
28
  raw_steps
@@ -1,6 +1,3 @@
1
- require 'gherkin/rubify'
2
- require 'gherkin/lexer/i18n_lexer'
3
- require 'gherkin/formatter/escaping'
4
1
  require 'cucumber/core/ast/describes_itself'
5
2
  require 'cucumber/core/ast/location'
6
3
 
@@ -30,15 +27,13 @@ module Cucumber
30
27
  include DescribesItself
31
28
  include HasLocation
32
29
 
33
- include ::Gherkin::Rubify
34
-
35
30
  # Creates a new instance. +raw+ should be an Array of Array of String
36
31
  # or an Array of Hash
37
32
  # You don't typically create your own DataTable objects - Cucumber will do
38
33
  # it internally and pass them to your Step Definitions.
39
34
  #
40
- def initialize(raw, location)
41
- raw = ensure_array_of_array(rubify(raw))
35
+ def initialize(rows, location)
36
+ raw = ensure_array_of_array(rows)
42
37
  verify_rows_are_same_length(raw)
43
38
  @raw = raw.freeze
44
39
  @location = location
@@ -5,7 +5,11 @@ module Cucumber
5
5
  def describe_to(visitor, *args)
6
6
  visitor.send(description_for_visitors, self, *args) do |child_visitor|
7
7
  children.each do |child|
8
- child.describe_to(child_visitor, *args)
8
+ begin
9
+ child.describe_to(child_visitor, *args)
10
+ rescue => e
11
+ raise e.class, "Failed describing child of #{self.inspect} - #{e.message}", e.backtrace
12
+ end
9
13
  end
10
14
  end
11
15
  self
@@ -25,8 +25,8 @@ module Cucumber
25
25
 
26
26
  attr_reader :content_type, :content
27
27
 
28
- def initialize(string, content_type, location)
29
- @content = string
28
+ def initialize(content, content_type, location)
29
+ @content = content
30
30
  @content_type = content_type
31
31
  @location = location
32
32
  super @content
@@ -11,8 +11,7 @@ module Cucumber
11
11
  include HasLocation
12
12
  include DescribesItself
13
13
 
14
- def initialize(gherkin_statement, location, comments, tags, keyword, name, description, header, example_rows)
15
- @gherkin_statement = gherkin_statement
14
+ def initialize(location, comments, tags, keyword, name, description, header, example_rows)
16
15
  @location = location
17
16
  @comments = comments
18
17
  @tags = tags
@@ -23,7 +22,7 @@ module Cucumber
23
22
  @example_rows = example_rows
24
23
  end
25
24
 
26
- attr_reader :gherkin_statement, :location, :comments, :tags, :keyword,
25
+ attr_reader :location, :tags, :keyword, :comments,
27
26
  :header, :example_rows
28
27
 
29
28
  private
@@ -109,6 +108,7 @@ module Cucumber
109
108
  end
110
109
  end
111
110
  end
111
+ class Examples < ExamplesTable; end
112
112
  end
113
113
  end
114
114
  end
@@ -1,6 +1,7 @@
1
1
  require 'cucumber/core/ast/describes_itself'
2
2
  require 'cucumber/core/ast/names'
3
3
  require 'cucumber/core/ast/location'
4
+ require 'gherkin3/dialect'
4
5
 
5
6
  module Cucumber
6
7
  module Core
@@ -11,12 +12,11 @@ module Cucumber
11
12
  include HasLocation
12
13
  include DescribesItself
13
14
 
14
- attr_reader :gherkin_statement, :language, :location, :background,
15
+ attr_reader :language, :location, :background,
15
16
  :comments, :tags, :keyword, :description,
16
17
  :feature_elements
17
18
 
18
- def initialize(gherkin_statement, language, location, background, comments, tags, keyword, name, description, feature_elements)
19
- @gherkin_statement = gherkin_statement
19
+ def initialize(language, location, background, comments, tags, keyword, name, description, scenario_definitions)
20
20
  @language = language
21
21
  @location = location
22
22
  @background = background
@@ -25,7 +25,7 @@ module Cucumber
25
25
  @keyword = keyword
26
26
  @name = name
27
27
  @description = description
28
- @feature_elements = feature_elements
28
+ @feature_elements = scenario_definitions
29
29
  end
30
30
 
31
31
  def children
@@ -34,7 +34,7 @@ module Cucumber
34
34
 
35
35
  def short_name
36
36
  first_line = name.split(/\n/)[0]
37
- if first_line =~ /#{language.keywords('feature')}:(.*)/
37
+ if first_line =~ /#{language.feature_keywords}:(.*)/
38
38
  $1.strip
39
39
  else
40
40
  first_line
@@ -65,6 +65,15 @@ module Cucumber
65
65
  self
66
66
  end
67
67
  end
68
+
69
+ class LanguageDelegator < SimpleDelegator
70
+ attr_reader :iso_code
71
+
72
+ def initialize(iso_code, obj)
73
+ super(obj)
74
+ @iso_code = iso_code
75
+ end
76
+ end
68
77
  end
69
78
  end
70
79
  end
@@ -13,9 +13,25 @@ module Cucumber
13
13
  def_delegator :filepath, :same_as?
14
14
  def_delegator :filepath, :filename, :file
15
15
 
16
- def self.of_caller
17
- file, raw_line = *caller[1].split(':')[0..1]
18
- new(file, raw_line.to_i)
16
+ def self.of_caller(additional_depth = 0)
17
+ from_file_colon_line(*caller[1 + additional_depth])
18
+ end
19
+
20
+ def self.from_file_colon_line(file_colon_line)
21
+ file, raw_line = file_colon_line.match(/(.*):(\d+)/)[1..2]
22
+ from_source_location(file, raw_line.to_i)
23
+ end
24
+
25
+ def self.from_source_location(file, line)
26
+ file = File.expand_path(file)
27
+ pwd = File.expand_path(Dir.pwd)
28
+ pwd.force_encoding(file.encoding)
29
+ if file.index(pwd)
30
+ file = file[pwd.length+1..-1]
31
+ elsif file =~ /.*\/gems\/(.*\.rb)$/
32
+ file = $1
33
+ end
34
+ new(file, line)
19
35
  end
20
36
 
21
37
  def initialize(filepath, raw_lines=WILDCARD)
@@ -63,9 +79,6 @@ module Cucumber
63
79
  attr_reader :line
64
80
 
65
81
  def initialize(raw_data)
66
- if Cucumber::JRUBY && raw_data.is_a?(::Java::GherkinFormatterModel::Range)
67
- raw_data = Range.new(raw_data.first, raw_data.last)
68
- end
69
82
  super Array(raw_data).to_set
70
83
  @line = data.first
71
84
  end
@@ -10,15 +10,14 @@ module Cucumber
10
10
  include HasLocation
11
11
  include DescribesItself
12
12
 
13
- attr_reader :gherkin_statement, :language, :location, :comments, :keyword, :name, :multiline_arg
13
+ attr_reader :language, :location, :comments, :keyword, :name, :multiline_arg
14
14
 
15
- def initialize(gherkin_statement, language, location, comments, keyword, name, multiline_arg)
16
- @gherkin_statement, @language, @location, @comments, @keyword, @name, @multiline_arg = gherkin_statement, language, location, comments, keyword, name, multiline_arg
17
- @language || raise("Language is required!")
15
+ def initialize(language, location, comments, keyword, text, multiline_arg)
16
+ @language, @location, @comments, @keyword, @name, @multiline_arg = language, location, comments, keyword, text, multiline_arg
18
17
  end
19
18
 
20
19
  def to_step(row)
21
- Ast::ExpandedOutlineStep.new(self, gherkin_statement, language, row.location, comments, keyword, row.expand(name), replace_multiline_arg(row))
20
+ Ast::ExpandedOutlineStep.new(self, language, row.location, comments, keyword, row.expand(name), replace_multiline_arg(row))
22
21
  end
23
22
 
24
23
  def inspect
@@ -11,23 +11,19 @@ module Cucumber
11
11
  include HasLocation
12
12
  include DescribesItself
13
13
 
14
- attr_reader :gherkin_statement, :language, :location, :background,
15
- :comments, :tags, :feature_tags, :keyword,
14
+ attr_reader :location, :background,
15
+ :comments, :tags, :keyword,
16
16
  :description, :raw_steps
17
17
  private :raw_steps
18
18
 
19
- def initialize(gherkin_statement, language, location, background, comments, tags, feature_tags, keyword, name, description, raw_steps)
20
- @gherkin_statement = gherkin_statement
21
- @language = language
19
+ def initialize(location, comments, tags, keyword, name, description, steps)
22
20
  @location = location
23
- @background = background
24
21
  @comments = comments
25
22
  @tags = tags
26
- @feature_tags = feature_tags
27
23
  @keyword = keyword
28
24
  @name = name
29
25
  @description = description
30
- @raw_steps = raw_steps
26
+ @raw_steps = steps
31
27
  end
32
28
 
33
29
  def children
@@ -1,6 +1,5 @@
1
1
  require 'cucumber/core/ast/names'
2
2
  require 'cucumber/core/ast/location'
3
- require 'cucumber/core/ast/empty_background'
4
3
  require 'cucumber/core/ast/describes_itself'
5
4
 
6
5
  module Cucumber
@@ -13,24 +12,19 @@ module Cucumber
13
12
 
14
13
  MissingExamples = Class.new(StandardError)
15
14
 
16
- attr_reader :gherkin_statement, :language, :background, :comments,
17
- :tags, :feature_tags, :keyword,
15
+ attr_reader :comments, :tags, :keyword,
18
16
  :steps, :examples_tables, :line
19
- private :language, :background, :feature_tags, :line
17
+ private :line
20
18
 
21
- def initialize(gherkin_statement, language, location, background, comments, tags, feature_tags, keyword, name, description, steps, examples_tables)
22
- @gherkin_statement = gherkin_statement
23
- @language = language
19
+ def initialize(location, comments, tags, keyword, name, description, steps, examples)
24
20
  @location = location
25
- @background = background
26
21
  @comments = comments
27
22
  @tags = tags
28
- @feature_tags = feature_tags
29
23
  @keyword = keyword
30
24
  @name = name
31
25
  @description = description
32
26
  @steps = steps
33
- @examples_tables = examples_tables
27
+ @examples_tables = examples
34
28
  end
35
29
 
36
30
  private
@@ -8,10 +8,10 @@ module Cucumber
8
8
  include HasLocation
9
9
  include DescribesItself
10
10
 
11
- attr_reader :keyword, :name, :language, :comments, :exception, :multiline_arg, :gherkin_statement
11
+ attr_reader :keyword, :name, :language, :comments, :exception, :multiline_arg
12
12
 
13
- def initialize(gherkin_statement, language, location, comments, keyword, name, multiline_arg)
14
- @gherkin_statement, @language, @location, @comments, @keyword, @name, @multiline_arg = gherkin_statement, language, location, comments, keyword, name, multiline_arg
13
+ def initialize(language, location, comments, keyword, name, multiline_arg)
14
+ @language, @location, @comments, @keyword, @name, @multiline_arg = language, location, comments, keyword, name, multiline_arg
15
15
  end
16
16
 
17
17
  def to_sexp
@@ -23,9 +23,9 @@ module Cucumber
23
23
  end
24
24
 
25
25
  def actual_keyword(previous_step_keyword = nil)
26
- if [language.keywords('and'), language.keywords('but')].flatten.uniq.include? keyword
26
+ if [language.and_keywords, language.but_keywords].flatten.uniq.include? keyword
27
27
  if previous_step_keyword.nil?
28
- language.keywords('given').reject{|kw| kw == '* '}[0]
28
+ language.given_keywords.reject{|kw| kw == '* '}[0]
29
29
  else
30
30
  previous_step_keyword
31
31
  end
@@ -52,14 +52,15 @@ module Cucumber
52
52
 
53
53
  class ExpandedOutlineStep < Step
54
54
 
55
- def initialize(outline_step, gherkin_statement, language, location, comments, keyword, name, multiline_arg)
56
- @outline_step, @gherkin_statement, @language, @location, @comments, @keyword, @name, @multiline_arg = outline_step, gherkin_statement, language, location, comments, keyword, name, multiline_arg
55
+ def initialize(outline_step, language, location, comments, keyword, name, multiline_arg)
56
+ @outline_step, @language, @location, @comments, @keyword, @name, @multiline_arg = outline_step, language, location, comments, keyword, name, multiline_arg
57
57
  end
58
58
 
59
59
  alias :self_match_locations? :match_locations?
60
60
 
61
61
  def match_locations?(queried_locations)
62
- self_match_locations?(queried_locations) or @outline_step.match_locations?(queried_locations)
62
+ return true if self_match_locations?(queried_locations)
63
+ @outline_step.match_locations?(queried_locations)
63
64
  end
64
65
 
65
66
  alias :step_backtrace_line :backtrace_line
@@ -1,6 +1,5 @@
1
1
  require 'cucumber/core/ast'
2
2
  require 'cucumber/core/platform'
3
- require 'gherkin/rubify'
4
3
 
5
4
  module Cucumber
6
5
  module Core
@@ -9,339 +8,364 @@ module Cucumber
9
8
  # Gherkin parser.
10
9
  class AstBuilder
11
10
 
12
- def initialize(path)
13
- @path = path
14
- @feature_builder = nil
11
+ def initialize(uri)
12
+ @uri = uri
15
13
  end
16
14
 
17
- def result
18
- return Ast::NullFeature.new unless @feature_builder
19
- @feature_builder.result(language)
20
- end
21
-
22
- def language=(language)
23
- @language = language
24
- end
25
-
26
- def uri(uri)
27
- @path = uri
28
- end
29
-
30
- def feature(node)
31
- @feature_builder = FeatureBuilder.new(file, node)
32
- end
33
-
34
- def background(node)
35
- builder = BackgroundBuilder.new(file, node)
36
- @feature_builder.background_builder = builder
37
- @current = builder
38
- end
39
-
40
- def scenario(node)
41
- builder = ScenarioBuilder.new(file, node)
42
- @feature_builder.add_child builder
43
- @current = builder
44
- end
45
-
46
- def scenario_outline(node)
47
- builder = ScenarioOutlineBuilder.new(file, node)
48
- @feature_builder.add_child builder
49
- @current = builder
50
- end
51
-
52
- def examples(node)
53
- @current.add_examples file, node
54
- end
55
-
56
- def step(node)
57
- @current.add_step file, node
58
- end
59
-
60
- def eof
61
- end
62
-
63
- def syntax_error(state, event, legal_events, line)
64
- # raise "SYNTAX ERROR"
15
+ def feature(attributes)
16
+ FeatureBuilder.new(file, attributes).result
65
17
  end
66
18
 
67
19
  private
68
20
 
69
- def language
70
- @language || raise("Language has not been set")
71
- end
72
-
73
21
  def file
74
- @path
22
+ @uri
75
23
  end
76
24
 
77
25
  class Builder
78
- attr_reader :file, :node
79
- private :file, :node
26
+ attr_reader :file, :attributes, :comments, :line
27
+ private :file, :attributes, :comments, :line
80
28
 
81
- def initialize(file, node)
29
+ def initialize(file, attributes)
82
30
  @file = file
83
- @node = node
31
+ @attributes = rubify_keys(attributes.dup)
32
+ @comments = []
33
+ @line = @attributes[:location][:line]
34
+ end
35
+
36
+ def handle_comments(comments)
37
+ remaining_comments = []
38
+ comments.each do |comment|
39
+ if line > comment.location.line
40
+ @comments << comment
41
+ else
42
+ remaining_comments << comment
43
+ end
44
+ end
45
+ children.each { |child| remaining_comments = child.handle_comments(remaining_comments) }
46
+ remaining_comments
84
47
  end
85
48
 
86
49
  private
87
50
 
51
+ def keyword
52
+ attributes[:keyword]
53
+ end
54
+
55
+ def name
56
+ attributes[:name]
57
+ end
58
+
59
+ def description
60
+ attributes[:description] ||= ""
61
+ end
62
+
88
63
  def tags
89
- node.tags.map do |tag|
64
+ attributes[:tags].map do |tag|
90
65
  Ast::Tag.new(
91
- Ast::Location.new(file, tag.line),
92
- tag.name)
66
+ Ast::Location.new(file, tag[:location][:line]),
67
+ tag[:name])
93
68
  end
94
69
  end
95
70
 
96
71
  def location
97
- Ast::Location.new(file, node.line)
72
+ Ast::Location.new(file, attributes[:location][:line])
98
73
  end
99
74
 
100
- def comments
101
- node.comments.map do |comment|
102
- Ast::Comment.new(
103
- Ast::Location.new(file, comment.line),
104
- comment.value
105
- )
75
+ def children
76
+ []
77
+ end
78
+
79
+ def rubify_keys(hash)
80
+ hash.keys.each do |key|
81
+ if key.downcase != key
82
+ hash[underscore(key).to_sym] = hash.delete(key)
83
+ end
106
84
  end
85
+ return hash
86
+ end
87
+
88
+ def underscore(string)
89
+ string.to_s.gsub(/::/, '/').
90
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
91
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
92
+ tr("-", "_").
93
+ downcase
107
94
  end
108
95
  end
109
96
 
110
97
  class FeatureBuilder < Builder
111
- attr_accessor :background_builder
112
- private :background_builder
98
+ attr_reader :language, :background_builder, :scenario_definition_builders
113
99
 
114
100
  def initialize(*)
115
101
  super
116
- @background_builder = nil
102
+ @language = Ast::LanguageDelegator.new(attributes[:language], ::Gherkin3::Dialect.for(attributes[:language]))
103
+ @background_builder = BackgroundBuilder.new(file, attributes[:background]) if attributes[:background]
104
+ @scenario_definition_builders = attributes[:scenario_definitions].map do |sd|
105
+ sd[:type] == :Scenario ? ScenarioBuilder.new(file, sd) : ScenarioOutlineBuilder.new(file, sd)
106
+ end
117
107
  end
118
108
 
119
- def result(language)
120
- background = background(language)
109
+ def result
110
+ handle_comments(all_comments)
121
111
  Ast::Feature.new(
122
- node,
123
112
  language,
124
113
  location,
125
114
  background,
126
115
  comments,
127
116
  tags,
128
- node.keyword,
129
- node.name.lstrip,
130
- node.description.rstrip,
131
- children.map { |builder| builder.result(background, language, tags) }
117
+ keyword,
118
+ name,
119
+ description,
120
+ scenario_definitions
132
121
  )
133
122
  end
134
123
 
135
- def add_child(child)
136
- children << child
124
+ private
125
+
126
+ def background
127
+ return Ast::EmptyBackground.new unless background_builder
128
+ background_builder.result(language)
137
129
  end
138
130
 
139
- def children
140
- @children ||= []
131
+ def scenario_definitions
132
+ scenario_definition_builders.map { |builder| builder.result(language) }
141
133
  end
142
134
 
143
- private
135
+ def all_comments
136
+ attributes[:comments].map do |comment|
137
+ Ast::Comment.new(
138
+ Ast::Location.new(file, comment[:location][:line]),
139
+ comment[:text]
140
+ )
141
+ end
142
+ end
144
143
 
145
- def background(language)
146
- return Ast::EmptyBackground.new unless background_builder
147
- @background ||= background_builder.result(language)
144
+ def children
145
+ (background_builder ? [background_builder] : []) + scenario_definition_builders
148
146
  end
149
147
  end
150
148
 
151
149
  class BackgroundBuilder < Builder
150
+ attr_reader :step_builders
151
+
152
+ def initialize(*)
153
+ super
154
+ @step_builders = attributes[:steps].map { |step| StepBuilder.new(file, step) }
155
+ end
156
+
152
157
  def result(language)
153
158
  Ast::Background.new(
154
- node,
155
- language,
156
159
  location,
157
160
  comments,
158
- node.keyword,
159
- node.name,
160
- node.description,
161
+ keyword,
162
+ name,
163
+ description,
161
164
  steps(language)
162
165
  )
163
166
  end
164
167
 
165
- def add_step(file, node)
166
- step_builders << ScenarioBuilder::StepBuilder.new(file, node)
167
- end
168
-
169
- private
170
-
171
168
  def steps(language)
172
- step_builders.map { |step_builder| step_builder.result(language) }
169
+ step_builders.map { |builder| builder.result(language) }
173
170
  end
174
171
 
175
- def step_builders
176
- @step_builders ||= []
172
+ def children
173
+ step_builders
177
174
  end
178
-
179
175
  end
180
176
 
181
177
  class ScenarioBuilder < Builder
182
- def result(background, language, feature_tags)
178
+ attr_reader :step_builders
179
+
180
+ def initialize(*)
181
+ super
182
+ @step_builders = attributes[:steps].map { |step| StepBuilder.new(file, step) }
183
+ end
184
+
185
+ def result(language)
183
186
  Ast::Scenario.new(
184
- node,
185
- language,
186
187
  location,
187
- background,
188
188
  comments,
189
189
  tags,
190
- feature_tags,
191
- node.keyword,
192
- node.name,
193
- node.description,
190
+ keyword,
191
+ name,
192
+ description,
194
193
  steps(language)
195
194
  )
196
195
  end
197
196
 
198
- def add_step(file, node)
199
- step_builders << StepBuilder.new(file, node)
197
+ def steps(language)
198
+ step_builders.map { |builder| builder.result(language) }
200
199
  end
201
200
 
202
- private
201
+ def children
202
+ step_builders
203
+ end
204
+ end
203
205
 
204
- def steps(language)
205
- step_builders.map { |step_builder| step_builder.result(language) }
206
+ class StepBuilder < Builder
207
+ attr_reader :multiline_argument_builder
208
+
209
+ def initialize(*)
210
+ super
211
+ @multiline_argument_builder = attributes[:argument] ? argument_builder(attributes[:argument]) : nil
206
212
  end
207
213
 
208
- def step_builders
209
- @step_builders ||= []
214
+ def result(language)
215
+ Ast::Step.new(
216
+ language,
217
+ location,
218
+ comments,
219
+ keyword,
220
+ attributes[:text],
221
+ multiline_argument
222
+ )
210
223
  end
211
224
 
212
- class StepBuilder < Builder
213
- def result(language)
214
- Ast::Step.new(
215
- node,
216
- language,
217
- location,
218
- comments,
219
- node.keyword,
220
- node.name,
221
-
222
- MultilineArgument.from(node.doc_string || node.rows, location)
223
- )
224
- end
225
+ def multiline_argument
226
+ return Ast::EmptyMultilineArgument.new unless multiline_argument_builder
227
+ multiline_argument_builder.result
228
+ end
229
+
230
+ def children
231
+ return [] unless multiline_argument_builder
232
+ [multiline_argument_builder]
233
+ end
234
+
235
+ private
236
+
237
+ def argument_builder(attributes)
238
+ attributes[:type] == :DataTable ? DataTableBuilder.new(file, attributes) : DocStringBuilder.new(file, attributes)
239
+ end
240
+ end
241
+
242
+ class OutlineStepBuilder < StepBuilder
243
+ def result(language)
244
+ Ast::OutlineStep.new(
245
+ language,
246
+ location,
247
+ comments,
248
+ keyword,
249
+ attributes[:text],
250
+ multiline_argument
251
+ )
225
252
  end
226
253
  end
227
254
 
228
255
  class ScenarioOutlineBuilder < Builder
229
- def result(background, language, feature_tags)
230
- raise ParseError.new("Missing Examples section for Scenario Outline at #{location}") if examples_builders.empty?
256
+ attr_reader :step_builders, :example_builders
257
+
258
+ def initialize(*)
259
+ super
260
+ @step_builders = attributes[:steps].map { |step| OutlineStepBuilder.new(file, step) }
261
+ @example_builders = attributes[:examples].map { |example| ExamplesTableBuilder.new(file, example) }
262
+ end
263
+
264
+ def result(language)
231
265
  Ast::ScenarioOutline.new(
232
- node,
233
- language,
234
266
  location,
235
- background,
236
267
  comments,
237
268
  tags,
238
- feature_tags,
239
- node.keyword,
240
- node.name,
241
- node.description,
269
+ keyword,
270
+ name,
271
+ description,
242
272
  steps(language),
243
- examples_tables(language)
273
+ examples(language)
244
274
  )
245
275
  end
246
276
 
247
- def add_examples(file, node)
248
- examples_builders << ExamplesTableBuilder.new(file, node)
277
+ def steps(language)
278
+ step_builders.map { |builder| builder.result(language) }
249
279
  end
250
280
 
251
- def add_step(file, node)
252
- step_builders << StepBuilder.new(file, node)
281
+ def examples(language)
282
+ example_builders.map { |builder| builder.result(language) }
253
283
  end
254
284
 
255
- private
256
-
257
- def steps(language)
258
- step_builders.map { |step_builder| step_builder.result(language) }
285
+ def children
286
+ step_builders + example_builders
259
287
  end
288
+ end
260
289
 
261
- def step_builders
262
- @step_builders ||= []
263
- end
290
+ class ExamplesTableBuilder < Builder
291
+ attr_reader :header_builder, :example_rows_builders
264
292
 
265
- def examples_tables(language)
266
- examples_builders.map { |examples_builder| examples_builder.result(language) }
293
+ def initialize(*)
294
+ super
295
+ @header_builder = HeaderBuilder.new(file, attributes[:table_header])
296
+ @example_rows_builders = attributes[:table_body].map do |row_attributes|
297
+ ExampleRowBuilder.new(file, row_attributes)
298
+ end
267
299
  end
268
300
 
269
- def examples_builders
270
- @examples_builders ||= []
301
+ def result(language)
302
+ Ast::Examples.new(
303
+ location,
304
+ comments,
305
+ tags,
306
+ keyword,
307
+ name,
308
+ description,
309
+ header,
310
+ example_rows(language)
311
+ )
271
312
  end
272
313
 
273
- class ExamplesTableBuilder < Builder
314
+ private
274
315
 
275
- def result(language)
276
- Ast::ExamplesTable.new(
277
- node,
278
- location,
279
- comments,
280
- tags,
281
- node.keyword,
282
- node.name,
283
- node.description,
284
- header,
285
- example_rows(language)
286
- )
287
- end
316
+ def header
317
+ @header = header_builder.result
318
+ end
288
319
 
289
- private
320
+ def example_rows(language)
321
+ example_rows_builders.each.with_index.map { |builder, index| builder.result(language, header, index) }
322
+ end
290
323
 
291
- def header
292
- row = node.rows[0]
293
- Ast::ExamplesTable::Header.new(row.cells, location, row_comments(row))
324
+ class HeaderBuilder < Builder
325
+ def result
326
+ cells = attributes[:cells].map { |c| c[:value] }
327
+ Ast::ExamplesTable::Header.new(cells, location, comments)
294
328
  end
329
+ end
295
330
 
296
- def example_rows(language)
297
- _, *raw_examples = *node.rows
298
- raw_examples.each_with_index.map do |row, index|
299
- header.build_row(row.cells, index + 1, location.on_line(row.line), language, row_comments(row))
300
- end
301
- end
331
+ def children
332
+ [header_builder] + example_rows_builders
333
+ end
302
334
 
303
- def row_comments(row)
304
- row.comments.map do |comment|
305
- Ast::Comment.new(
306
- Ast::Location.new(file, comment.line),
307
- comment.value
308
- )
309
- end
335
+ class ExampleRowBuilder < Builder
336
+ def result(language, header, index)
337
+ cells = attributes[:cells].map { |c| c[:value] }
338
+ header.build_row(cells, index + 1, location, language, comments)
310
339
  end
311
340
  end
341
+ end
312
342
 
313
- class StepBuilder < Builder
314
- def result(language)
315
- Ast::OutlineStep.new(
316
- node,
317
- language,
318
- location,
319
- comments,
320
- node.keyword,
321
- node.name,
322
- MultilineArgument.from(node.doc_string || node.rows, location)
323
- )
324
- end
343
+ class DataTableBuilder < Builder
344
+ def result
345
+ Ast::DataTable.new(
346
+ rows,
347
+ location
348
+ )
349
+ end
350
+
351
+ def rows
352
+ attributes[:rows] = attributes[:rows].map { |r| r[:cells].map { |c| c[:value] } }
325
353
  end
326
354
  end
327
355
 
328
- module MultilineArgument
329
- class << self
330
- include ::Gherkin::Rubify
331
-
332
- def from(argument, parent_location)
333
- return Ast::EmptyMultilineArgument.new unless argument
334
- argument = rubify(argument)
335
- case argument
336
- when ::Gherkin::Formatter::Model::DocString
337
- Ast::DocString.new(argument.value, argument.content_type, parent_location.on_line(argument.line_range))
338
- when Array
339
- location = parent_location.on_line(argument.first.line..argument.last.line)
340
- Ast::DataTable.new(argument.map{|row| row.cells}, location)
341
- else
342
- raise ArgumentError, "Don't know how to convert #{argument.inspect} into a MultilineArgument"
343
- end
344
- end
356
+ class DocStringBuilder < Builder
357
+ def result
358
+ Ast::DocString.new(
359
+ attributes[:content],
360
+ attributes[:content_type],
361
+ doc_string_location
362
+ )
363
+ end
364
+
365
+ def doc_string_location
366
+ start_line = attributes[:location][:line]
367
+ end_line = start_line + attributes[:content].each_line.to_a.length + 1
368
+ Ast::Location.new(file, start_line..end_line)
345
369
  end
346
370
  end
347
371