cucumber-core 17.0.0 → 19.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -5
  3. data/README.md +1 -5
  4. data/lib/cucumber/core/{events → event}/base.rb +1 -1
  5. data/lib/cucumber/core/{events → event}/envelope.rb +1 -1
  6. data/lib/cucumber/core/{events → event}/gherkin_source_parsed.rb +1 -1
  7. data/lib/cucumber/core/{events → event}/test_case_created.rb +1 -1
  8. data/lib/cucumber/core/{events → event}/test_case_finished.rb +2 -2
  9. data/lib/cucumber/core/{events → event}/test_case_started.rb +1 -1
  10. data/lib/cucumber/core/{events → event}/test_step_created.rb +1 -1
  11. data/lib/cucumber/core/{events → event}/test_step_finished.rb +1 -1
  12. data/lib/cucumber/core/{events → event}/test_step_started.rb +1 -1
  13. data/lib/cucumber/core/event.rb +8 -59
  14. data/lib/cucumber/core/event_bus.rb +24 -2
  15. data/lib/cucumber/core/gherkin/parser.rb +1 -1
  16. data/lib/cucumber/core/gherkin/writer/accepts_comments.rb +23 -0
  17. data/lib/cucumber/core/gherkin/writer/background.rb +3 -1
  18. data/lib/cucumber/core/gherkin/writer/doc_string.rb +3 -1
  19. data/lib/cucumber/core/gherkin/writer/example.rb +3 -1
  20. data/lib/cucumber/core/gherkin/writer/examples.rb +3 -1
  21. data/lib/cucumber/core/gherkin/writer/feature.rb +3 -1
  22. data/lib/cucumber/core/gherkin/writer/has_description.rb +21 -0
  23. data/lib/cucumber/core/gherkin/writer/has_elements.rb +47 -0
  24. data/lib/cucumber/core/gherkin/writer/has_options_initializer.rb +58 -0
  25. data/lib/cucumber/core/gherkin/writer/has_rows.rb +43 -0
  26. data/lib/cucumber/core/gherkin/writer/helpers.rb +6 -174
  27. data/lib/cucumber/core/gherkin/writer/indentation.rb +40 -0
  28. data/lib/cucumber/core/gherkin/writer/rule.rb +3 -1
  29. data/lib/cucumber/core/gherkin/writer/scenario.rb +3 -1
  30. data/lib/cucumber/core/gherkin/writer/scenario_outline.rb +3 -1
  31. data/lib/cucumber/core/gherkin/writer/step.rb +3 -1
  32. data/lib/cucumber/core/gherkin/writer/table.rb +4 -1
  33. data/lib/cucumber/core/test/data_table.rb +1 -1
  34. data/lib/cucumber/core/test/location.rb +1 -1
  35. data/lib/cucumber/core/test/result/flaky.rb +1 -1
  36. data/lib/cucumber/core/test/runner.rb +101 -5
  37. data/lib/cucumber/core/test/timer.rb +1 -1
  38. metadata +28 -23
  39. data/lib/cucumber/core/events.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88faed296b96d11e574838451644e6ab5e784bb2e4bdf9238f24b4488dc4a902
4
- data.tar.gz: 020fe0f4bbc9072752a0748465157991e9c65765ce47c33061c89bedd7e37db0
3
+ metadata.gz: 25b931cc726aa46d40c838941a4a033fd70a34ef4899884750e86e9ad2a0f1b3
4
+ data.tar.gz: d4f808dfc9dc0106e96b1fb6754df766f18a1248914323e89fe32c9a5eee6202
5
5
  SHA512:
