cucumber 3.1.2 → 8.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 (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1880 -1146
  3. data/CONTRIBUTING.md +220 -61
  4. data/README.md +143 -22
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +49 -53
  7. data/lib/autotest/discover.rb +3 -2
  8. data/lib/cucumber/cli/configuration.rb +32 -7
  9. data/lib/cucumber/cli/main.rb +16 -15
  10. data/lib/cucumber/cli/options.rb +111 -79
  11. data/lib/cucumber/cli/profile_loader.rb +45 -26
  12. data/lib/cucumber/cli/rerun_file.rb +1 -1
  13. data/lib/cucumber/configuration.rb +47 -31
  14. data/lib/cucumber/constantize.rb +3 -6
  15. data/lib/cucumber/deprecate.rb +32 -7
  16. data/lib/cucumber/errors.rb +5 -7
  17. data/lib/cucumber/events/envelope.rb +9 -0
  18. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  19. data/lib/cucumber/events/hook_test_step_created.rb +12 -0
  20. data/lib/cucumber/events/step_activated.rb +0 -5
  21. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  22. data/lib/cucumber/events/test_case_created.rb +12 -0
  23. data/lib/cucumber/events/test_case_ready.rb +12 -0
  24. data/lib/cucumber/events/test_run_finished.rb +2 -1
  25. data/lib/cucumber/events/test_step_created.rb +12 -0
  26. data/lib/cucumber/events/undefined_parameter_type.rb +9 -0
  27. data/lib/cucumber/events.rb +15 -8
  28. data/lib/cucumber/file_specs.rb +8 -7
  29. data/lib/cucumber/filters/activate_steps.rb +6 -3
  30. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  31. data/lib/cucumber/filters/prepare_world.rb +5 -9
  32. data/lib/cucumber/filters/quit.rb +1 -3
  33. data/lib/cucumber/filters/tag_limits/verifier.rb +3 -7
  34. data/lib/cucumber/filters/tag_limits.rb +1 -3
  35. data/lib/cucumber/filters.rb +1 -0
  36. data/lib/cucumber/formatter/ansicolor.rb +74 -86
  37. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  38. data/lib/cucumber/formatter/backtrace_filter.rb +10 -7
  39. data/lib/cucumber/formatter/console.rb +76 -68
  40. data/lib/cucumber/formatter/console_counts.rb +4 -9
  41. data/lib/cucumber/formatter/console_issues.rb +12 -4
  42. data/lib/cucumber/formatter/duration.rb +1 -1
  43. data/lib/cucumber/formatter/duration_extractor.rb +4 -1
  44. data/lib/cucumber/formatter/errors.rb +7 -0
  45. data/lib/cucumber/formatter/fanout.rb +3 -1
  46. data/lib/cucumber/formatter/html.rb +11 -598
  47. data/lib/cucumber/formatter/http_io.rb +152 -0
  48. data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -2
  49. data/lib/cucumber/formatter/interceptor.rb +11 -30
  50. data/lib/cucumber/formatter/io.rb +57 -13
  51. data/lib/cucumber/formatter/json.rb +119 -124
  52. data/lib/cucumber/formatter/junit.rb +75 -55
  53. data/lib/cucumber/formatter/message.rb +23 -0
  54. data/lib/cucumber/formatter/message_builder.rb +256 -0
  55. data/lib/cucumber/formatter/pretty.rb +370 -153
  56. data/lib/cucumber/formatter/progress.rb +31 -32
  57. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  58. data/lib/cucumber/formatter/query/hook_by_test_step.rb +32 -0
  59. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  60. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  61. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  62. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +42 -0
  63. data/lib/cucumber/formatter/rerun.rb +24 -4
  64. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  65. data/lib/cucumber/formatter/steps.rb +8 -6
  66. data/lib/cucumber/formatter/summary.rb +17 -8
  67. data/lib/cucumber/formatter/unicode.rb +18 -20
  68. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  69. data/lib/cucumber/formatter/usage.rb +18 -15
  70. data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
  71. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +14 -18
  72. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  73. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  74. data/lib/cucumber/glue/dsl.rb +29 -15
  75. data/lib/cucumber/glue/hook.rb +37 -11
  76. data/lib/cucumber/glue/invoke_in_world.rb +17 -22
  77. data/lib/cucumber/glue/proto_world.rb +47 -53
  78. data/lib/cucumber/glue/registry_and_more.rb +62 -17
  79. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  80. data/lib/cucumber/glue/snippet.rb +23 -22
  81. data/lib/cucumber/glue/step_definition.rb +48 -23
  82. data/lib/cucumber/glue/world_factory.rb +1 -1
  83. data/lib/cucumber/hooks.rb +12 -11
  84. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +4 -3
  85. data/lib/cucumber/multiline_argument/data_table.rb +143 -123
  86. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  87. data/lib/cucumber/multiline_argument.rb +4 -6
  88. data/lib/cucumber/platform.rb +5 -5
  89. data/lib/cucumber/rake/task.rb +34 -25
  90. data/lib/cucumber/rspec/disable_option_parser.rb +15 -11
  91. data/lib/cucumber/rspec/doubles.rb +3 -5
  92. data/lib/cucumber/running_test_case.rb +3 -53
  93. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  94. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  95. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  96. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  97. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  98. data/lib/cucumber/runtime/support_code.rb +16 -15
  99. data/lib/cucumber/runtime/user_interface.rb +10 -19
  100. data/lib/cucumber/runtime.rb +78 -76
  101. data/lib/cucumber/step_definition_light.rb +4 -3
  102. data/lib/cucumber/step_definitions.rb +2 -2
  103. data/lib/cucumber/step_match.rb +17 -20
  104. data/lib/cucumber/step_match_search.rb +5 -3
  105. data/lib/cucumber/term/ansicolor.rb +72 -48
  106. data/lib/cucumber/term/banner.rb +57 -0
  107. data/lib/cucumber/version +1 -1
  108. data/lib/cucumber.rb +3 -2
  109. data/lib/simplecov_setup.rb +1 -1
  110. metadata +279 -81
  111. data/lib/cucumber/core_ext/string.rb +0 -11
  112. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  113. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  114. data/lib/cucumber/formatter/cucumber.css +0 -286
  115. data/lib/cucumber/formatter/cucumber.sass +0 -247
  116. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  117. data/lib/cucumber/formatter/html_builder.rb +0 -121
  118. data/lib/cucumber/formatter/inline-js.js +0 -30
  119. data/lib/cucumber/formatter/jquery-min.js +0 -154
  120. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  121. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  122. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  123. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  124. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  125. data/lib/cucumber/step_argument.rb +0 -25
@@ -5,9 +5,7 @@ require 'cucumber/gherkin/formatter/ansi_escapes'
5
5
  begin
6
6
  # Support Rake > 0.8.7
7
7
  require 'rake/dsl_definition'
8
- # rubocop:disable Lint/HandleExceptions
9
8
  rescue LoadError
10
- # rubocop:enable Lint/HandleExceptions
11
9
  end
12
10
 
13
11
  module Cucumber
@@ -32,14 +30,15 @@ module Cucumber
32
30
  include Cucumber::Gherkin::Formatter::AnsiEscapes
33
31
  include ::Rake::DSL if defined?(::Rake::DSL)
34
32
 
35
- class InProcessCucumberRunner #:nodoc:
33
+ class InProcessCucumberRunner # :nodoc:
36
34
  include ::Rake::DSL if defined?(::Rake::DSL)
37
35
 
38
36
  attr_reader :args
39
37
 
40
38
  def initialize(libs, cucumber_opts, feature_files)
41
- raise 'libs must be an Array when running in-process' unless Array === libs
42
- libs.reverse.each { |lib| $LOAD_PATH.unshift(lib) }
39
+ raise 'libs must be an Array when running in-process' unless libs.instance_of? Array
40
+
41
+ libs.reverse_each { |lib| $LOAD_PATH.unshift(lib) }
43
42
  @args = (
44
43
  cucumber_opts +
45
44
  feature_files
@@ -53,7 +52,7 @@ module Cucumber
53
52
  end
54
53
  end
55
54
 
56
- class ForkedCucumberRunner #:nodoc:
55
+ class ForkedCucumberRunner # :nodoc:
57
56
  include ::Rake::DSL if defined?(::Rake::DSL)
58
57
 
59
58
  def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files)
@@ -65,11 +64,11 @@ module Cucumber
65
64
  end
66
65
 
67
66
  def load_path
68
- [format('"%s"', @libs.join(File::PATH_SEPARATOR))]
67
+ [format('"%<path>s"', path: @libs.join(File::PATH_SEPARATOR))]
69
68
  end
70
69
 
71
70
  def quoted_binary(cucumber_bin)
72
- [format('"%s"', cucumber_bin)]
71
+ [format('"%<path>s"', path: cucumber_bin)]
73
72
  end
74
73
 
75
74
  def use_bundler
@@ -98,9 +97,7 @@ module Cucumber
98
97
 
99
98
  def run
100
99
  sh cmd.join(' ') do |ok, res|
101
- if !ok
102
- exit res.exitstatus
103
- end
100
+ exit res.exitstatus unless ok
104
101
  end
105
102
  end
106
103
  end
@@ -113,9 +110,18 @@ module Cucumber
113
110
 
114
111
  # Extra options to pass to the cucumber binary. Can be overridden by the CUCUMBER_OPTS environment variable.
115
112
  # It's recommended to pass an Array, but if it's a String it will be #split by ' '.
116
- attr_accessor :cucumber_opts
117
- def cucumber_opts=(opts) #:nodoc:
118
- @cucumber_opts = String === opts ? opts.split(' ') : opts
113
+ attr_reader :cucumber_opts
114
+
115
+ def cucumber_opts=(opts) # :nodoc:
116
+ unless opts.instance_of? String
117
+ @cucumber_opts = opts
118
+ return
119
+ end
120
+
121
+ @cucumber_opts = opts.split(' ')
122
+ return if @cucumber_opts.length <= 1
123
+
124
+ $stderr.puts 'WARNING: consider using an array rather than a space-delimited string with cucumber_opts to avoid undesired behavior.'
119
125
  end
120
126
 
121
127
  # Whether or not to fork a new ruby interpreter. Defaults to true. You may gain
@@ -134,38 +140,41 @@ module Cucumber
134
140
  # Note that this attribute has no effect if you don't run in forked mode.
135
141
  attr_accessor :bundler
136
142
 
143
+ # Name of the running task
144
+ attr_reader :task_name
145
+
137
146
  # Define Cucumber Rake task
138
147
  def initialize(task_name = 'cucumber', desc = 'Run Cucumber features')
139
- @task_name, @desc = task_name, desc
148
+ @task_name = task_name
149
+ @desc = desc
140
150
  @fork = true
141
151
  @libs = ['lib']
142
- @rcov_opts = %w{--rails --exclude osx\/objc,gems\/}
152
+ @rcov_opts = %w[--rails --exclude osx\/objc,gems\/]
143
153
  yield self if block_given?
144
154
  @binary = binary.nil? ? Cucumber::BINARY : File.expand_path(binary)
145
155
  define_task
146
156
  end
147
157
 
148
- def define_task #:nodoc:
158
+ def define_task # :nodoc:
149
159
  desc @desc
150
160
  task @task_name do
151
161
  runner.run
152
162
  end
153
163
  end
154
164
 
155
- def runner(_task_args = nil) #:nodoc:
165
+ def runner(_task_args = nil) # :nodoc:
156
166
  cucumber_opts = [(ENV['CUCUMBER_OPTS'] ? ENV['CUCUMBER_OPTS'].split(/\s+/) : nil) || cucumber_opts_with_profile]
157
- if @fork
158
- return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files)
159
- end
167
+ return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
168
+
160
169
  InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
