cucumber 3.1.2 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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/deprecate'
7
6
  require 'cucumber/load_path'
8
7
  require 'cucumber/formatter/duration'
9
8
  require 'cucumber/file_specs'
10
9
  require 'cucumber/filters'
11
10
  require 'cucumber/formatter/fanout'
12
11
  require 'cucumber/gherkin/i18n'
12
+ require 'cucumber/glue/registry_wrapper'
13
13
  require 'cucumber/step_match_search'
14
+ require 'cucumber/messages'
15
+ require 'cucumber/runtime/meta_message_builder'
16
+ require 'sys/uname'
14
17
 
15
18
  module Cucumber
16
19
  module FixRuby21Bug9285
@@ -19,21 +22,22 @@ 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
- super(original_exception)
27
29
  @path = path
30
+ super(original_exception)
28
31
  end
29
32
  end
30
33
 
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
40
+ super
37
41
  end
38
42
 
39
43
  def message
@@ -54,7 +58,6 @@ module Cucumber
54
58
  def initialize(configuration = Configuration.default)
55
59
  @configuration = Configuration.new(configuration)
56
60
  @support_code = SupportCode.new(self, @configuration)
57
- @results = Formatter::LegacyApi::Results.new
58
61
  end
59
62
 
60
63
  # Allows you to take an existing runtime and change its configuration
@@ -63,17 +66,22 @@ module Cucumber
63
66
  @support_code.configure(@configuration)
64
67
  end
65
68
 
66
- require 'cucumber/wire/plugin'
67
69
  def run!
70
+ @configuration.notify :envelope, Cucumber::Messages::Envelope.new(
71
+ meta: MetaMessageBuilder.build_meta_message
72
+ )
73
+
68
74
  load_step_definitions
69
- install_wire_plugin
70
- fire_after_configuration_hook
75
+ fire_install_plugin_hook
76
+ fire_before_all_hook unless dry_run?
71
77
  # TODO: can we remove this state?
72
78
  self.visitor = report
73
79
 
74
80
  receiver = Test::Runner.new(@configuration.event_bus)
75
- compile features, receiver, filters
76
- @configuration.notify :test_run_finished
81
+ compile features, receiver, filters, @configuration.event_bus
82
+ @configuration.notify :test_run_finished, !failure?
83
+
84
+ fire_after_all_hook unless dry_run?
77
85
  end
78
86
 
79
87
  def features_paths
@@ -84,14 +92,6 @@ module Cucumber
84
92
  @configuration.dry_run?
85
93
  end
86
94
 
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
95
  def unmatched_step_definitions
96
96
  @support_code.unmatched_step_definitions
97
97
  end
@@ -107,14 +107,29 @@ module Cucumber
107
107
  # Returns Ast::DocString for +string_without_triple_quotes+.
108
108
  #
109
109
  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)
110
+ Core::Test::DocString.new(string_without_triple_quotes, content_type)
111
+ end
112
+
113
+ def failure?
114
+ if @configuration.wip?
115
+ summary_report.test_cases.total_passed > 0
116
+ else
117
+ !summary_report.ok?(@configuration.strict)
118
+ end
112
119
  end
113
120
 
114
121
  private
115
122
 
116
- def fire_after_configuration_hook #:nodoc
117
- @support_code.fire_hook(:after_configuration, @configuration)
123
+ def fire_install_plugin_hook # :nodoc:
124
+ @support_code.fire_hook(:install_plugin, @configuration, registry_wrapper)
125
+ end
126
+
127
+ def fire_before_all_hook # :nodoc:
128
+ @support_code.fire_hook(:before_all)
129
+ end
130
+
131
+ def fire_after_all_hook # :nodoc:
132
+ @support_code.fire_hook(:after_all)
118
133
  end
119
134
 
120
135
  require 'cucumber/core/gherkin/document'
@@ -135,22 +150,20 @@ module Cucumber
135
150
  end
136
151
 
137
152
  class NormalisedEncodingFile
138
- COMMENT_OR_EMPTY_LINE_PATTERN = /^\s*#|^\s*$/ #:nodoc:
139
- ENCODING_PATTERN = /^\s*#\s*encoding\s*:\s*([^\s]+)/ #:nodoc:
153
+ COMMENT_OR_EMPTY_LINE_PATTERN = /^\s*#|^\s*$/ # :nodoc:
154
+ ENCODING_PATTERN = /^\s*#\s*encoding\s*:\s*([^\s]+)/ # :nodoc:
140
155
 
