cucumber 3.1.2 → 7.1.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 (118) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1780 -1146
  3. data/CONTRIBUTING.md +224 -61
  4. data/README.md +144 -22
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +46 -53
  7. data/lib/cucumber/cli/configuration.rb +28 -6
  8. data/lib/cucumber/cli/main.rb +12 -12
  9. data/lib/cucumber/cli/options.rb +103 -77
  10. data/lib/cucumber/cli/profile_loader.rb +49 -26
  11. data/lib/cucumber/configuration.rb +44 -29
  12. data/lib/cucumber/constantize.rb +2 -5
  13. data/lib/cucumber/deprecate.rb +31 -7
  14. data/lib/cucumber/errors.rb +5 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  17. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  18. data/lib/cucumber/events/step_activated.rb +2 -1
  19. data/lib/cucumber/events/test_case_created.rb +13 -0
  20. data/lib/cucumber/events/test_case_ready.rb +12 -0
  21. data/lib/cucumber/events/test_step_created.rb +13 -0
  22. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  23. data/lib/cucumber/events.rb +14 -7
  24. data/lib/cucumber/file_specs.rb +6 -6
  25. data/lib/cucumber/filters/activate_steps.rb +5 -3
  26. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  27. data/lib/cucumber/filters/prepare_world.rb +5 -9
  28. data/lib/cucumber/filters/quit.rb +1 -3
  29. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  30. data/lib/cucumber/filters.rb +1 -0
  31. data/lib/cucumber/formatter/ansicolor.rb +40 -52
  32. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  33. data/lib/cucumber/formatter/backtrace_filter.rb +10 -8
  34. data/lib/cucumber/formatter/console.rb +69 -69
  35. data/lib/cucumber/formatter/console_counts.rb +4 -9
  36. data/lib/cucumber/formatter/console_issues.rb +6 -3
  37. data/lib/cucumber/formatter/duration.rb +1 -1
  38. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  39. data/lib/cucumber/formatter/errors.rb +6 -0
  40. data/lib/cucumber/formatter/fanout.rb +2 -0
  41. data/lib/cucumber/formatter/html.rb +11 -598
  42. data/lib/cucumber/formatter/http_io.rb +147 -0
  43. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  44. data/lib/cucumber/formatter/interceptor.rb +11 -30
  45. data/lib/cucumber/formatter/io.rb +55 -13
  46. data/lib/cucumber/formatter/json.rb +115 -122
  47. data/lib/cucumber/formatter/junit.rb +72 -55
  48. data/lib/cucumber/formatter/message.rb +23 -0
  49. data/lib/cucumber/formatter/message_builder.rb +255 -0
  50. data/lib/cucumber/formatter/pretty.rb +360 -153
  51. data/lib/cucumber/formatter/progress.rb +30 -32
  52. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  53. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  54. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  55. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  56. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  57. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  58. data/lib/cucumber/formatter/rerun.rb +22 -4
  59. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  60. data/lib/cucumber/formatter/steps.rb +8 -6
  61. data/lib/cucumber/formatter/summary.rb +16 -8
  62. data/lib/cucumber/formatter/unicode.rb +15 -17
  63. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  64. data/lib/cucumber/formatter/usage.rb +17 -14
  65. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  66. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  67. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  68. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  69. data/lib/cucumber/glue/dsl.rb +19 -0
  70. data/lib/cucumber/glue/hook.rb +34 -11
  71. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  72. data/lib/cucumber/glue/proto_world.rb +37 -44
  73. data/lib/cucumber/glue/registry_and_more.rb +71 -12
  74. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  75. data/lib/cucumber/glue/snippet.rb +23 -22
  76. data/lib/cucumber/glue/step_definition.rb +42 -20
  77. data/lib/cucumber/glue/world_factory.rb +1 -1
  78. data/lib/cucumber/hooks.rb +11 -11
  79. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
  80. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  81. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  82. data/lib/cucumber/multiline_argument.rb +4 -6
  83. data/lib/cucumber/platform.rb +3 -3
  84. data/lib/cucumber/rake/task.rb +16 -18
  85. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  86. data/lib/cucumber/rspec/doubles.rb +3 -5
  87. data/lib/cucumber/running_test_case.rb +2 -53
  88. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  89. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  90. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  91. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  92. data/lib/cucumber/runtime/support_code.rb +13 -15
  93. data/lib/cucumber/runtime/user_interface.rb +6 -16
  94. data/lib/cucumber/runtime.rb +77 -59
  95. data/lib/cucumber/step_definition_light.rb +4 -3
  96. data/lib/cucumber/step_definitions.rb +2 -2
  97. data/lib/cucumber/step_match.rb +12 -17
  98. data/lib/cucumber/step_match_search.rb +2 -1
  99. data/lib/cucumber/term/ansicolor.rb +9 -9
  100. data/lib/cucumber/term/banner.rb +56 -0
  101. data/lib/cucumber/version +1 -1
  102. data/lib/cucumber.rb +1 -1
  103. metadata +272 -81
  104. data/lib/cucumber/core_ext/string.rb +0 -11
  105. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  106. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  107. data/lib/cucumber/formatter/cucumber.css +0 -286
  108. data/lib/cucumber/formatter/cucumber.sass +0 -247
  109. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  110. data/lib/cucumber/formatter/html_builder.rb +0 -121
  111. data/lib/cucumber/formatter/inline-js.js +0 -30
  112. data/lib/cucumber/formatter/jquery-min.js +0 -154
  113. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  114. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  115. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  116. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  117. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  118. data/lib/cucumber/step_argument.rb +0 -25