161
170
  end
162
171
 
163
- def cucumber_opts_with_profile #:nodoc:
164
- Array(cucumber_opts).concat Array(@profile).flat_map { |p| ['--profile', p] }
172
+ def cucumber_opts_with_profile # :nodoc:
173
+ Array(cucumber_opts).concat(Array(@profile).flat_map { |p| ['--profile', p] })
165
174
  end
166
175
 
167
- def feature_files #:nodoc:
168
- make_command_line_safe(FileList[ ENV['FEATURE'] || [] ])
176
+ def feature_files # :nodoc:
177
+ make_command_line_safe(FileList[ENV['FEATURE'] || []])
169
178
  end
170
179
 
171
180
  def make_command_line_safe(list)
@@ -2,23 +2,27 @@
2
2
 
3
3
  require 'optparse'
4
4
 
5
- module Spec #:nodoc:
6
- module Runner #:nodoc:
5
+ module Spec # :nodoc:
6
+ module Runner # :nodoc:
7
7
  # Neuters RSpec's option parser.
8
8
  # (RSpec's option parser tries to parse ARGV, which
9
9
  # will fail when running cucumber)
10
- class OptionParser < ::OptionParser #:nodoc:
10
+ class OptionParser < ::OptionParser # :nodoc:
11
11
  NEUTERED_RSPEC = Object.new