6
- metadata.gz: 9f1900994b1560af95ba5871cd9ee8f9647d1e93e4148e6ed1d56406c4fffaa85afdccebfc9d05a11d795bebf473e2450589656d193e7d42d6412d4c1a861fa4
7
- data.tar.gz: 7490537caa92180e2887080e8bd7fa169bc9994dd2d01262ffb57b3d3b3d5df1a34c7ae6503d5433847409b44586e221bdd2dd00ec7f28a094c00878751e480e
6
+ metadata.gz: 8a0428cdbc69794d04b068fc6b72a83026cb8b08959c91153fc4c3b71b44062e442e5842dcef7b3dc8af424daa6c7dbebcce9fe13258ee4cc51a1aa6b9b798b6
7
+ data.tar.gz: ae12323897fe7bb9b0f1bad297dadf5a2be335f5eb187a1093317350e5bd6211dac5cb93c1bafcca50610a5366571e9b85cb512363567471973ddbbf55bbcdd0
data/CHANGELOG.md CHANGED
@@ -10,6 +10,22 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [19.0.0] - 2026-08-19
14
+ ### Added
15
+ - Emit test_case_started/finished and test_step_started/test_step_finished envelopes from the Runner. ([#331](https://github.com/cucumber/cucumber-ruby-core/pull/331))
16
+
17
+ ### Changed
18
+ - Increased upper bounds of `cucumber-messages` to v34 and `cucumber-gherkin` to v42
19
+ - **BREAKING CHANGE:** Event base class has been internally refactored. See upgrading notes for [19.0.0.md](upgrading_notes/19.0.0.md#upgrading-to-cucumber-core-1900), for full changes
20
+ - Permit usage of `cucumber-tag-expressions` up to v11
21
+
22
+ ## [18.0.0] - 2026-07-13
23
+ ### Changed
24
+ - **BREAKING CHANGE:** Structure of Gherkin writer helpers have been changed. Now one helper per file, and all helpers are still available by requiring `gherkin/writer/helpers`.
25
+ See upgrading notes for [18.0.0.md](upgrading_notes/18.0.0.md#upgrading-to-cucumber-core-1800), for full changes
26
+ - **BREAKING CHANGE:** Flaky results are now treated as a passing state. See upgrading notes for [18.0.0.md](upgrading_notes/18.0.0.md#upgrading-to-cucumber-core-1800), for full changes
27
+ - Tidied up a bunch of mostly internal test code to be more rubocop compliant
28
+
13
29
  ## [17.0.0] - 2026-06-01
14
30
  ### Added
15
31
  - Added `#to_envelope` for `Cucumber::Core::Gherkin::Document` ([#329](https://github.com/cucumber/cucumber-ruby-core/pull/329))
@@ -87,13 +103,12 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo
87
103
 
88
104
  ## [14.0.0] - 2024-08-08
89
105
  ### Changed
90
- - Permit usage of gherkin up to v29 and messages up to v26
91
- - **Internal Breaking Change**: Structure of `Action` classes have changed.
92
- See upgrading notes for [14.0.0.md](upgrading_notes/14.0.0.md#upgrading-to-cucumber-core-1400)
106
+ - **BREAKING CHANGE:** Structure of `Action` classes have changed. See upgrading notes for [14.0.0.md](upgrading_notes/14.0.0.md#upgrading-to-cucumber-core-1400)
93
107
  ([#282](https://github.com/cucumber/cucumber-ruby-core/pull/282))
108
+ - Permit usage of gherkin up to v29 and messages up to v26
94
109
 
95
110
  ### Removed
96
- - Remove support for ruby 2.6 and below. 2.7 or higher is required now (Autofixed to Ruby 2.7 styles)
111
+ - Removed support for ruby 2.6 and below. 2.7 or higher is required now (Autofixed to Ruby 2.7 styles)
97
112
 
98
113
  ## [13.0.3] - 2024-07-24
99
114
  ### Changed
@@ -149,7 +164,9 @@ See upgrading notes for [13.0.0.md](upgrading_notes/13.0.0.md#upgrading-to-cucum
149
164
  ### Changed
150
165
  - Updated `cucumber-gherkin` and `cucumber-messages`
151
166
 
152
- [Unreleased]: https://github.com/cucumber/cucumber-ruby-core/compare/v17.0.0...HEAD
167
+ [Unreleased]: https://github.com/cucumber/cucumber-ruby-core/compare/v19.0.0...HEAD
168
+ [19.0.0]: https://github.com/cucumber/cucumber-ruby-core/compare/v18.0.0...v19.0.0
169
+ [18.0.0]: https://github.com/cucumber/cucumber-ruby-core/compare/v17.0.0...v18.0.0
153
170
  [17.0.0]: https://github.com/cucumber/cucumber-ruby-core/compare/v16.2.0...v17.0.0
154
171
  [16.2.0]: https://github.com/cucumber/cucumber-ruby-core/compare/v16.1.1...v16.2.0
155
172
  [16.1.1]: https://github.com/cucumber/cucumber-ruby-core/compare/v16.1.0...v16.1.1
data/README.md CHANGED
@@ -45,11 +45,7 @@ or install the gem directly:
45
45
 
46
46
  ### Supported platforms
47
47
 
48
- - Ruby 3.3
49
- - Ruby 3.2
50
- - Ruby 3.1
51
- - Ruby 3.0
52
- - JRuby 9.4 (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/main/docs/jruby-limitations.md))
48
+ Supported Platforms are detailed in the [cucumber-ruby repository](https://github.com/cucumber/cucumber-ruby#supported-platforms).
53
49
 
54
50
  ## Usage
55
51
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cucumber
4
4
  module Core
5
- module Events
5
+ module Event
6
6
  # An archetype of what each Cucumber Event defined in cucumber-ruby must adhere to
7
7
  class Base
8
8
  # The "key" name of the class to be used as the key in the event registry (Underscored name symbolized)
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  class Envelope < Base
9
9
  attr_reader :envelope
10
10
 
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a gherkin source has been parsed
9
9
  class GherkinSourceParsed < Base
10
10
  # @return [GherkinDocument] the GherkinDocument Ast Node that was parsed
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a Test::Case was created from a Pickle
9
9
  class TestCaseCreated < Base
10
10
  attr_reader :test_case, :pickle
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../event'
3
+ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a {Test::Case} has finished executing
9
9
  class TestCaseFinished < Base
10
10
  # @return [Test::Case] that was executed
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a {Test::Case} is about to be executed
9
9
  class TestCaseStarted < Base
10
10
  # @return [Cucumber::Core::Test::Case] the test case to be executed
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a Test::Step was created from a PickleStep
9
9
  class TestStepCreated < Base
10
10
  attr_reader :test_step, :pickle_step
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a {Test::Step} has finished executing
9
9
  class TestStepFinished < Base
10
10
  # @return [Cucumber::Core::Test::Step] the test step that was executed
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
- module Events
7
+ module Event
8
8
  # Signals that a {Test::Step} is about to be executed
9
9
  class TestStepStarted < Base
10
10
  # @return [Cucumber::Core::Test::Step] the test step to be executed
@@ -1,61 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Cucumber
4
- module Core
5
- class Event
6
- # Macro to generate new subclasses of {Event} with attribute readers.
7
- def self.new(*events)
8
- # Use normal constructor for subclasses of Event
9
- return super if ancestors.index(Event).positive?
10
-
11
- Class.new(Event) do
12
- # NB: We need to use metaprogramming here instead of direct variable obtainment
13
- # because JRuby does not guarantee the order in which variables are defined is equivalent
14
- # to the order in which they are obtainable
15
- #
16
- # See https://github.com/jruby/jruby/issues/7988 for more info
17
- attr_reader(*events)
18
-
19
- define_method(:initialize) do |*attributes|
20
- events.zip(attributes) do |name, value|
21
- instance_variable_set(:"@#{name}", value)
22
- end
23
- end
24
- end
25
- end
26
-
27
- def to_h
28
- instance_variables.to_h do |variable_name|
29
- [variable_name[1..].to_sym, instance_variable_get(variable_name)]
30
- end
31
- end
32
-
33
- def attributes
34
- instance_variables.map { |var| instance_variable_get(var) }
35
- end
36
-
37
- def event_id
38
- self.class.event_id
39
- end
40
-
41
- class << self
42
- # @return [Symbol] the underscored name of the class to be used as the key in an event registry
43
- def event_id
44
- underscore(name.split('::').last).to_sym
45
- end
46
-
47
- private
48
-
49
- def underscore(string)
50
- string
51
- .to_s
52
- .gsub('::', '/')
53
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
54
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
55
- .tr('-', '_')
56
- .downcase
57
- end
58
- end
59
- end
60
- end
61
- end
3
+ require_relative 'event/envelope'
4
+ require_relative 'event/gherkin_source_parsed'
5
+ require_relative 'event/test_case_created'
6
+ require_relative 'event/test_case_started'
7
+ require_relative 'event/test_case_finished'
8
+ require_relative 'event/test_step_created'
9
+ require_relative 'event/test_step_started'
10
+ require_relative 'event/test_step_finished'
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'events'
3
+ require_relative 'event'
4
4
 
5
5
  module Cucumber
6
6
  module Core
@@ -11,8 +11,30 @@ module Cucumber
11
11
  class EventBus
12
12
  attr_reader :event_types
13
13
 
14
+ # The registry contains all the event registered in the core, that will be used by the {EventBus} by default.
15
+ def self.registry
16
+ build_registry(
17
+ Event::Envelope,
18
+ Event::GherkinSourceParsed,
19
+ Event::TestCaseCreated,
20
+ Event::TestCaseStarted,
21
+ Event::TestCaseFinished,
22
+ Event::TestStepCreated,
23
+ Event::TestStepStarted,
24
+ Event::TestStepFinished
25
+ )
26
+ end
27
+
28
+ # Build an event registry to be passed to the {EventBus} constructor from a list of types.
29
+ # Each type must respond to `event_id` so that it can be added to the registry hash
30
+ #
31
+ # @return [Hash{Symbol => Class}]
32
+ def self.build_registry(*types)
33
+ types.to_h { |type| [type.event_id, type] }
34
+ end
35
+
14
36
  # @param registry [Hash{Symbol => Class}] a hash of event types to use on the bus
15
- def initialize(registry = Events.registry)
37
+ def initialize(registry = self.class.registry)
16
38
  @event_types = registry.freeze
17
39
  @handlers = {}
18
40
  @event_queue = []
@@ -5,7 +5,7 @@ require 'gherkin'
5
5
  module Cucumber
6
6
  module Core
7
7
  module Gherkin
8
- ParseError = Class.new(StandardError)
8
+ class ParseError < StandardError; end
9
9
 
10
10
  class Parser
11
11
  attr_reader :receiver, :event_bus, :gherkin_query
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module AcceptsComments
8
+ def comment(line)
9
+ comment_lines << "# #{line}"
10
+ end
11
+
12
+ def comment_lines
13
+ @comment_lines ||= []
14
+ end
15
+
16
+ def slurp_comments
17
+ comment_lines.tap { @comment_lines = nil }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -10,7 +10,9 @@ module Cucumber
10
10
  include HasElements
11
11
  include HasOptionsInitializer
12
12
  include HasDescription
13
- include Indentation.level 2
13
+ extend Indentation
14
+
15
+ indentation_level 2
14
16
 
15
17
  default_keyword 'Background'
16
18
 
@@ -7,7 +7,9 @@ module Cucumber
7
7
  module Gherkin
8
8
  module Writer
9
9
  class DocString
10
- include Indentation.level(6)
10
+ extend Indentation
11
+
12
+ indentation_level 6
11
13
 
12
14
  attr_reader :strings, :content_type
13
15
  private :strings, :content_type
@@ -8,7 +8,9 @@ module Cucumber
8
8
  module Gherkin
9
9
  module Writer
10
10
  class Example < Scenario
11
- include Indentation.level 4
11
+ extend Indentation
12
+
13
+ indentation_level 4
12
14
 
13
15
  default_keyword 'Example'
14
16
  end
@@ -12,7 +12,9 @@ module Cucumber
12
12
  include HasOptionsInitializer
13
13
  include HasRows
14
14
  include HasDescription
15
- include Indentation.level(4)
15
+ extend Indentation
16
+
17
+ indentation_level 4
16
18
 
17
19
  default_keyword 'Examples'
18
20
 
@@ -12,7 +12,9 @@ module Cucumber
12
12
  include HasElements
13
13
  include HasOptionsInitializer
14
14
  include HasDescription
15
- include Indentation.level(0)
15
+ extend Indentation
16
+
17
+ indentation_level 0
16
18
 
17
19
  default_keyword 'Feature'
18
20
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module HasDescription
8
+ private
9
+
10
+ def description
11
+ options.fetch(:description, '').split("\n").map(&:strip)
12
+ end
13
+
14
+ def description_statement
15
+ description.map { |s| indent(s, 2) } unless description.empty?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module HasElements
8
+ include AcceptsComments
9
+
10
+ def self.included(base)
11
+ base.extend HasElementBuilders
12
+ end
13
+
14
+ def build(source = [])
15
+ elements.inject(source + statements) { |acc, el| el.build(acc) }
16
+ end
17
+
18
+ private
19
+
20
+ def elements
21
+ @elements ||= []
22
+ end
23
+
24
+ module HasElementBuilders
25
+ def elements(*names)
26
+ names.each { |name| element(name) }
27
+ end
28
+
29
+ private
30
+
31
+ def element(name)
32
+ define_method(name) do |*args, &source|
33
+ factory_name = String(name).split('_').map(&:capitalize).join
34
+ factory = Writer.const_get(factory_name)
35
+ factory.new(slurp_comments, *args).tap do |builder|
36
+ builder.instance_exec(&source) if source
37
+ elements << builder
38
+ end
39
+ self
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module HasOptionsInitializer
8
+ def self.included(base)
9
+ base.extend HasDefaultKeyword
10
+ end
11
+
12
+ attr_reader :name, :options
13
+ private :name, :options
14
+
15
+ def initialize(*args)
16
+ @comments = args.shift if args.first.is_a?(Array)
17
+ @comments ||= []
18
+ @options = args.pop if args.last.is_a?(Hash)
19
+ @options ||= {}
20
+ @name = args.first
21
+ end
22
+
23
+ private
24
+
25
+ def comments_statement
26
+ @comments
27
+ end
28
+
29
+ def keyword
30
+ options.fetch(:keyword) { self.class.keyword }
31
+ end
32
+
33
+ def name_statement
34
+ "#{keyword}: #{name}".strip
35
+ end
36
+
37
+ def tag_statement
38
+ tags
39
+ end
40
+
41
+ def tags
42
+ options[:tags]
43
+ end
44
+
45
+ module HasDefaultKeyword
46
+ def default_keyword(keyword)
47
+ @keyword = keyword
48
+ end
49
+
50
+ def keyword
51
+ @keyword
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module HasRows
8
+ def row(*cells)
9
+ rows << cells
10
+ end
11
+
12
+ def rows
13
+ @rows ||= []
14
+ end
15
+
16
+ private
17
+
18
+ def row_statements(indent = nil)
19
+ rows.map { |row| indent(table_row(row), indent) }
20
+ end
21
+
22
+ def table_row(row)
23
+ padded = pad(row)
24
+ "| #{padded.join(' | ')} |"
25
+ end
26
+
27
+ def pad(row)
28
+ row.map.with_index { |text, position| justify_cell(text, position) }
29
+ end
30
+
31
+ def column_length(column)
32
+ lengths = rows.transpose.map { |r| r.map(&:length).max }
33
+ lengths[column]
34
+ end
35
+
36
+ def justify_cell(cell, position)
37
+ cell.ljust(column_length(position))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,177 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Cucumber
4
- module Core
5
- module Gherkin
6
- module Writer
7
- module HasOptionsInitializer
8
- def self.included(base)
9
- base.extend HasDefaultKeyword
10
- end
3
+ require_relative 'accepts_comments'
11
4
 
12
- attr_reader :name, :options
13
- private :name, :options
14
-
15
- def initialize(*args)
16
- @comments = args.shift if args.first.is_a?(Array)
17
- @comments ||= []
18
- @options = args.pop if args.last.is_a?(Hash)
19
- @options ||= {}
20
- @name = args.first
21
- end
22
-
23
- private
24
-
25
- def comments_statement
26
- @comments
27
- end
28
-
29
- def keyword
30
- options.fetch(:keyword) { self.class.keyword }
31
- end
32
-
33
- def name_statement
34
- "#{keyword}: #{name}".strip
35
- end
36
-
37
- def tag_statement
38
- tags
39
- end
40
-
41
- def tags
42
- options[:tags]
43
- end
44
-
45
- module HasDefaultKeyword
46
- def default_keyword(keyword)
47
- @keyword = keyword
48
- end
49
-
50
- def keyword
51
- @keyword
52
- end
53
- end
54
- end
55
-
56
- module AcceptsComments
57
- def comment(line)
58
- comment_lines << "# #{line}"
59
- end
60
-
61
- def comment_lines
62
- @comment_lines ||= []
63
- end
64
-
65
- def slurp_comments
66
- comment_lines.tap { @comment_lines = nil }
67
- end
68
- end
69
-
70
- module HasElements
71
- include AcceptsComments
72
-
73
- def self.included(base)
74
- base.extend HasElementBuilders
75
- end
76
-
77
- def build(source = [])
78
- elements.inject(source + statements) { |acc, el| el.build(acc) }
79
- end
80
-
81
- private
82
-
83
- def elements
84
- @elements ||= []
85
- end
86
-
87
- module HasElementBuilders
88
- def elements(*names)
89
- names.each { |name| element(name) }
90
- end
91
-
92
- private
93
-
94
- def element(name)
95
- define_method(name) do |*args, &source|
96
- factory_name = String(name).split('_').map(&:capitalize).join
97
- factory = Writer.const_get(factory_name)
98
- factory.new(slurp_comments, *args).tap do |builder|
99
- builder.instance_exec(&source) if source
100
- elements << builder
101
- end
102
- self
103
- end
104
- end
105
- end
106
- end
107
-
108
- module Indentation
109
- def self.level(number)
110
- Module.new do
111
- define_method(:indent) do |string, amount = nil|
112
- return string if string.nil? || string.empty?
113
-
114
- amount ||= number
115
- "#{' ' * amount}#{string}"
116
- end
117
-
118
- define_method(:indent_level) do
119
- number
120
- end
121
-
122
- define_method(:prepare_statements) do |*statements|
123
- statements.flatten.compact.map { |s| indent(s) }
124
- end
125
- end
126
- end
127
- end
128
-
129
- module HasDescription
130
- private
131
-
132
- def description
133
- options.fetch(:description, '').split("\n").map(&:strip)
134
- end
135
-
136
- def description_statement
137
- description.map { |s| indent(s, 2) } unless description.empty?
138
- end
139
- end
140
-
141
- module HasRows
142
- def row(*cells)
143
- rows << cells
144
- end
145
-
146
- def rows
147
- @rows ||= []
148
- end
149
-
150
- private
151
-
152
- def row_statements(indent = nil)
153
- rows.map { |row| indent(table_row(row), indent) }
154
- end
155
-
156
- def table_row(row)
157
- padded = pad(row)
158
- "| #{padded.join(' | ')} |"
159
- end
160
-
161
- def pad(row)
162
- row.map.with_index { |text, position| justify_cell(text, position) }
163
- end
164
-
165
- def column_length(column)
166
- lengths = rows.transpose.map { |r| r.map(&:length).max }
167
- lengths[column]
168
- end
169
-
170
- def justify_cell(cell, position)
171
- cell.ljust(column_length(position))
172
- end
173
- end
174
- end
175
- end
176
- end
177
- end
5
+ require_relative 'has_description'
6
+ require_relative 'has_elements'
7
+ require_relative 'has_options_initializer'
8
+ require_relative 'has_rows'
9
+ require_relative 'indentation'
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Gherkin
6
+ module Writer
7
+ module Indentation
8
+ def indentation_level(number)
9
+ create_indent(number)
10
+ create_indent_level(number)
11
+ create_prepare_statements
12
+ end
13
+
14
+ private
15
+
16
+ def create_indent(number)
17
+ define_method(:indent) do |string, amount = nil|
18
+ return string if string.nil? || string.empty?
19
+
20
+ amount ||= number
21
+ "#{' ' * amount}#{string}"
22
+ end
23
+ end
24
+
25
+ def create_indent_level(number)
26
+ define_method(:indent_level) do
27
+ number
28
+ end
29
+ end
30
+
31
+ def create_prepare_statements
32
+ define_method(:prepare_statements) do |*statements|
33
+ statements.flatten.compact.map { |s| indent(s) }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -12,7 +12,9 @@ module Cucumber
12
12
  include HasElements
13
13
  include HasOptionsInitializer
14
14
  include HasDescription
15
- include Indentation.level 2
15
+ extend Indentation
16
+
17
+ indentation_level 2
16
18
 
17
19
  default_keyword 'Rule'
18
20
 
@@ -10,7 +10,9 @@ module Cucumber
10
10
  include HasElements
11
11
  include HasOptionsInitializer
12
12
  include HasDescription
13
- include Indentation.level 2
13
+ extend Indentation
14
+
15
+ indentation_level 2
14
16
 
15
17
  default_keyword 'Scenario'
16
18
 
@@ -10,7 +10,9 @@ module Cucumber
10
10
  include HasElements
11
11
  include HasOptionsInitializer
12
12
  include HasDescription
13
- include Indentation.level 2
13
+ extend Indentation
14
+
15
+ indentation_level 2
14
16
 
15
17
  default_keyword 'Scenario Outline'
16
18
 
@@ -11,7 +11,9 @@ module Cucumber
11
11
  class Step
12
12
  include HasElements
13
13
  include HasOptionsInitializer
14
- include Indentation.level 4
14
+ extend Indentation
15
+
16
+ indentation_level 4
15
17
 
16
18
  default_keyword 'Given'
17
19
 
@@ -7,7 +7,10 @@ module Cucumber
7
7
  module Gherkin
8
8
  module Writer
9
9
  class Table
10
- include Indentation.level(6)
10
+ extend Indentation
11
+
12
+ indentation_level 6
13
+
11
14
  include HasRows
12
15
 
13
16
  def initialize(*); end
@@ -57,7 +57,7 @@ module Cucumber
57
57
  end
58
58
 
59
59
  def inspect
60
- %{#<#{self.class} #{raw.inspect}>}
60
+ %(#<#{self.class} #{raw.inspect}>)
61
61
  end
62
62
 
63
63
  def lines_count
@@ -5,7 +5,7 @@ require 'set'
5
5
  module Cucumber
6
6
  module Core
7
7
  module Test
8
- IncompatibleLocations = Class.new(StandardError)
8
+ class IncompatibleLocations < StandardError; end
9
9
 
10
10
  module Location
11
11
  def self.of_caller(additional_depth = 0)
@@ -12,7 +12,7 @@ module Cucumber
12
12
  # retry, therefore only the class method self.ok? is needed.
13
13
  class Flaky
14
14
  def self.ok?
15
- false
15
+ true
16
16
  end
17
17
  end
18
18
  end
@@ -3,34 +3,51 @@
3
3
  require 'cucumber/messages/helpers/test_step_result_comparator'
4
4
 
5
5
  require_relative 'timer'
6
+ require 'cucumber/messages'
6
7
 
7
8
  module Cucumber
8
9
  module Core
9
10
  module Test
10
11
  class Runner
11
- attr_reader :event_bus, :running_test_case, :running_test_step
12
- private :event_bus, :running_test_case, :running_test_step
12
+ include Cucumber::Messages::Helpers::TimeConversion
13
13
 
14
- def initialize(event_bus)
14
+ attr_reader :event_bus, :running_test_case, :running_test_step, :id_generator
15
+ private :event_bus, :running_test_case, :running_test_step, :id_generator
16
+
17
+ def initialize(event_bus, id_generator = Cucumber::Messages::Helpers::IdGenerator::UUID.new, backtrace_filter = nil, max_attempts = 1)
15
18
  @event_bus = event_bus
19
+ @id_generator = id_generator
20
+ @backtrace_filter = backtrace_filter
21
+ @max_attempts = max_attempts
22
+ @current_test_case = nil
16
23
  end
17
24
 
18
25
  def test_case(test_case, &descend)
26
+ @attempt = @current_test_case == test_case ? @attempt + 1 : 1
27
+ @current_test_case = test_case
28
+ @current_test_case_started_id = id_generator.new_id
19
29
  @running_test_case = RunningTestCase.new
20
30
  @running_test_step = nil
21
31
  event_bus.test_case_started(test_case)
32
+ event_bus.envelope(to_test_case_started_envelope(test_case))
33
+
22
34
  descend.call(self)
23
- result = running_test_case.result
24
- result = Result::Undefined.new('The test case has no steps', Result::UnknownDuration.new, ["#{test_case.location}:in `#{test_case.name}'"]) if result.unknown?
35
+
36
+ result = calculate_test_case_result(test_case)
25
37
  event_bus.test_case_finished(test_case, result)
38
+ event_bus.envelope(to_test_case_finished_envelope(result))
26
39
  self
27
40
  end
28
41
 
29
42
  def test_step(test_step)
30
43
  @running_test_step = test_step
31
44
  event_bus.test_step_started test_step
45
+ event_bus.envelope(to_test_step_started_envelope(test_step))
46
+
32
47
  step_result = running_test_case.execute(test_step)
48
+
33
49
  event_bus.test_step_finished test_step, step_result
50
+ event_bus.envelope(to_test_step_finished_envelope(test_step, step_result))
34
51
  @running_test_step = nil
35
52
  self
36
53
  end
@@ -46,6 +63,85 @@ module Cucumber
46
63
  self
47
64
  end
48
65
 
66
+ def calculate_test_case_result(test_case)
67
+ if running_test_case.result.unknown?
68
+ Result::Undefined.new('The test case has no steps', Result::UnknownDuration.new, ["#{test_case.location}:in `#{test_case.name}'"])
69
+ else
70
+ running_test_case.result
71
+ end
72
+ end
73
+
74
+ def to_test_case_started_envelope(test_case)
75
+ Cucumber::Messages::Envelope.new(
76
+ test_case_started: Cucumber::Messages::TestCaseStarted.new(
77
+ id: @current_test_case_started_id,
78
+ test_case_id: test_case.id,
79
+ timestamp: time_to_timestamp(Time.now),
80
+ attempt: @attempt
81
+ )
82
+ )
83
+ end
84
+
85
+ def to_test_case_finished_envelope(result)
86
+ Cucumber::Messages::Envelope.new(
87
+ test_case_finished: Cucumber::Messages::TestCaseFinished.new(
88
+ test_case_started_id: @current_test_case_started_id,
89
+ timestamp: time_to_timestamp(Time.now),
90
+ will_be_retried: result.failed? && (@attempt < @max_attempts)
91
+ )
92
+ )
93
+ end
94
+
95
+ def to_test_step_started_envelope(test_step)
96
+ Cucumber::Messages::Envelope.new(
97
+ test_step_started: Cucumber::Messages::TestStepStarted.new(
98
+ test_step_id: test_step.id,
99
+ test_case_started_id: @current_test_case_started_id,
100
+ timestamp: time_to_timestamp(Time.now)
101
+ )
102
+ )
103
+ end
104
+
105
+ def to_test_step_finished_envelope(test_step, step_result)
106
+ result = @backtrace_filter.nil? ? step_result : step_result.with_filtered_backtrace(@backtrace_filter)
107
+ result_message = result.to_message
108
+ if result.failed? || result.pending?
109
+ message_element = result.failed? ? result.exception : result
110
+
111
+ result_message = Cucumber::Messages::TestStepResult.new(
112
+ status: result_message.status,
113
+ duration: result_message.duration,
114
+ message: to_error_message(message_element),
115
+ exception: to_exception_object(result, message_element)
116
+ )
117
+ end
118
+ Cucumber::Messages::Envelope.new(
119
+ test_step_finished: Cucumber::Messages::TestStepFinished.new(
120
+ test_step_id: test_step.id,
121
+ test_case_started_id: @current_test_case_started_id,
122
+ test_step_result: result_message,
123
+ timestamp: time_to_timestamp(Time.now)
124
+ )
125
+ )
126
+ end
127
+
128
+ def to_error_message(message_element)
129
+ <<~ERROR_MESSAGE
130
+ #{message_element.message} (#{message_element.class})
131
+ #{message_element.backtrace}
132
+ ERROR_MESSAGE
133
+ end
134
+
135
+ def to_exception_object(result, message_element)
136
+ return unless result.failed?
137
+
138
+ Cucumber::Messages::Exception.new(
139
+ type: message_element.class,
140
+ message: message_element.message,
141
+ stack_trace: message_element.backtrace.join("\n")
142
+ )
143
+ end
144
+
49
145
  class RunningTestCase
50
146
  include Cucumber::Messages::Helpers::TestStepResultComparator
51
147
 
@@ -38,7 +38,7 @@ module Cucumber
38
38
  end
39
39
  elsif (defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby') == 'jruby'
40
40
  def time_in_nanoseconds
41
- java.lang.System.nanoTime()
41
+ java.lang.System.nanoTime
42
42
  end
43
43
  else
44
44
  def time_in_nanoseconds
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 17.0.0
4
+ version: 19.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aslak Hellesøy
@@ -22,7 +22,7 @@ dependencies:
22
22
  version: '36'
23
23
  - - "<"
24
24
  - !ruby/object:Gem::Version
25
- version: '40'
25
+ version: '43'
26
26
  type: :runtime
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '36'
33
33
  - - "<"
34
34
  - !ruby/object:Gem::Version
35
- version: '40'
35
+ version: '43'
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: cucumber-messages
38
38
  requirement: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  version: '31'
43
43
  - - "<"
44
44
  - !ruby/object:Gem::Version
45
- version: '33'
45
+ version: '35'
46
46
  type: :runtime
47
47
  prerelease: false
48
48
  version_requirements: !ruby/object:Gem::Requirement
@@ -52,7 +52,7 @@ dependencies:
52
52
  version: '31'
53
53
  - - "<"
54
54
  - !ruby/object:Gem::Version
55
- version: '33'
55
+ version: '35'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: cucumber-tag-expressions
58
58
  requirement: !ruby/object:Gem::Requirement
@@ -62,7 +62,7 @@ dependencies:
62
62
  version: '6'
63
63
  - - "<"
64
64
  - !ruby/object:Gem::Version
65
- version: '10'
65
+ version: '12'
66
66
  type: :runtime
67
67
  prerelease: false
68
68
  version_requirements: !ruby/object:Gem::Requirement
@@ -72,7 +72,7 @@ dependencies:
72
72
  version: '6'
73
73
  - - "<"
74
74
  - !ruby/object:Gem::Version
75
- version: '10'
75
+ version: '12'
76
76
  - !ruby/object:Gem::Dependency
77
77
  name: rake
78
78
  requirement: !ruby/object:Gem::Requirement
@@ -107,14 +107,14 @@ dependencies:
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 1.87.0
110
+ version: 1.89.0
111
111
  type: :development
112
112
  prerelease: false
113
113
  version_requirements: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.87.0
117
+ version: 1.89.0
118
118
  - !ruby/object:Gem::Dependency
119
119
  name: rubocop-packaging
120
120
  requirement: !ruby/object:Gem::Requirement
@@ -149,14 +149,14 @@ dependencies:
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 3.9.0
152
+ version: 3.10.2
153
153
  type: :development
154
154
  prerelease: false
155
155
  version_requirements: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 3.9.0
159
+ version: 3.10.2
160
160
  description: Core library for the Cucumber BDD app
161
161
  email: cukes@googlegroups.com
162
162
  executables: []
@@ -169,28 +169,33 @@ files:
169
169
  - lib/cucumber/core.rb
170
170
  - lib/cucumber/core/compiler.rb
171
171
  - lib/cucumber/core/event.rb
172
+ - lib/cucumber/core/event/base.rb
173
+ - lib/cucumber/core/event/envelope.rb
174
+ - lib/cucumber/core/event/gherkin_source_parsed.rb
175
+ - lib/cucumber/core/event/test_case_created.rb
176
+ - lib/cucumber/core/event/test_case_finished.rb
177
+ - lib/cucumber/core/event/test_case_started.rb
178
+ - lib/cucumber/core/event/test_step_created.rb
179
+ - lib/cucumber/core/event/test_step_finished.rb
180
+ - lib/cucumber/core/event/test_step_started.rb
172
181
  - lib/cucumber/core/event_bus.rb
173
- - lib/cucumber/core/events.rb
174
- - lib/cucumber/core/events/base.rb
175
- - lib/cucumber/core/events/envelope.rb
176
- - lib/cucumber/core/events/gherkin_source_parsed.rb
177
- - lib/cucumber/core/events/test_case_created.rb
178
- - lib/cucumber/core/events/test_case_finished.rb
179
- - lib/cucumber/core/events/test_case_started.rb
180
- - lib/cucumber/core/events/test_step_created.rb
181
- - lib/cucumber/core/events/test_step_finished.rb
182
- - lib/cucumber/core/events/test_step_started.rb
183
182
  - lib/cucumber/core/filter.rb
184
183
  - lib/cucumber/core/gherkin/document.rb
185
184
  - lib/cucumber/core/gherkin/parser.rb
186
185
  - lib/cucumber/core/gherkin/writer.rb
186
+ - lib/cucumber/core/gherkin/writer/accepts_comments.rb
187
187
  - lib/cucumber/core/gherkin/writer/background.rb
188
188
  - lib/cucumber/core/gherkin/writer/doc_string.rb
189
189
  - lib/cucumber/core/gherkin/writer/example.rb
190
190
  - lib/cucumber/core/gherkin/writer/examples.rb
191
191
  - lib/cucumber/core/gherkin/writer/feature.rb
192
192
  - lib/cucumber/core/gherkin/writer/gherkin.rb
193
+ - lib/cucumber/core/gherkin/writer/has_description.rb
194
+ - lib/cucumber/core/gherkin/writer/has_elements.rb
195
+ - lib/cucumber/core/gherkin/writer/has_options_initializer.rb
196
+ - lib/cucumber/core/gherkin/writer/has_rows.rb
193
197
  - lib/cucumber/core/gherkin/writer/helpers.rb
198
+ - lib/cucumber/core/gherkin/writer/indentation.rb
194
199
  - lib/cucumber/core/gherkin/writer/rule.rb
195
200
  - lib/cucumber/core/gherkin/writer/scenario.rb
196
201
  - lib/cucumber/core/gherkin/writer/scenario_outline.rb
@@ -255,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
260
  - !ruby/object:Gem::Version
256
261
  version: 3.2.8
257
262
  requirements: []
258
- rubygems_version: 4.0.10
263
+ rubygems_version: 4.0.16
259
264
  specification_version: 4
260
- summary: cucumber-core-17.0.0
265
+ summary: cucumber-core-19.0.0
261
266
  test_files: []
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'events/envelope'
4
- require_relative 'events/gherkin_source_parsed'
5
- require_relative 'events/test_case_created'
6
- require_relative 'events/test_case_started'
7
- require_relative 'events/test_case_finished'
8
- require_relative 'events/test_step_created'
9
- require_relative 'events/test_step_started'
10
- require_relative 'events/test_step_finished'
11
-
12
- module Cucumber
13
- module Core
14
- module Events
15
- # The registry contains all the events registered in the core, that will be used by the {EventBus} by default.
16
- def self.registry
17
- build_registry(
18
- Envelope,
19
- GherkinSourceParsed,
20
- TestCaseCreated,
21
- TestCaseStarted,
22
- TestCaseFinished,
23
- TestStepCreated,
24
- TestStepStarted,
25
- TestStepFinished
26
- )
27
- end
28
-
29
- # Build an event registry to be passed to the {EventBus} constructor from a list of types.
30
- # Each type must respond to `event_id` so that it can be added to the registry hash
31
- #
32
- # @return [Hash{Symbol => Class}]
33
- def self.build_registry(*types)
34
- types.to_h { |type| [type.event_id, type] }
35
- end
36
- end
37
- end
38
- end