@@ -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,
@@ -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
 
@@ -108,26 +104,28 @@ module Cucumber
108
104
  def find_after_step_hooks(test_case)
109
105
  scenario = RunningTestCase.new(test_case)
110
106
  hooks = registry.hooks_for(:after_step, scenario)
111
- StepHooks.new hooks
107
+ StepHooks.new(@configuration.id_generator, hooks, @configuration.event_bus)
112
108
  end
113
109
 
114
110
  def apply_before_hooks(test_case)
111
+ return test_case if test_case.test_steps.empty?
115
112
  scenario = RunningTestCase.new(test_case)
116
113
  hooks = registry.hooks_for(:before, scenario)
117
- BeforeHooks.new(hooks, scenario).apply_to(test_case)
114
+ BeforeHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
118
115
  end
119
116
 
120
117
  def apply_after_hooks(test_case)
118
+ return test_case if test_case.test_steps.empty?
121
119
  scenario = RunningTestCase.new(test_case)
122
120
  hooks = registry.hooks_for(:after, scenario)
123
- AfterHooks.new(hooks, scenario).apply_to(test_case)
121
+ AfterHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
124
122
  end
125
123
 
126
124
  def find_around_hooks(test_case)
127
125
  scenario = RunningTestCase.new(test_case)
128
126
 
129
127
  registry.hooks_for(:around, scenario).map do |hook|
130
- Hooks.around_hook(test_case.source) do |run_scenario|
128
+ Hooks.around_hook do |run_scenario|
131
129
  hook.invoke('Around', scenario, &run_scenario)
132
130
  end
133
131
  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
@@ -48,20 +40,18 @@ module Cucumber
48
40
  # be a path to a file, or if it's an image it may also be a Base64 encoded image.
49
41
  # The embedded data may or may not be ignored, depending on what kind of formatter(s) are active.
50
42
  #
51
- def embed(src, mime_type, label)
52
- @visitor.embed(src, mime_type, label)
43
+ def attach(src, media_type)
44
+ @visitor.attach(src, media_type)
53
45
  end
54
46
 
55
47
  private
56
48
 
57
49
  def mri_gets(timeout_seconds)
58
- begin
59
- Timeout.timeout(timeout_seconds) do
60
- STDIN.gets
61
- end
62
- rescue Timeout::Error
63
- nil
50
+ Timeout.timeout(timeout_seconds) do
51
+ STDIN.gets
64
52
  end
53
+ rescue Timeout::Error
54
+ nil
65
55
  end
66
56
 
67
57
  def jruby_gets(timeout_seconds)
@@ -1,16 +1,19 @@
1
- # -*- coding: utf-8 -*-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'fileutils'
5
- require 'multi_json'
6
4
  require 'cucumber/configuration'