12
- def NEUTERED_RSPEC.method_missing(_m, *_args); self; end
12
+ def NEUTERED_RSPEC.method_missing(_method, *_args) # rubocop:disable Style/MissingRespondToMissing
13
+ self || super
14
+ end
15
+
16
+ def self.method_added(method)
17
+ return if @__neutering_rspec
13
18
 
14
- def self.method_added(m)
15
- unless @__neutering_rspec
16
- @__neutering_rspec = true
17
- define_method(m) do |*a|
18
- NEUTERED_RSPEC
19
- end
20
- @__neutering_rspec = false
19
+ @__neutering_rspec = true
20
+ define_method(method) do |*_a|
21
+ NEUTERED_RSPEC
21
22
  end
23
+ @__neutering_rspec = false
24
+
25
+ super
22
26
  end
23
27
  end
24
28
  end
@@ -13,9 +13,7 @@ Before do
13
13
  end
14
14
 
15
15
  After do
16
- begin
17
- RSpec::Mocks.verify
18
- ensure
19
- RSpec::Mocks.teardown
20
- end
16
+ RSpec::Mocks.verify
17
+ ensure
18
+ RSpec::Mocks.teardown
21
19
  end
@@ -15,45 +15,12 @@ module Cucumber
15
15
  # the passed / failed / undefined / skipped status of
