cucumber-core 3.2.1 → 4.0.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.
Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +52 -0
  3. data/lib/cucumber/core/compiler.rb +37 -145
  4. data/lib/cucumber/core/events.rb +1 -4
  5. data/lib/cucumber/core/gherkin/parser.rb +14 -22
  6. data/lib/cucumber/core/report/summary.rb +1 -23
  7. data/lib/cucumber/core/test/action.rb +2 -2
  8. data/lib/cucumber/core/test/around_hook.rb +4 -0
  9. data/lib/cucumber/core/test/case.rb +15 -121
  10. data/lib/cucumber/core/{ast → test}/data_table.rb +6 -8
  11. data/lib/cucumber/core/{ast → test}/doc_string.rb +5 -9
  12. data/lib/cucumber/core/{ast → test}/empty_multiline_argument.rb +1 -2
  13. data/lib/cucumber/core/test/filters/locations_filter.rb +2 -2
  14. data/lib/cucumber/core/{ast → test}/location.rb +10 -17
  15. data/lib/cucumber/core/test/runner.rb +5 -3
  16. data/lib/cucumber/core/test/step.rb +20 -36
  17. data/lib/cucumber/core/{ast → test}/tag.rb +1 -1
  18. data/lib/cucumber/core/version.rb +1 -1
  19. metadata +12 -26
  20. data/lib/cucumber/core/ast.rb +0 -14
  21. data/lib/cucumber/core/ast/background.rb +0 -41
  22. data/lib/cucumber/core/ast/comment.rb +0 -28
  23. data/lib/cucumber/core/ast/describes_itself.rb +0 -21
  24. data/lib/cucumber/core/ast/empty_background.rb +0 -17
  25. data/lib/cucumber/core/ast/examples_table.rb +0 -119
  26. data/lib/cucumber/core/ast/feature.rb +0 -88
  27. data/lib/cucumber/core/ast/names.rb +0 -25
  28. data/lib/cucumber/core/ast/outline_step.rb +0 -53
  29. data/lib/cucumber/core/ast/scenario.rb +0 -42
  30. data/lib/cucumber/core/ast/scenario_outline.rb +0 -45
  31. data/lib/cucumber/core/ast/step.rb +0 -83
  32. data/lib/cucumber/core/gherkin/ast_builder.rb +0 -403
  33. data/lib/cucumber/core/gherkin/tag_expression.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dca5c34098216100e0a9ff047bc2d42b9e8dda47
4
- data.tar.gz: aa847f085e1019a5a453ffb7b1a6d9a024694489
2
+ SHA256:
3
+ metadata.gz: 82c21261d3bd32b6f07c066ffc1fc87559b54c80adedacb1b219dc5a6fa607dc
4
+ data.tar.gz: 2b11fd0ed43ca9b8aa3b5fc0041c5c341bd2ba2f16324319bde2e0c010fdc308
5
5
  SHA512:
6
- metadata.gz: d3a9110d17664518200a78fd4671ba89118e1b6513f7c0bdccdbd8706e40a31278e1f5e364e07d60c981fdc6aa7fbe21f1e59264b1c647be8afb3f2103087922
7
- data.tar.gz: b3bca052952a7b249c76bd6e3890cba35ea05390a775656920828447721c84182c2b36fe8ce548c1f53c0b24e68e701a169f606c1c196eef366ca2f71a0c679b
6
+ metadata.gz: b39751c63f5fda3cbaa4d66fe634e86eadbdbb9efb92b6c4f8bf5bde36259a374c9a41c313a10db9ebc849564e97374c9af715f71a74ec64820b4630c789d256
7
+ data.tar.gz: 06c3dc949a7a213e661ac021a793d4177dde521887a538ed291a4031200562ddb8f96ab70c2598ac78cbbcb68b6eee8c0fe67a61f72a894fc396b887b3150e7a
@@ -1,5 +1,57 @@
1
1
  Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber.
2
2
 