141
156
  def self.read(path)
142
157
  new(path).read
143
158
  end
144
159
 
145
160
  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
161
+ @file = File.new(path)
162
+ set_encoding
163
+ rescue Errno::EACCES => e
164
+ raise FileNotFoundException.new(e, File.expand_path(path))
165
+ rescue Errno::ENOENT
166
+ raise FeatureFolderNotFoundException, path
154
167
  end
155
168
 
156
169
  def read
@@ -162,7 +175,7 @@ module Cucumber
162
175
  def set_encoding
163
176
  @file.each do |line|
164
177
  if ENCODING_PATTERN =~ line
165
- @file.set_encoding $1
178
+ @file.set_encoding Regexp.last_match(1)
166
179
  break
167
180
  end
168
181
  break unless COMMENT_OR_EMPTY_LINE_PATTERN =~ line
@@ -171,16 +184,17 @@ module Cucumber
171
184
  end
172
185
  end
173
186
 
174
- require 'cucumber/formatter/legacy_api/adapter'
175
- require 'cucumber/formatter/legacy_api/runtime_facade'
176
- require 'cucumber/formatter/legacy_api/results'
177
187
  require 'cucumber/formatter/ignore_missing_messages'
178
188
  require 'cucumber/formatter/fail_fast'
189
+ require 'cucumber/formatter/publish_banner_printer'
179
190
  require 'cucumber/core/report/summary'
191
+
180
192
  def report
181
193
  return @report if @report
194
+
182
195
  reports = [summary_report] + formatters
183
196
  reports << fail_fast_report if @configuration.fail_fast?
197
+ reports << publish_banner_printer unless @configuration.publish_quiet?
184
198
  @report ||= Formatter::Fanout.new(reports)
185
199
  end
186
200
 
@@ -192,52 +206,36 @@ module Cucumber
192
206
  @fail_fast_report ||= Formatter::FailFast.new(@configuration)
193
207
  end
194
208
 
209
+ def publish_banner_printer
210
+ @publish_banner_printer ||= Formatter::PublishBannerPrinter.new(@configuration)
211
+ end
212
+
195
213
  def formatters
196
214
  @formatters ||=
197
- @configuration.formatter_factories do |factory, formatter_options, path_or_io, options|
198
- create_formatter(factory, formatter_options, path_or_io, options)
215
+ @configuration.formatter_factories do |factory, formatter_options, path_or_io|
216
+ create_formatter(factory, formatter_options, path_or_io)
199
217
  end
200
218
  end
201
219
 
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
220
+ def create_formatter(factory, formatter_options, path_or_io)
221
+ if accept_options?(factory)
222
+ return factory.new(@configuration, formatter_options) if path_or_io.nil?
223
+
224
+ factory.new(@configuration.with_options(out_stream: path_or_io),
225
+ formatter_options)
226
+ else
227
+ return factory.new(@configuration) if path_or_io.nil?
228
+
229
+ factory.new(@configuration.with_options(out_stream: path_or_io))
212
230
  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
231
  end
221
232
 
222
233
  def accept_options?(factory)
223
234
  factory.instance_method(:initialize).arity > 1
224
235
  end
225
236
 
226
- def legacy_formatter?(factory)
227
- factory.instance_method(:initialize).arity > 2
228
- end
229
-
230
- def failure?
231
- if @configuration.wip?
232
- summary_report.test_cases.total_passed > 0
233
- else
234
- !summary_report.ok?(@configuration.strict)
235
- end
236
- end
237
- public :failure?
238
-
239
237
  require 'cucumber/core/test/filters'
240
- def filters
238
+ def filters # rubocop:disable Metrics/AbcSize
241
239
  tag_expressions = @configuration.tag_expressions
242
240
  name_regexps = @configuration.name_regexps
243
241
  tag_limits = @configuration.tag_limits
@@ -250,14 +248,18 @@ module Cucumber
250
248
  # TODO: can we just use Glue::RegistryAndMore's step definitions directly?
251
249
  step_match_search = StepMatchSearch.new(@support_code.registry.method(:step_matches), @configuration)