16
16
  # the test case.
17
17
  #
18
- # The test case might come from a regular Scenario or
19
- # a Scenario outline. You can call the `#outline?`
20
- # predicate to find out. If it's from an outline,
21
- # you get a couple of extra methods.
22
18
  module RunningTestCase
23
19
  def self.new(test_case)
24
- Builder.new(test_case).running_test_case
20
+ TestCase.new(test_case)
25
21
  end
26
22
 
27
- class Builder
28
- def initialize(test_case)
29
- @test_case = test_case
30
- test_case.describe_source_to(self)
31
- end
32
-
33
- def feature(feature)
34
- end
35
-
36
- def scenario(_scenario)
37
- @factory = Scenario
38
- end
39
-
40
- def scenario_outline(_scenario)
41
- @factory = ScenarioOutlineExample
42
- end
43
-
44
- def examples_table(examples_table)
45
- end
46
-
47
- def examples_table_row(row)
48
- end
49
-
50
- def running_test_case
51
- @factory.new(@test_case)
52
- end
53
- end
54
- private_constant :Builder
55
-
56
- class Scenario < SimpleDelegator
23
+ class TestCase < SimpleDelegator
57
24
  def initialize(test_case, result = Core::Test::Result::Unknown.new)
58
25
  @test_case = test_case
