cucumber 3.1.2 → 7.1.0

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