5
+ require 'cucumber/create_meta'
6
+ require 'cucumber/deprecate'
7
7
  require 'cucumber/load_path'
8
8
  require 'cucumber/formatter/duration'
9
9
  require 'cucumber/file_specs'
10
10
  require 'cucumber/filters'
11
11
  require 'cucumber/formatter/fanout'
12
12
  require 'cucumber/gherkin/i18n'
13
+ require 'cucumber/glue/registry_wrapper'
13
14
  require 'cucumber/step_match_search'
15
+ require 'cucumber/messages'
16
+ require 'sys/uname'
14
17
 
15
18
  module Cucumber
16
19
  module FixRuby21Bug9285
@@ -19,8 +22,8 @@ module Cucumber
19
22
  end
20
23
  end
21
24
 
22
- class FileException < Exception
23
- attr :path
25
+ class FileException < RuntimeError
26
+ attr_reader :path
24
27
 
25
28
  def initialize(original_exception, path)
26
29
  super(original_exception)
@@ -31,7 +34,7 @@ module Cucumber
31
34
  class FileNotFoundException < FileException
32
35
  end
33
36
 
34
- class FeatureFolderNotFoundException < Exception
37
+ class FeatureFolderNotFoundException < RuntimeError
35
38
  def initialize(path)
36
39
  @path = path
37
40
  end
@@ -54,7 +57,6 @@ module Cucumber
54
57
  def initialize(configuration = Configuration.default)
55
58
  @configuration = Configuration.new(configuration)
56
59
  @support_code = SupportCode.new(self, @configuration)
57
- @results = Formatter::LegacyApi::Results.new
58
60
  end
59
61
 
60
62
  # Allows you to take an existing runtime and change its configuration
@@ -65,15 +67,23 @@ module Cucumber
65
67
 
66
68
  require 'cucumber/wire/plugin'
67
69
  def run!
70
+ @configuration.notify :envelope, Cucumber::Messages::Envelope.new(
71
+ meta: Cucumber::CreateMeta.create_meta('cucumber-ruby', Cucumber::VERSION)
72
+ )
73
+
68
74
  load_step_definitions
69
- install_wire_plugin
70
75
  fire_after_configuration_hook
76
+ fire_install_plugin_hook
77
+ install_wire_plugin
78
+ fire_before_all_hook unless dry_run?
71
79
  # TODO: can we remove this state?
72
80
  self.visitor = report
73
81
 
74
82
  receiver = Test::Runner.new(@configuration.event_bus)
75
- compile features, receiver, filters
83
+ compile features, receiver, filters, @configuration.event_bus
76
84
  @configuration.notify :test_run_finished
85
+
86
+ fire_after_all_hook unless dry_run?
77
87
  end
78
88
 
79
89
  def features_paths
@@ -84,14 +94,6 @@ module Cucumber
84
94
  @configuration.dry_run?
85
95
  end
86
96
 
87
- def scenarios(status = nil)
88
- @results.scenarios(status)
89
- end
90
-
91
- def steps(status = nil)
92
- @results.steps(status)
93
- end
94
-
95
97
  def unmatched_step_definitions
96
98
  @support_code.unmatched_step_definitions
97
99
  end
@@ -107,16 +109,27 @@ module Cucumber
107
109
  # Returns Ast::DocString for +string_without_triple_quotes+.
108
110
  #
109
111
  def doc_string(string_without_triple_quotes, content_type = '', _line_offset = 0)
110
- location = Core::Ast::Location.of_caller
111
- Core::Ast::DocString.new(string_without_triple_quotes, content_type, location)
112
+ Core::Test::DocString.new(string_without_triple_quotes, content_type)
112
113
  end
113
114
 
114
115
  private
115
116
 
116
- def fire_after_configuration_hook #:nodoc
117
+ def fire_after_configuration_hook #:nodoc:
117
118
  @support_code.fire_hook(:after_configuration, @configuration)
118
119
  end
119
120
 