59
26
  @result = result
@@ -66,6 +33,7 @@ module Cucumber
66
33
 
67
34
  def exception
68
35
  return unless @result.failed?
36
+
69
37
  @result.exception
70
38
  end
71
39
 
@@ -85,27 +53,9 @@ module Cucumber
85
53
  tags.map &:name
86
54
  end
87
55
 
88
- def outline?
89
- false
90
- end
91
-
92
56
  def with_result(result)
93
57
  self.class.new(@test_case, result)
94
58
  end
95
59
  end
96
-
97
- class ScenarioOutlineExample < Scenario
98
- def outline?
99
- true
100
- end
101
-
102
- def scenario_outline
103
- self
104
- end
105
-
106
- def cell_values
107
- source.last.values
108
- end
109
- end
110
60
  end
111
61
  end
@@ -3,23 +3,27 @@
3
3
  module Cucumber
4
4
  class Runtime
5
5
  class AfterHooks
6
- def initialize(hooks, scenario)
6
+ def initialize(id_generator, hooks, scenario, event_bus)
7
7
  @hooks = hooks
8
8
  @scenario = scenario
9
+ @id_generator = id_generator
10
+ @event_bus = event_bus
9
11
  end
10
12
 
11
13
  def apply_to(test_case)
12
14
  test_case.with_steps(
13
- test_case.test_steps + after_hooks(test_case.source).reverse
15
+ test_case.test_steps + after_hooks.reverse
14
16
  )
15
17
  end
16
18
 
17
19
  private
18
20
 
19
- def after_hooks(source)
21
+ def after_hooks
20
22
  @hooks.map do |hook|
21
23
  action = ->(result) { hook.invoke('After', @scenario.with_result(result)) }
22
- Hooks.after_hook(source, hook.location, &action)
24
+ hook_step = Hooks.after_hook(@id_generator.new_id, hook.location, &action)
25
+ @event_bus.hook_test_step_created(hook_step, hook)
26
+ hook_step
23
27
  end
24
28
  end
25
29
  end
@@ -5,23 +5,27 @@ require 'cucumber/hooks'
5
5
  module Cucumber
6
6
  class Runtime
7
7
  class BeforeHooks
8
- def initialize(hooks, scenario)
8
+ def initialize(id_generator, hooks, scenario, event_bus)
9
9
  @hooks = hooks
10
10
  @scenario = scenario
11
+ @id_generator = id_generator
12
+ @event_bus = event_bus
11
13
  end
12
14
 
13
15
  def apply_to(test_case)
14
16
  test_case.with_steps(
15
- before_hooks(test_case.source) + test_case.test_steps
17
+ before_hooks + test_case.test_steps
16
18
  )
17
19
  end
18
20
 
19
21
  private
20
22
 
21
- def before_hooks(source)
23
+ def before_hooks
22
24
  @hooks.map do |hook|
23
25
  action_block = ->(result) { hook.invoke('Before', @scenario.with_result(result)) }
24
- Hooks.before_hook(source, hook.location, &action_block)
26
+ hook_step = Hooks.before_hook(@id_generator.new_id, hook.location, &action_block)
27
+ @event_bus.hook_test_step_created(hook_step, hook)
28
+ hook_step
25
29
  end
26
30
  end
27
31
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require 'cucumber/core/ast/doc_string'
4
+ require 'cucumber/core/test/doc_string'
5
5
 
6
6
  module Cucumber
7
7
  class Runtime