252
250
  filters << Filters::ActivateSteps.new(step_match_search, @configuration)
253
- @configuration.filters.each do |filter|
254
- filters << filter
255
- end
251
+ @configuration.filters.each { |filter| filters << filter }
252
+
256
253
  unless configuration.dry_run?
257
254
  filters << Filters::ApplyAfterStepHooks.new(@support_code)
258
255
  filters << Filters::ApplyBeforeHooks.new(@support_code)
259
256
  filters << Filters::ApplyAfterHooks.new(@support_code)
260
257
  filters << Filters::ApplyAroundHooks.new(@support_code)
258
+ end
259
+
260
+ filters << Filters::BroadcastTestCaseReadyEvent.new(@configuration)
261
+
262
+ unless configuration.dry_run?
261
263
  filters << Filters::BroadcastTestRunStartedEvent.new(@configuration)
262
264
  filters << Filters::Quit.new
263
265
  filters << Filters::Retry.new(@configuration)
@@ -272,8 +274,8 @@ module Cucumber
272
274
  @support_code.load_files!(files)
273
275
  end
274
276
 
275
- def install_wire_plugin
276
- Cucumber::Wire::Plugin.new(@configuration).install if @configuration.all_files_to_load.any? { |f| f =~ %r{\.wire$} }
277
+ def registry_wrapper
278
+ Cucumber::Glue::RegistryWrapper.new(@support_code.registry)
277
279
  end
278
280
 
279
281
  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
@@ -4,12 +4,15 @@ require 'cucumber/multiline_argument'
4
4
 
5
5
  module Cucumber
6
6
  # Represents the match found between a Test Step and its activation
7
- class StepMatch #:nodoc:
7
+ class StepMatch # :nodoc:
8
8
  attr_reader :step_definition, :step_arguments
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
+
13
+ @step_definition = step_definition
14
+ @name_to_match = step_name
15
+ @step_arguments = step_arguments
13
16
  end
14
17
 
15
18
  def args
@@ -21,12 +24,12 @@ module Cucumber
21
24
 
22
25
  def activate(test_step)
23
26
  test_step.with_action(@step_definition.location) do
24
- invoke(MultilineArgument.from_core(test_step.source.last.multiline_arg))
27
+ invoke(MultilineArgument.from_core(test_step.multiline_arg))
25
28
  end
26
29
  end
27
30
 
28
31
  def invoke(multiline_arg)
29
- all_args = deep_clone_args
32
+ all_args = args
30
33
  multiline_arg.append_to(all_args)
31
34
  @step_definition.invoke(all_args)
32
35
  end
@@ -46,7 +49,7 @@ module Cucumber
46
49
  #
47
50
  # lambda { |param| "[#{param}]" }
48
51
  #
49
- def format_args(format = lambda { |a| a }, &proc)
52
+ def format_args(format = ->(a) { a }, &proc)
50
53
  replace_arguments(@name_to_match, @step_arguments, format, &proc)
51
54
  end
52
55
 
@@ -75,7 +78,7 @@ module Cucumber
75
78
 
76
79
  replacement = if block_given?
77
80
  yield(group.value)
78
- elsif Proc === format
81
+ elsif Proc == format.class
79
82
  format.call(group.value)
80
83
  else
81
84
  format % group.value
@@ -88,24 +91,18 @@ module Cucumber
88
91
  s
89
92
  end
90
93
 
91
- def inspect #:nodoc:
94
+ def inspect # :nodoc:
92
95
  "#<#{self.class}: #{location}>"
93
96
  end
94
-
95
- private
96
-
97
- def deep_clone_args
98
- Marshal.load( Marshal.dump( args ) )
99
- end
100
97
  end
101
98
 
102
99
  class SkippingStepMatch
103
100
  def activate(test_step)
104
- return test_step.with_action { raise Core::Test::Result::Skipped.new }
101
+ test_step.with_action { raise Core::Test::Result::Skipped }
105
102
  end
106
103
  end
107
104
 
108
- class NoStepMatch #:nodoc:
105
+ class NoStepMatch # :nodoc:
109
106
  attr_reader :step_definition, :name
110
107
 
111
108
  def initialize(step, name)
@@ -119,12 +116,12 @@ module Cucumber
119
116
 
120
117
  def location
