cucumber-core 3.2.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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