121
+ def fire_install_plugin_hook #:nodoc:
122
+ @support_code.fire_hook(:install_plugin, @configuration, registry_wrapper)
123
+ end
124
+
125
+ def fire_before_all_hook #:nodoc:
126
+ @support_code.fire_hook(:before_all)
127
+ end
128
+
129
+ def fire_after_all_hook #:nodoc:
130
+ @support_code.fire_hook(:after_all)
131
+ end
132
+
120
133
  require 'cucumber/core/gherkin/document'
121
134
  def features
122
135
  @features ||= feature_files.map do |path|
@@ -143,14 +156,12 @@ module Cucumber
143
156
  end
144
157
 
145
158
  def initialize(path)
146
- begin
147
- @file = File.new(path)
148
- set_encoding
149
- rescue Errno::EACCES => e
150
- raise FileNotFoundException.new(e, File.expand_path(path))
151
- rescue Errno::ENOENT
152
- raise FeatureFolderNotFoundException.new(path)
153
- end
159
+ @file = File.new(path)
160
+ set_encoding
161
+ rescue Errno::EACCES => e
162
+ raise FileNotFoundException.new(e, File.expand_path(path))
163
+ rescue Errno::ENOENT
164
+ raise FeatureFolderNotFoundException, path
154
165
  end
155
166
 
156
167
  def read
@@ -162,7 +173,7 @@ module Cucumber
162
173
  def set_encoding
163
174
  @file.each do |line|
164
175
  if ENCODING_PATTERN =~ line
165
- @file.set_encoding $1
176
+ @file.set_encoding Regexp.last_match(1)
166
177
  break
167
178
  end
168
179
  break unless COMMENT_OR_EMPTY_LINE_PATTERN =~ line
@@ -171,16 +182,16 @@ module Cucumber
171
182
  end
172
183
  end
173
184
 
174
- require 'cucumber/formatter/legacy_api/adapter'
175
- require 'cucumber/formatter/legacy_api/runtime_facade'
176
- require 'cucumber/formatter/legacy_api/results'
177
185
  require 'cucumber/formatter/ignore_missing_messages'
178
186
  require 'cucumber/formatter/fail_fast'
187
+ require 'cucumber/formatter/publish_banner_printer'
179
188
  require 'cucumber/core/report/summary'
189
+
180
190
  def report
181
191
  return @report if @report
182
192
  reports = [summary_report] + formatters
183
193
  reports << fail_fast_report if @configuration.fail_fast?
194
+ reports << publish_banner_printer unless @configuration.publish_quiet?
184
195
  @report ||= Formatter::Fanout.new(reports)
185
196
  end
186
197
 
@@ -192,41 +203,32 @@ module Cucumber
192
203
  @fail_fast_report ||= Formatter::FailFast.new(@configuration)
193
204
  end
194
205
 
206
+ def publish_banner_printer
207
+ @publish_banner_printer ||= Formatter::PublishBannerPrinter.new(@configuration)
208
+ end
209
+
195
210
  def formatters
196
211
  @formatters ||=
197
- @configuration.formatter_factories do |factory, formatter_options, path_or_io, options|
198
- create_formatter(factory, formatter_options, path_or_io, options)
212
+ @configuration.formatter_factories do |factory, formatter_options, path_or_io|
213
+ create_formatter(factory, formatter_options, path_or_io)
199
214
  end
200
215
  end
201
216
 
202
- def create_formatter(factory, formatter_options, path_or_io, cli_options)
203
- if !legacy_formatter?(factory)
204
- if accept_options?(factory)
205
- return factory.new(@configuration, formatter_options) if path_or_io.nil?
206
- return factory.new(@configuration.with_options(out_stream: path_or_io),
207
- formatter_options)
208
- else
209
- return factory.new(@configuration) if path_or_io.nil?
210
- return factory.new(@configuration.with_options(out_stream: path_or_io))
211
- end
217
+ def create_formatter(factory, formatter_options, path_or_io)
218
+ if accept_options?(factory)
219
+ return factory.new(@configuration, formatter_options) if path_or_io.nil?
220
+ factory.new(@configuration.with_options(out_stream: path_or_io),
221
+ formatter_options)
222
+ else
223
+ return factory.new(@configuration) if path_or_io.nil?
224
+ factory.new(@configuration.with_options(out_stream: path_or_io))
212
225
  end