121
118
  raise "No location for #{@step}" unless @step.location
119
+
122
120
  @step.location
123
121
  end
124
122
 
125
123
  def file_colon_line
126
- raise "No file:line for #{@step}" unless @step.file_colon_line
127
- @step.file_colon_line
124
+ location.to_s
128
125
  end
129
126
 
130
127
  def backtrace_line
@@ -132,7 +129,7 @@ module Cucumber
132
129
  end
133
130
 
134
131
  def text_length
135
- @step.text_length
132
+ @step.text.length
136
133
  end
137
134
 
138
135
  def step_arguments
@@ -141,7 +138,7 @@ module Cucumber
141
138
 
142
139
  def activate(test_step)
143
140
  # noop
144
- return test_step
141
+ test_step
145
142
  end
146
143
  end
147
144
 
@@ -151,7 +148,7 @@ module Cucumber
151
148
  end
152
149
 
153
150
  def activate(test_step)
154
- return test_step.with_action { raise @error }
151
+ test_step.with_action { raise @error }
155
152
  end
156
153
  end
157
154
  end
@@ -13,12 +13,14 @@ 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)
20
21
  result = @search.call(step_name)
21
22
  raise Cucumber::Ambiguous.new(step_name, result, @configuration.guess?) if result.length > 1
23
+
22
24
  result
23
25
  end
24
26
  end
@@ -34,7 +36,7 @@ module Cucumber
34
36
 
35
37
  private
36
38
 
37
- def best_matches(_step_name, step_matches) #:nodoc:
39
+ def best_matches(_step_name, step_matches) # :nodoc:
38
40
  no_groups = step_matches.select { |step_match| step_match.args.empty? }
39
41
  max_arg_length = step_matches.map { |step_match| step_match.args.length }.max
40
42
  top_groups = step_matches.select { |step_match| step_match.args.length == max_arg_length }
@@ -53,7 +55,7 @@ module Cucumber
53
55
 
54
56
  require 'delegate'
55
57
  class CachesStepMatch < SimpleDelegator
56
- def call(step_name) #:nodoc:
58
+ def call(step_name) # :nodoc:
57
59
  @match_cache ||= {}
58
60
 
59
61
  matches = @match_cache[step_name]
@@ -2,8 +2,28 @@
2
2
 
3
3
  module Cucumber
4
4
  module Term
5
- # The ANSIColor module can be used for namespacing and mixed into your own
6
- # classes.
5
+ # This module allows to colorize text using ANSI escape sequences.
6
+ #
7
+ # Include the module in your class and use its methods to colorize text.
8
+ #
9
+ # Example:
10
+ #
11
+ # require 'cucumber/term/ansicolor'
12
+ #
13
+ # class MyFormatter
14
+ # include Cucumber::Term::ANSIColor
15
+ #
16
+ # def initialize(config)
17
+ # $stdout.puts yellow("Initializing formatter")
18
+ # $stdout.puts green("Coloring is active \o/") if Cucumber::Term::ANSIColor.coloring?
19
+ # $stdout.puts grey("Feature path:") + blue(bold(config.feature_dirs))
20
+ # end
21
+ # end
22
+ #
23
+ # To see what colours and effects are available, just run this in your shell:
24
+ #
25
+ # ruby -e "require 'rubygems'; require 'cucumber/term/ansicolor'; puts Cucumber::Term::ANSIColor.attributes"
26
+ #
7
27
  module ANSIColor
8
28
  # :stopdoc:
9
29
  ATTRIBUTES = [
@@ -27,6 +47,7 @@ module Cucumber
27
47
  [:magenta, 35],
28
48
  [:cyan, 36],
29
49
  [:white, 37],
50
+ [:grey, 90],
30
51
  [:on_black, 40],
31
52
  [:on_red, 41],
32
53
  [:on_green, 42],
@@ -35,77 +56,80 @@ module Cucumber
35
56
  [:on_magenta, 45],
36
57
  [:on_cyan, 46],
37
58
  [:on_white, 47]
38
- ]
59
+ ].freeze
39
60
 
40
61
  ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
41
62
  # :startdoc:
42
63
 