@@ -15,11 +15,13 @@ module Cucumber
15
15
  attr_reader :support_code
16
16
 
17
17
  def initialize(support_code, user_interface)
18
- @support_code, @user_interface = support_code, user_interface
18
+ @support_code = support_code
19
+ @user_interface = user_interface
19
20
  end
20
21
 
21
22
  def_delegators :@user_interface,
22
23
  :embed,
24
+ :attach,
23
25
  :ask,
24
26
  :puts,
25
27
  :features_paths,
@@ -0,0 +1,106 @@
1
+ require 'cucumber/messages'
2
+ require 'cucumber/ci_environment'
3
+
4
+ module Cucumber
5
+ class Runtime
6
+ # Builder to instanciate a Cucumber::Messages::Meta message filled-in with
7
+ # the runtime meta-data:
8
+ # - protocol version: the version of the Cucumber::Messages protocol
9
+ # - implementation: the name and version of the implementation (e.g. cucumber-ruby 8.0.0)
10
+ # - runtime: the name and version of the runtime (e.g. ruby 3.0.1)
11
+ # - os: the name and version of the operating system (e.g. linux 3.13.0-45-generic)
12
+ # - cpu: the name of the CPU (e.g. x86_64)
13
+ # - ci: informtion about the CI environment if any, including:
14
+ # - name: the name of the CI environment (e.g. Jenkins)
15
+ # - url: the URL of the CI environment (e.g. https://ci.example.com)
16
+ # - build_number: the build number of the CI environment (e.g. 123)
17
+ # - git: the git information of the CI environment if any
18
+ # - remote: the remote of the git repository (e.g. git@github.com:cucumber/cucumber-ruby.git)
19
+ # - revision: the revision of the git repository (e.g. abcdef)
20
+ # - branch: the name of the git branch (e.g. main)
21
+ # - tag: the name of the git tag (e.g. v1.0.0)
22
+ class MetaMessageBuilder
23
+ class << self
24
+ # Builds a Cucumber::Messages::Meta filled-in with the runtime meta-data
25
+ #
26
+ # @param [env] environment data from which the CI information will be
27
+ # retrieved (default ENV). Can be used to mock the environment for
28
+ # testing purpose.
29
+ #
30
+ # @return [Cucumber::Messages::Meta] the meta message
31
+ #
32
+ # @see Cucumber::Runtime::MetaMessageBuilder
33
+ #
34
+ # @example
35
+ # Cucumber::Runtime::MetaMessageBuilder.build_meta_message
36
+ #
37
+ def build_meta_message(env = ENV)
38
+ Cucumber::Messages::Meta.new(
39
+ protocol_version: protocol_version,
40
+ implementation: implementation,
41
+ runtime: runtime,
42
+ os: os,
43
+ cpu: cpu,
44
+ ci: ci(env)
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def protocol_version
51
+ Cucumber::Messages::VERSION
52
+ end
53
+
54
+ def implementation
55
+ Cucumber::Messages::Product.new(
56
+ name: 'cucumber-ruby',
57
+ version: Cucumber::VERSION
58
+ )
59
+ end
60
+
61
+ def runtime
62
+ Cucumber::Messages::Product.new(
63
+ name: RUBY_ENGINE,
64
+ version: RUBY_VERSION
65
+ )
66
+ end
67
+
68
+ def os
69
+ Cucumber::Messages::Product.new(
70
+ name: RbConfig::CONFIG['target_os'],
71
+ version: Sys::Uname.uname.version
72
+ )
73
+ end
74
+
75
+ def cpu
76
+ Cucumber::Messages::Product.new(
77
+ name: RbConfig::CONFIG['target_cpu']
78
+ )
79
+ end
80
+
81
+ def ci(env)
82
+ ci_data = Cucumber::CiEnvironment.detect_ci_environment(env)
83
+ return nil unless ci_data
84
+
85
+ Cucumber::Messages::Ci.new(
86
+ name: ci_data[:name],
87
+ url: ci_data[:url],
88
+ build_number: ci_data[:buildNumber],
89
+ git: git_info(ci_data)
90
+ )
91
+ end
92
+
93
+ def git_info(ci_data)
94
+ return nil unless ci_data[:git]
95
+
96
+ Cucumber::Messages::Git.new(
97
+ remote: ci_data[:git][:remote],
98
+ revision: ci_data[:git][:revision],
99
+ branch: ci_data[:git][:branch],
100
+ tag: ci_data[:git][:tag]
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -3,8 +3,10 @@
3
3
  module Cucumber
4
4
  class Runtime
5
5
  class StepHooks
6
- def initialize(hooks)
6
+ def initialize(id_generator, hooks, event_bus)
7
7
  @hooks = hooks
8
+ @id_generator = id_generator
9
+ @event_bus = event_bus
8
10
  end
9
11
 
10
12
  def apply(test_steps)
@@ -18,7 +20,9 @@ module Cucumber
18
20
  def after_step_hooks(test_step)
19
21
  @hooks.map do |hook|
20
22
  action = ->(*args) { hook.invoke('AfterStep', [args, test_step]) }
21
- Hooks.after_step_hook(test_step.source, hook.location, &action)
23
+ hook_step = Hooks.after_step_hook(@id_generator.new_id, test_step, hook.location, &action)
24
+ @event_bus.hook_test_step_created(hook_step, hook)
25
+ hook_step
22
26
  end
23
27
  end
24
28
  end
@@ -22,19 +22,15 @@ module Cucumber
22
22
  end
23
23
 
24
24
  def step(step)
25
- location = Core::Ast::Location.of_caller
25
+ location = Core::Test::Location.of_caller
26
26
  @support_code.invoke_dynamic_step(step[:text], multiline_arg(step, location))
27
27
  end
28
28
 
29
29
  def multiline_arg(step, location)
30
- argument = step[:argument]
31
-
32
- if argument
33
- if argument[:type] == :DocString
34
- MultilineArgument.from(argument[:content], location, argument[:content_type])
35
- else
36
- MultilineArgument::DataTable.from(argument[:rows].map { |row| row[:cells].map { |cell| cell[:value] } })
37
- end
30
+ if !step[:doc_string].nil?
31
+ MultilineArgument.from(step[:doc_string][:content], location, step[:doc_string][:content_type])
32
+ elsif !step[:data_table].nil?
33
+ MultilineArgument::DataTable.from(step[:data_table][:rows].map { |row| row[:cells].map { |cell| cell[:value] } })
38
34
  else
39
35
  MultilineArgument.from(nil)
40
36
  end
@@ -62,8 +58,8 @@ module Cucumber
62
58
  # Given I have 8 cukes in my belly
63
59
  # Then I should not be thirsty
64
60
  # })
65
- def invoke_dynamic_steps(steps_text, i18n, _location)
66
- parser = Cucumber::Gherkin::StepsParser.new(StepInvoker.new(self), i18n.iso_code)
61
+ def invoke_dynamic_steps(steps_text, iso_code, _location)
62
+ parser = Cucumber::Gherkin::StepsParser.new(StepInvoker.new(self), iso_code)
67
63
  parser.parse(steps_text)
68
64
  end
69
65
 
@@ -76,6 +72,7 @@ module Cucumber
76
72
  def invoke_dynamic_step(step_name, multiline_argument, _location = nil)
77
73
  matches = step_matches(step_name)
78
74
  raise UndefinedDynamicStep, step_name if matches.empty?
75
+
79
76
  matches.first.invoke(multiline_argument)
80
77
  end
81
78
 
@@ -108,26 +105,30 @@ module Cucumber
108
105
  def find_after_step_hooks(test_case)
109
106
  scenario = RunningTestCase.new(test_case)
110
107
  hooks = registry.hooks_for(:after_step, scenario)
111
- StepHooks.new hooks
108
+ StepHooks.new(@configuration.id_generator, hooks, @configuration.event_bus)
112
109
  end
113
110
 
114
111
  def apply_before_hooks(test_case)
112
+ return test_case if test_case.test_steps.empty?
113
+
115
114
  scenario = RunningTestCase.new(test_case)
116
115
  hooks = registry.hooks_for(:before, scenario)
117
- BeforeHooks.new(hooks, scenario).apply_to(test_case)
116
+ BeforeHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
118
117
  end
119
118
 
120
119
  def apply_after_hooks(test_case)
120
+ return test_case if test_case.test_steps.empty?
121
+
121
122
  scenario = RunningTestCase.new(test_case)
122
123
  hooks = registry.hooks_for(:after, scenario)
123
- AfterHooks.new(hooks, scenario).apply_to(test_case)
124
+ AfterHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
124
125
  end
125
126
 
126
127
  def find_around_hooks(test_case)
127
128
  scenario = RunningTestCase.new(test_case)
128
129
 
129
130
  registry.hooks_for(:around, scenario).map do |hook|
130
- Hooks.around_hook(test_case.source) do |run_scenario|
131
+ Hooks.around_hook do |run_scenario|
131
132
  hook.invoke('Around', scenario, &run_scenario)
132
133
  end
133
134
  end
@@ -7,14 +7,6 @@ module Cucumber
7
7
  module UserInterface
8
8
  attr_writer :visitor
9
9
 
10
- # Output +messages+ alongside the formatted output.
11
- # This is an alternative to using Kernel#puts - it will display
12
- # nicer, and in all outputs (in case you use several formatters)
13
- #
14
- def puts(*messages)
15
- @visitor.puts(*messages)
16
- end
17
-
18
10
  # Suspends execution and prompts +question+ to the console (STDOUT).
19
11
  # An operator (manual tester) can then enter a line of text and hit
20
12
  # <ENTER>. The entered text is returned, and both +question+ and
@@ -29,8 +21,8 @@ module Cucumber
29
21
  # that makes a sound before invoking #ask.
30
22
  #
31
23
  def ask(question, timeout_seconds)
32
- STDOUT.puts(question)
33
- STDOUT.flush
24
+ $stdout.puts(question)
25
+ $stdout.flush
34
26
  puts(question)
35
27
 
36
28
  answer = if Cucumber::JRUBY
@@ -40,6 +32,7 @@ module Cucumber
40
32
  end
41
33
 
42
34
  raise("Waited for input for #{timeout_seconds} seconds, then timed out.") unless answer
35
+
43
36
  puts(answer)
44
37
  answer
45
38
  end
@@ -48,26 +41,24 @@ module Cucumber
48
41
  # be a path to a file, or if it's an image it may also be a Base64 encoded image.
49
42
  # The embedded data may or may not be ignored, depending on what kind of formatter(s) are active.
50
43
  #
51
- def embed(src, mime_type, label)
52
- @visitor.embed(src, mime_type, label)
44
+ def attach(src, media_type)
45
+ @visitor.attach(src, media_type)
53
46
  end
54
47
 
55
48
  private
56
49
 
57
50
  def mri_gets(timeout_seconds)
58
- begin
59
- Timeout.timeout(timeout_seconds) do
60
- STDIN.gets
61
- end
62
- rescue Timeout::Error
63
- nil
51
+ Timeout.timeout(timeout_seconds) do
52
+ $stdin.gets
64
53
  end
54
+ rescue Timeout::Error
55
+ nil
65
56
  end
66
57
 
67
58
  def jruby_gets(timeout_seconds)
68
59
  answer = nil
69
60
  t = java.lang.Thread.new do
70
- answer = STDIN.gets
61
+ answer = $stdin.gets
71
62
  end
72
63
  t.start
73
64
  t.join(timeout_seconds * 1000)