213
- results = Formatter::LegacyApi::Results.new
214
- runtime_facade = Formatter::LegacyApi::RuntimeFacade.new(results, @support_code, @configuration)
215
- formatter = factory.new(runtime_facade, path_or_io, cli_options)
216
- Formatter::LegacyApi::Adapter.new(
217
- Formatter::IgnoreMissingMessages.new(formatter),
218
- results, @configuration
219
- )
220
226
  end
221
227
 
222
228
  def accept_options?(factory)
223
229
  factory.instance_method(:initialize).arity > 1
224
230
  end
225
231
 
226
- def legacy_formatter?(factory)
227
- factory.instance_method(:initialize).arity > 2
228
- end
229
-
230
232
  def failure?
231
233
  if @configuration.wip?
232
234
  summary_report.test_cases.total_passed > 0
@@ -237,7 +239,7 @@ module Cucumber
237
239
  public :failure?
238
240
 
239
241
  require 'cucumber/core/test/filters'
240
- def filters
242
+ def filters # rubocop:disable Metrics/AbcSize
241
243
  tag_expressions = @configuration.tag_expressions
242
244
  name_regexps = @configuration.name_regexps
243
245
  tag_limits = @configuration.tag_limits
@@ -250,14 +252,18 @@ module Cucumber
250
252
  # TODO: can we just use Glue::RegistryAndMore's step definitions directly?
251
253
  step_match_search = StepMatchSearch.new(@support_code.registry.method(:step_matches), @configuration)
252
254
  filters << Filters::ActivateSteps.new(step_match_search, @configuration)
253
- @configuration.filters.each do |filter|
254
- filters << filter
255
- end
255
+ @configuration.filters.each { |filter| filters << filter }
256
+
256
257
  unless configuration.dry_run?
257
258
  filters << Filters::ApplyAfterStepHooks.new(@support_code)
258
259
  filters << Filters::ApplyBeforeHooks.new(@support_code)
259
260
  filters << Filters::ApplyAfterHooks.new(@support_code)
260
261
  filters << Filters::ApplyAroundHooks.new(@support_code)
262
+ end
263
+
264
+ filters << Filters::BroadcastTestCaseReadyEvent.new(@configuration)
265
+
266
+ unless configuration.dry_run?
261
267
  filters << Filters::BroadcastTestRunStartedEvent.new(@configuration)
262
268
  filters << Filters::Quit.new
263
269
  filters << Filters::Retry.new(@configuration)
@@ -273,7 +279,19 @@ module Cucumber
273
279
  end
274
280
 
275
281
  def install_wire_plugin
276
- Cucumber::Wire::Plugin.new(@configuration).install if @configuration.all_files_to_load.any? { |f| f =~ %r{\.wire$} }
282
+ return if Cucumber::Wire::Plugin.installed?
283
+ return unless @configuration.all_files_to_load.any? { |f| f =~ /\.wire$/ }
284
+
285
+ Cucumber::Wire::Plugin.new(@configuration, registry_wrapper).install
286
+ Cucumber.deprecate(
287
+ 'See https://github.com/cucumber/cucumber-ruby/blob/main/UPGRADING.md#upgrading-to-710 for more info',
288
+ ' built-in usage of the wire protocol',
289
+ '8.0.0'
290
+ )
291
+ end
292
+
293
+ def registry_wrapper
294
+ Cucumber::Glue::RegistryWrapper.new(@support_code.registry)
277
295
  end
278
296
 
279
297
  def log
@@ -9,11 +9,12 @@ module Cucumber
9
9
  attr_reader :regexp_source, :location
10
10
 
11
11
  def initialize(regexp_source, location)
12
- @regexp_source, @location = regexp_source, location
12
+ @regexp_source = regexp_source
13
+ @location = location
13
14
  end
14
15
 
15
- def eql?(o)
16
- regexp_source == o.regexp_source && location == o.location
16
+ def eql?(other)
17
+ regexp_source == other.regexp_source && location == other.location
17
18
  end
18
19
 
19
20
  def hash
@@ -8,8 +8,8 @@ module Cucumber
8
8
  @support_code.load_files_from_paths(configuration.autoload_code_paths)