43
- # Returns true, if the coloring function of this module
44
- # is switched on, false otherwise.
45
- def self.coloring?
46
- @coloring
47
- end
48
-
49
- # Turns the coloring on or off globally, so you can easily do
50
- # this for example:
51
- # Cucumber::Term::ANSIColor::coloring = STDOUT.isatty
52
- def self.coloring=(val)
53
- @coloring = val
54
- end
55
- self.coloring = true
56
-
57
- ATTRIBUTES.each do |c, v|
58
- eval %Q{
59
- def #{c}(string = nil)
60
- result = String.new
61
- result << "\e[#{v}m" if Cucumber::Term::ANSIColor.coloring?
62
- if block_given?
63
- result << yield
64
- elsif string
65
- result << string
66
- elsif respond_to?(:to_str)
67
- result << to_str
68
- else
69
- return result #only switch on
70
- end
71
- result << "\e[0m" if Cucumber::Term::ANSIColor.coloring?
72
- result
73
- end
74
- }
75
- end
76
-
77
64
  # Regular expression that is used to scan for ANSI-sequences while
78
65
  # uncoloring strings.
79
66
  COLORED_REGEXP = /\e\[(?:[34][0-7]|[0-9])?m/
80
67
 
81
- def self.included(klass)
82
- if klass == String
68
+ @coloring = true
69
+
70
+ class << self
71
+ # Turns the coloring on or off globally, so you can easily do
72
+ # this for example:
73
+ # Cucumber::Term::ANSIColor::coloring = $stdout.isatty
74
+ attr_accessor :coloring
75
+
76
+ # Returns true, if the coloring function of this module
77
+ # is switched on, false otherwise.
78
+ alias coloring? :coloring
79
+
80
+ def included(klass)
81
+ return unless klass == String
82
+
83
83
  ATTRIBUTES.delete(:clear)
84
84
  ATTRIBUTE_NAMES.delete(:clear)
85
85
  end
86
86
  end
87
87
 
88
- # Returns an uncolored version of the string, that is all
88
+ ATTRIBUTES.each do |color_name, color_code|
89
+ define_method(color_name) do |text = nil, &block|
90
+ if block
91
+ colorize(block.call, color_code)
92
+ elsif text
93
+ colorize(text, color_code)
94
+ elsif respond_to?(:to_str)
95
+ colorize(to_str, color_code)
96
+ else
97
+ colorize(nil, color_code) # switch coloration on
98
+ end
99
+ end
100
+ end
101
+
102
+ # Returns an uncolored version of the string
89
103
  # ANSI-sequences are stripped from the string.
90
- def uncolored(string = nil) # :yields:
104
+ def uncolored(text = nil)
91
105
  if block_given?
92
- yield.gsub(COLORED_REGEXP, '')
93
- elsif string
94
- string.gsub(COLORED_REGEXP, '')
106
+ uncolorize(yield)
107
+ elsif text
108
+ uncolorize(text)
95
109
  elsif respond_to?(:to_str)
96
- to_str.gsub(COLORED_REGEXP, '')
110
+ uncolorize(to_str)
97
111
  else
98
112
  ''
99
113
  end
100
114
  end
101
115
 
102
- module_function
103
-
104
116
  # Returns an array of all Cucumber::Term::ANSIColor attributes as symbols.
105
117
  def attributes
106
118
  ATTRIBUTE_NAMES
107
119
  end
108
- extend self
120
+
121
+ private
122
+
123
+ def colorize(text, color_code)
124
+ return String.new(text || '') unless Cucumber::Term::ANSIColor.coloring?
125
+ return "\e[#{color_code}m" unless text
126
+
127
+ "\e[#{color_code}m#{text}\e[0m"
128
+ end
129
+
130
+ def uncolorize(string)
131
+ string.gsub(COLORED_REGEXP, '')
132
+ end
109
133
  end
110
134
  end
111
135
  end
@@ -0,0 +1,57 @@
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
+
34
+ span
35
+ end
36
+
37
+ def apply_modifiers(str, modifiers)
38
+ display = str
39
+ modifiers.each { |modifier| display = send(modifier, display) }
40
+ display
41
+ end
42
+
43
+ def line_length(line)
44
+ if line.is_a?(Array)
45
+ line.map { |span| span_length(span) }.sum
46
+ else
47
+ line.length
48
+ end
49
+ end
50
+
51
+ def span_length(span)
52
+ span.is_a?(Array) ? span[0].length : span.length
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end