3
+ ## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v4.0.0...master)
4
+
5
+ ### Changed
6
+
7
+ * N/A
8
+
9
+ ### Added
10
+
11
+ * N/A
12
+
13
+ ### Fixed
14
+
15
+ * N/A
16
+
17
+ ### Removed
18
+
19
+ * N/A
20
+
21
+ ### Improved
22
+
23
+ * N/A
24
+
25
+ ## [4.0.0](https://github.com/cucumber/cucumber-ruby-core/compare/v3.2.0...v4.0.0)
26
+
27
+ ### Changed
28
+
29
+ * Update to use Gherkin v6 ([#158](https://github.com/cucumber/cucumber-ruby-core/pull/158) @brasmusson)
30
+ * Let Scenarios with no Steps get the result status Undefined ([#157](https://github.com/cucumber/cucumber-ruby-core/pull/157) @brasmusson)
31
+ * Convert to use the Gherkin compiler and Pickles ([#156](https://github.com/cucumber/cucumber-ruby-core/pull/156) @brasmusson)
32
+
33
+ ### Added
34
+
35
+ * N/A
36
+
37
+ ### Fixed
38
+
39
+ * N/A
40
+
41
+ ### Removed
42
+
43
+ * Remove the support for old style tag expressions ([#159](https://github.com/cucumber/cucumber-ruby-core/pull/159) @brasmusson)
44
+
45
+ ### Improved
46
+
47
+ * N/A
48
+
49
+ ## [3.2.1](https://github.com/cucumber/cucumber-ruby-core/compare/v3.2.0...v3.2.1)
50
+
51
+ ### Fixed
52
+
53
+ * Switched `gherkin` in Gemspec to use _pessimistic_ versioning. (These two commits aren't merged into `master`, as they already exist in newer commits. This is a 'backported' patch to resolve [#160](https://github.com/cucumber/cucumber-ruby-core/issues/160)).
54
+
3
55
  ## [3.2.0](https://github.com/cucumber/cucumber-ruby-core/compare/v3.1.0...v3.2.0)
4
56
 
5
57
  ### Added
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/core/test/case'
3
4
  require 'cucumber/core/test/step'
5
+ require 'cucumber/core/test/tag'
6
+ require 'cucumber/core/test/doc_string'
7
+ require 'cucumber/core/test/data_table'
8
+ require 'cucumber/core/test/empty_multiline_argument'
4
9
 
5
10
  module Cucumber
6
11
  module Core
7
-
8
- # Compiles the AST into test cases
12
+ # Compiles the Pickles into test cases
9
13
  class Compiler
10
14
  attr_reader :receiver
11
15
  private :receiver
@@ -14,10 +18,9 @@ module Cucumber
14
18
  @receiver = receiver
15
19
  end
16
20
 
17
- def feature(feature)
18
- compiler = FeatureCompiler.new(TestCaseBuilder.new(receiver))
19
- feature.describe_to(compiler)
20
- self
21
+ def pickle(pickle)
22
+ test_case = create_test_case(pickle)
23
+ test_case.describe_to(receiver)
21
24
  end
22
25
 
23
26
  def done
@@ -25,152 +28,41 @@ module Cucumber
25
28
  self
26
29
  end
27
30
 
28
- # @private
29
- class TestCaseBuilder
30
- attr_reader :receiver
31
- private :receiver
32
-
33
- def initialize(receiver)
34
- @receiver = receiver
35
- end
36
-
37
- def on_background_step(source)
38
- background_test_steps << Test::Step.new(source)
39
- self
40
- end
41
-
42
- def on_step(source)
43
- test_steps << Test::Step.new(source)
44
- self
45
- end
46
-
47
- def on_test_case(source)
48
- Test::Case.new(test_steps, source).describe_to(receiver) if test_steps.count > 0
49
- @test_steps = nil
50
- self
51
- end
52
-
53
- private
54
-
55
- def background_test_steps
56
- @background_test_steps ||= []
57
- end
58
-
59
- def test_steps
60
- @test_steps ||= background_test_steps.dup
61
- end
62
- end
63
-
64
- # @private
65
- class FeatureCompiler
66
- attr_reader :receiver
67
- private :receiver
68
-
69
- def initialize(receiver)
70
- @receiver = receiver
71
- end
72
-
73
- def feature(feature, &descend)
74
- @feature = feature
75
- descend.call(self)
76
- self
77
- end
78
-
79
- def background(background, &descend)
80
- source = [@feature, background]
81
- compiler = BackgroundCompiler.new(source, receiver)
82
- descend.call(compiler)
83
- self
84
- end
85
-
86
- def scenario(scenario, &descend)
87
- source = [@feature, scenario]
88
- scenario_compiler = ScenarioCompiler.new(source, receiver)
89
- descend.call(scenario_compiler)
90
- receiver.on_test_case(source)
91
- self
92
- end
93
-
94
- def scenario_outline(scenario_outline, &descend)
95
- source = [@feature, scenario_outline]
96
- compiler = ScenarioOutlineCompiler.new(source, receiver)
97
- descend.call(compiler)
98
- self
99
- end
100
- end
101
-
102
- # @private
103
- class ScenarioOutlineCompiler
104
- attr_reader :source, :receiver
105
- private :source, :receiver
106
-
107
- def initialize(source, receiver)
108
- @source = source
109
- @receiver = receiver
110
- end
111
-
112
- def outline_step(outline_step)
113
- outline_steps << outline_step
114
- self
115
- end
116
-
117
- def examples_table(examples_table, &descend)
118
- @examples_table = examples_table
119
- descend.call(self)
120
- self
121
- end
122
-
123
- def examples_table_row(row)
124
- steps(row).each do |step|
125
- receiver.on_step(source + [@examples_table, row, step])
126
- end
127
- receiver.on_test_case(source + [@examples_table, row])
128
- self
129
- end
31
+ private
130
32
 
131
- private
132
-
133
- def steps(row)
134
- outline_steps.map { |s| s.to_step(row) }
135
- end
136
-
137
- def outline_steps
138
- @outline_steps ||= []
139
- end
33
+ def create_test_case(pickle)
34
+ uri = pickle[:uri]
35
+ test_steps = pickle[:steps].map { |step| create_test_step(step, uri) }
36
+ lines = pickle[:locations].map { |location| location[:line] }.sort.reverse
37
+ tags = pickle[:tags].map { |tag| Test::Tag.new(Test::Location.new(uri, tag[:location][:line]), tag[:name]) }
38
+ Test::Case.new(pickle[:name], test_steps, Test::Location.new(uri, lines), tags, pickle[:language])
140
39
  end
141
40
 
142
- # @private
143
- class ScenarioCompiler
144
- attr_reader :source, :receiver
145
- private :source, :receiver
146
-
147
- def initialize(source, receiver)
148
- @source = source
149
- @receiver = receiver
150
- end
151
-
152
- def step(step)
153
- receiver.on_step(source + [step])
154
- self
155
- end
41
+ def create_test_step(pickle_step, uri)
42
+ lines = pickle_step[:locations].map { |location| location[:line] }.sort.reverse
43
+ multiline_arg = create_multiline_arg(pickle_step, uri)
44
+ Test::Step.new(pickle_step[:text], Test::Location.new(uri, lines), multiline_arg)
156
45
  end
157
46
 
158
- # @private
159
- class BackgroundCompiler
160
- attr_reader :source, :receiver
161
- private :source, :receiver
162
-
163
- def initialize(source, receiver)
164
- @source = source
165
- @receiver = receiver
166
- end
167
-
168
- def step(step)
169
- receiver.on_background_step(source + [step])
170
- self
47
+ def create_multiline_arg(pickle_step, uri)
48
+ if !pickle_step[:doc_string].nil?
49
+ argument = pickle_step[:doc_string]
50
+ Test::DocString.new(
51
+ argument[:content],
52
+ argument[:content_type],
53
+ Test::Location.new(uri, argument[:location][:line])
54
+ )
55
+ elsif !pickle_step[:data_table].nil?
56
+ argument = pickle_step[:data_table]
57
+ first_cell = argument[:rows].first[:cells].first
58
+ Test::DataTable.new(
59
+ argument[:rows].map { |row| row[:cells].map { |cell| cell[:value] } },
60
+ Test::Location.new(uri, first_cell[:location][:line])
61
+ )
62
+ else
63
+ Test::EmptyMultilineArgument.new
171
64
  end
172
65
  end
173
-
174
66
  end
175
67
  end
176
68
  end
@@ -6,10 +6,7 @@ module Cucumber
6
6
  module Events
7
7
 
8
8
  # Signals that a gherkin source has been parsed
9
- class GherkinSourceParsed < Event.new(:uri, :gherkin_document)
10
- # The uri of the file
11
- attr_reader :uri
12
-
9
+ class GherkinSourceParsed < Event.new(:gherkin_document)
13
10
  # @return [GherkinDocument] the GherkinDocument Ast Node
14
11
  attr_reader :gherkin_document
15
12
 
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'gherkin/parser'
3
- require 'gherkin/token_scanner'
4
- require 'gherkin/errors'
5
- require 'cucumber/core/gherkin/ast_builder'
6
- require 'cucumber/core/ast'
2
+ require 'gherkin/gherkin'
7
3
 
8
4
  module Cucumber
9
5
  module Core
@@ -20,30 +16,26 @@ module Cucumber
20
16
  end
21
17
 
22
18
  def document(document)
23
- parser = ::Gherkin::Parser.new
24
- scanner = ::Gherkin::TokenScanner.new(document.body)
25
- token_matcher = ::Gherkin::TokenMatcher.new(document.language)
26
- core_builder = AstBuilder.new(document.uri)
27
-
28
- begin
29
- result = parser.parse(scanner, token_matcher)
30
- event_bus.gherkin_source_parsed(document.uri, result.dup)
31
-
32
- receiver.feature core_builder.feature(result)
33
- rescue *PARSER_ERRORS => e
34
- raise Core::Gherkin::ParseError.new("#{document.uri}: #{e.message}")
19
+ messages = ::Gherkin::Gherkin.from_source(document.uri, document.body, {default_dialect: document.language, include_source: false})
20
+ messages.each do |message|
21
+ if !message.gherkinDocument.nil?
22
+ event_bus.gherkin_source_parsed(message.gherkinDocument.to_hash)
23
+ elsif !message.pickle.nil?
24
+ receiver.pickle(message.pickle.to_hash)
25
+ elsif !message.attachment.nil?
26
+ raise message.attachment.data
27
+ else
28
+ raise "Unknown message: #{message.to_hash}"
29
+ end
35
30
  end
31
+ rescue RuntimeError => e
32
+ raise Core::Gherkin::ParseError.new("#{document.uri}: #{e.message}")
36
33
  end
37
34
 
38
35
  def done
39
36
  receiver.done
40
37
  self
41
38
  end
42
-
43
- private
44
-
45
- PARSER_ERRORS = ::Gherkin::ParserError
46
-
47
39
  end
48
40
  end
49
41
  end
@@ -29,32 +29,10 @@ module Cucumber
29
29
  end
30
30
  end
31
31
  event_bus.on(:test_step_finished) do |event|
32
- event.result.describe_to test_steps if is_step?(event.test_step)
32
+ event.result.describe_to test_steps unless event.test_step.hook?
33
33
  end
34
34
  self
35
35
  end
36
-
37
- def is_step?(test_step)
38
- StepQueryVisitor.new(test_step).is_step?
39
- end
40
- end
41
-
42
- class StepQueryVisitor
43
- def initialize(test_step)
44
- @step = false
45
- test_step.source.last.describe_to(self)
46
- end
47
-
48
- def is_step?
49
- @step
50
- end
51
-
52
- def step(*)
53
- @step = true
54
- end
55
-
56
- def method_missing(*)
57
- end
58
36
  end
59
37
  end
60
38
  end
@@ -2,7 +2,7 @@
2
2
  require 'cucumber/core/test/result'
3
3
  require 'cucumber/core/test/timer'
4
4
  require 'cucumber/core/test/result'
5
- require 'cucumber/core/ast/location'
5
+ require 'cucumber/core/test/location'
6
6
 
7
7
  module Cucumber
8
8
  module Core
@@ -10,7 +10,7 @@ module Cucumber
10
10
  class Action
11
11
  def initialize(location = nil, &block)
12
12
  raise ArgumentError, "Passing a block to execute the action is mandatory." unless block
13
- @location = location ? location : Ast::Location.new(*block.source_location)
13
+ @location = location ? location : Test::Location.new(*block.source_location)
14
14
  @block = block
15
15
  @timer = Timer.new
16
16
  end
@@ -12,6 +12,10 @@ module Cucumber
12
12
  visitor.around_hook(self, *args, &continue)
13
13
  end
14
14
 
15
+ def hook?
16
+ true
17
+ end
18
+
15
19
  def execute(*args, &continue)
16
20
  @timer.start
17
21
  @block.call(continue)
@@ -1,19 +1,21 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/core/test/result'
3
4
  require 'cucumber/tag_expressions'
4
- require 'cucumber/core/gherkin/tag_expression'
5
- require 'cucumber/core/ast/location'
6
5
 
7
6
  module Cucumber
8
7
  module Core
9
8
  module Test
10
9
  class Case
11
- attr_reader :source, :test_steps, :around_hooks
10
+ attr_reader :name, :test_steps, :location, :tags, :language, :around_hooks
12
11
 
13
- def initialize(test_steps, source, around_hooks = [])
14
- raise ArgumentError.new("test_steps should be an Array but is a #{test_steps.class}") unless test_steps.kind_of?(Array)
12
+ def initialize(name, test_steps, location, tags, language, around_hooks = [])
13
+ raise ArgumentError.new("test_steps should be an Array but is a #{test_steps.class}") unless test_steps.is_a?(Array)
14
+ @name = name
15
15
  @test_steps = test_steps
16
- @source = source
16
+ @location = location
17
+ @tags = tags
18
+ @language = language
17
19
  @around_hooks = around_hooks
18
20
  end
19
21
 
@@ -32,31 +34,12 @@ module Cucumber
32
34
  self
33
35
  end
34
36
 
35
- def describe_source_to(visitor, *args)
36
- source.reverse.each do |node|
37
- node.describe_to(visitor, *args)
38
- end
39
- self
40
- end
41
-
42
37
  def with_steps(test_steps)
43
- self.class.new(test_steps, source, around_hooks)
38
+ self.class.new(name, test_steps, location, tags, language, around_hooks)
44
39
  end
45
40
 
46
41
  def with_around_hooks(around_hooks)
47
- self.class.new(test_steps, source, around_hooks)
48
- end
49
-
50
- def name
51
- @name ||= NameBuilder.new(self).result
52
- end
53
-
54
- def keyword
55
- @keyword ||= NameBuilder.new(self).keyword
56
- end
57
-
58
- def tags
59
- @tags ||= TagCollector.new(self).result
42
+ self.class.new(name, test_steps, location, tags, language, around_hooks)
60
43
  end
61
44
 
62
45
  def match_tags?(*expressions)
@@ -64,43 +47,19 @@ module Cucumber
64
47
  end
65
48
 
66
49
  def match_name?(name_regexp)
67
- source.any? { |node| node.respond_to?(:name) && node.name =~ name_regexp }
68
- end
69
-
70
- def language
71
- feature.language
72
- end
73
-
74
- def location
75
- source.last.location
50
+ name =~ name_regexp
76
51
  end
77
52
 
78
53
  def match_locations?(queried_locations)
79
- queried_locations.any? { |queried_location|
80
- all_source.any? { |node|
81
- node.all_locations.any? { |location|
82
- queried_location.match? location
83
- }
84
- }
85
- }
86
- end
87
-
88
- def all_locations
89
- @all_locations ||= Ast::Location.merge(all_source.map(&:all_locations).flatten)
90
- end
91
-
92
- def all_source
93
- @all_source ||= (source + test_steps.map(&:source)).flatten.uniq
54
+ queried_locations.any? do |queried_location|
55
+ queried_location.match? location
56
+ end
94
57
  end
95
58
 
96
59
  def inspect
97
60
  "#<#{self.class}: #{location}>"
98
61
  end
99
62
 
100
- def feature
101
- source.first
102
- end
103
-
104
63
  def hash
105
64
  location.hash
106
65
  end
@@ -122,73 +81,8 @@ module Cucumber
122
81
  end
123
82
 
124
83
  def match_single_tag_expression?(expression)
125
- if old_style_tag_expression?(expression)
126
- Cucumber::Core::Gherkin::TagExpression.new([expression]).evaluate(tags)
127
- else
128
- Cucumber::TagExpressions::Parser.new.parse(expression).evaluate(tags.map(&:name))
129
- end
130
- end
131
-
132
- def old_style_tag_expression?(expression)
133
- expression.include?(',') || expression.include?('~')
84
+ Cucumber::TagExpressions::Parser.new.parse(expression).evaluate(tags.map(&:name))
134
85
  end
135
-
136
- class NameBuilder
137
- attr_reader :result
138
- attr_reader :keyword
139
-
140
- def initialize(test_case)
141
- test_case.describe_source_to self
142
- end
143
-
144
- def feature(*)
145
- self
146
- end
147
-
148
- def scenario(scenario)
149
- @result = scenario.name
150
- @keyword = scenario.keyword
151
- self
152
- end
153
-
154
- def scenario_outline(outline)
155
- @result = outline.name + @result
156
- @keyword = outline.keyword
157
- self
158
- end
159
-
160
- def examples_table(table)
161
- name = table.name.strip
162
- name = table.keyword if name.length == 0
163
- @result = ", #{name}" + @result
164
- self
165
- end
166
-
167
- def examples_table_row(row)
168
- @result = " (##{row.number})"
169
- self
170
- end
171
- end
172
-
173
- class TagCollector
174
- attr_reader :result
175
-
176
- def initialize(test_case)
177
- @result = []
178
- test_case.describe_source_to self
179
- end
180
-
181
- [:feature, :scenario, :scenario_outline, :examples_table].each do |node_name|
182
- define_method(node_name) do |node|
183
- @result = node.tags + @result
184
- self
185
- end
186
- end
187
-
188
- def examples_table_row(*)
189
- end
190
- end
191
-
192
86
  end
193
87
  end
194
88
  end