9
9
  end
10
10
 
11
- def to_json
12
- @support_code.step_definitions.map(&:to_hash).to_json
11
+ def to_json(obj = nil)
12
+ @support_code.step_definitions.map(&:to_hash).to_json(obj)
13
13
  end
14
14
  end
15
15
  end
@@ -9,7 +9,9 @@ module Cucumber
9
9
 
10
10
  def initialize(step_definition, step_name, step_arguments)
11
11
  raise "step_arguments can't be nil (but it can be an empty array)" if step_arguments.nil?
12
- @step_definition, @name_to_match, @step_arguments = step_definition, step_name, step_arguments
12
+ @step_definition = step_definition
13
+ @name_to_match = step_name
14
+ @step_arguments = step_arguments
13
15
  end
14
16
 
15
17
  def args
@@ -21,12 +23,12 @@ module Cucumber
21
23
 
22
24
  def activate(test_step)
23
25
  test_step.with_action(@step_definition.location) do
24
- invoke(MultilineArgument.from_core(test_step.source.last.multiline_arg))
26
+ invoke(MultilineArgument.from_core(test_step.multiline_arg))
25
27
  end
26
28
  end
27
29
 
28
30
  def invoke(multiline_arg)
29
- all_args = deep_clone_args
31
+ all_args = args
30
32
  multiline_arg.append_to(all_args)
31
33
  @step_definition.invoke(all_args)
32
34
  end
@@ -46,7 +48,7 @@ module Cucumber
46
48
  #
47
49
  # lambda { |param| "[#{param}]" }
48
50
  #
49
- def format_args(format = lambda { |a| a }, &proc)
51
+ def format_args(format = ->(a) { a }, &proc)
50
52
  replace_arguments(@name_to_match, @step_arguments, format, &proc)
51
53
  end
52
54
 
@@ -75,7 +77,7 @@ module Cucumber
75
77
 
76
78
  replacement = if block_given?
77
79
  yield(group.value)
78
- elsif Proc === format
80
+ elsif Proc == format.class
79
81
  format.call(group.value)
80
82
  else
81
83
  format % group.value
@@ -91,17 +93,11 @@ module Cucumber
91
93
  def inspect #:nodoc:
92
94
  "#<#{self.class}: #{location}>"
93
95
  end
94
-
95
- private
96
-
97
- def deep_clone_args
98
- Marshal.load( Marshal.dump( args ) )
99
- end
100
96
  end
101
97
 
102
98
  class SkippingStepMatch
103
99
  def activate(test_step)
104
- return test_step.with_action { raise Core::Test::Result::Skipped.new }
100
+ test_step.with_action { raise Core::Test::Result::Skipped }
105
101
  end
106
102
  end
107
103
 
@@ -123,8 +119,7 @@ module Cucumber
123
119
  end
124
120
 
125
121
  def file_colon_line
126
- raise "No file:line for #{@step}" unless @step.file_colon_line
127
- @step.file_colon_line
122
+ location.to_s
128
123
  end
129
124
 
130
125
  def backtrace_line
@@ -132,7 +127,7 @@ module Cucumber
132
127
  end
133
128
 
134
129
  def text_length
135
- @step.text_length
130
+ @step.text.length
136
131
  end
137
132
 
138
133
  def step_arguments
@@ -141,7 +136,7 @@ module Cucumber
141
136
 
142
137
  def activate(test_step)
143
138
  # noop
144
- return test_step
139
+ test_step
145
140
  end
146
141
  end
147
142
 
@@ -151,7 +146,7 @@ module Cucumber
151
146
  end
152
147
 
153
148
  def activate(test_step)
154
- return test_step.with_action { raise @error }
149
+ test_step.with_action { raise @error }
155
150
  end
156
151
  end
157
152
  end
@@ -13,7 +13,8 @@ module Cucumber
13
13
 
14
14
  class AssertUnambiguousMatch
15
15
  def initialize(search, configuration)
16
- @search, @configuration = search, configuration
16
+ @search = search
17
+ @configuration = configuration
17
18
  end
18
19
 
19
20
  def call(step_name)
@@ -35,7 +35,7 @@ module Cucumber
35
35
  [:on_magenta, 45],
36
36
  [:on_cyan, 46],
37
37
  [:on_white, 47]
38
- ]
38
+ ].freeze
39
39
 
40
40
  ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
41
41
  # :startdoc:
@@ -54,8 +54,9 @@ module Cucumber
54
54
  end
55
55
  self.coloring = true
56
56
 
57
+ # rubocop:disable Security/Eval
57
58
  ATTRIBUTES.each do |c, v|
58
- eval %Q{
59
+ eval <<-END_EVAL, binding, __FILE__, __LINE__ + 1
59
60
  def #{c}(string = nil)
60
61
  result = String.new
61
62
  result << "\e[#{v}m" if Cucumber::Term::ANSIColor.coloring?
@@ -71,23 +72,23 @@ module Cucumber
71
72
  result << "\e[0m" if Cucumber::Term::ANSIColor.coloring?
72
73
  result
73
74
  end
74
- }
75
+ END_EVAL
75
76
  end
77
+ # rubocop:enable Security/Eval
76
78
 
77
79
  # Regular expression that is used to scan for ANSI-sequences while
78
80
  # uncoloring strings.
79
81
  COLORED_REGEXP = /\e\[(?:[34][0-7]|[0-9])?m/
80
82
 
81
83
  def self.included(klass)
82
- if klass == String
83
- ATTRIBUTES.delete(:clear)
84
- ATTRIBUTE_NAMES.delete(:clear)
85
- end
84
+ return unless klass == String
85
+ ATTRIBUTES.delete(:clear)
86
+ ATTRIBUTE_NAMES.delete(:clear)
86
87
  end
87
88
 
88
89
  # Returns an uncolored version of the string, that is all
89
90
  # ANSI-sequences are stripped from the string.
90
- def uncolored(string = nil) # :yields:
91
+ def uncolored(string = nil)
91
92
  if block_given?
92
93
  yield.gsub(COLORED_REGEXP, '')
93
94
  elsif string
@@ -105,7 +106,6 @@ module Cucumber
105
106
  def attributes
106
107
  ATTRIBUTE_NAMES
107
108
  end
108
- extend self
109
109
  end
110
110
  end
111
111
  end
@@ -0,0 +1,56 @@
1
+ require 'cucumber/term/ansicolor'
2
+
3
+ module Cucumber
4
+ module Term
5
+ module Banner
6
+ def display_banner(lines, io, border_modifiers = nil)
7
+ BannerMaker.new.display_banner(lines, io, border_modifiers || %i[green bold])
8
+ end
9
+
10
+ class BannerMaker
11
+ include Term::ANSIColor
12
+
13
+ def display_banner(lines, io, border_modifiers)
14
+ lines = lines.split("\n") if lines.is_a? String
15
+ longest_line_length = lines.map { |line| line_length(line) }.max
16
+
17
+ io.puts apply_modifiers("┌#{'─' * (longest_line_length + 2)}┐", border_modifiers)
18
+ lines.map do |line|
19
+ padding = ' ' * (longest_line_length - line_length(line))
20
+ io.puts "#{apply_modifiers('│', border_modifiers)} #{display_line(line)}#{padding} #{apply_modifiers('│', border_modifiers)}"
21
+ end
22
+ io.puts apply_modifiers("└#{'─' * (longest_line_length + 2)}┘", border_modifiers)
23
+ end
24
+
25
+ private
26
+
27
+ def display_line(line)
28
+ line.is_a?(Array) ? line.map { |span| display_span(span) }.join : line
29
+ end
30
+
31
+ def display_span(span)
32
+ return apply_modifiers(span.shift, span) if span.is_a?(Array)
33
+ span
34
+ end
35
+
36
+ def apply_modifiers(str, modifiers)
37
+ display = str
38
+ modifiers.each { |modifier| display = send(modifier, display) }
39
+ display
40
+ end
41
+
42
+ def line_length(line)
43
+ if line.is_a?(Array)
44
+ line.map { |span| span_length(span) }.sum
45
+ else
46
+ line.length
47
+ end
48
+ end
49
+
50
+ def span_length(span)
51
+ span.is_a?(Array) ? span[0].length : span.length
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end