cucumber 6.0.0 → 8.0.0.rc.1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +371 -168
  3. data/CONTRIBUTING.md +216 -55
  4. data/README.md +139 -21
  5. data/lib/autotest/cucumber_mixin.rb +5 -2
  6. data/lib/autotest/discover.rb +3 -2
  7. data/lib/cucumber/cli/configuration.rb +4 -1
  8. data/lib/cucumber/cli/main.rb +4 -3
  9. data/lib/cucumber/cli/options.rb +14 -4
  10. data/lib/cucumber/cli/profile_loader.rb +1 -5
  11. data/lib/cucumber/cli/rerun_file.rb +1 -1
  12. data/lib/cucumber/configuration.rb +5 -4
  13. data/lib/cucumber/constantize.rb +1 -1
  14. data/lib/cucumber/deprecate.rb +2 -1
  15. data/lib/cucumber/errors.rb +1 -1
  16. data/lib/cucumber/events/hook_test_step_created.rb +1 -2
  17. data/lib/cucumber/events/step_activated.rb +0 -6
  18. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  19. data/lib/cucumber/events/test_case_created.rb +1 -2
  20. data/lib/cucumber/events/test_run_finished.rb +2 -1
  21. data/lib/cucumber/events/test_step_created.rb +1 -2
  22. data/lib/cucumber/events/undefined_parameter_type.rb +1 -2
  23. data/lib/cucumber/events.rb +2 -2
  24. data/lib/cucumber/file_specs.rb +2 -1
  25. data/lib/cucumber/filters/activate_steps.rb +1 -0
  26. data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
  27. data/lib/cucumber/filters/tag_limits.rb +1 -3
  28. data/lib/cucumber/formatter/ansicolor.rb +63 -70
  29. data/lib/cucumber/formatter/ast_lookup.rb +2 -2
  30. data/lib/cucumber/formatter/backtrace_filter.rb +1 -1
  31. data/lib/cucumber/formatter/console.rb +20 -4
  32. data/lib/cucumber/formatter/console_issues.rb +6 -1
  33. data/lib/cucumber/formatter/duration_extractor.rb +1 -0
  34. data/lib/cucumber/formatter/errors.rb +1 -0
  35. data/lib/cucumber/formatter/fanout.rb +1 -1
  36. data/lib/cucumber/formatter/http_io.rb +6 -1
  37. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  38. data/lib/cucumber/formatter/io.rb +3 -1
  39. data/lib/cucumber/formatter/json.rb +32 -26
  40. data/lib/cucumber/formatter/junit.rb +6 -3
  41. data/lib/cucumber/formatter/message.rb +2 -1
  42. data/lib/cucumber/formatter/message_builder.rb +11 -10
  43. data/lib/cucumber/formatter/pretty.rb +34 -23
  44. data/lib/cucumber/formatter/progress.rb +1 -0
  45. data/lib/cucumber/formatter/publish_banner_printer.rb +1 -1
  46. data/lib/cucumber/formatter/query/hook_by_test_step.rb +1 -0
  47. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
  48. data/lib/cucumber/formatter/rerun.rb +2 -0
  49. data/lib/cucumber/formatter/steps.rb +5 -2
  50. data/lib/cucumber/formatter/summary.rb +1 -0
  51. data/lib/cucumber/formatter/unicode.rb +4 -4
  52. data/lib/cucumber/formatter/usage.rb +9 -7
  53. data/lib/cucumber/gherkin/data_table_parser.rb +2 -1
  54. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +2 -2
  55. data/lib/cucumber/gherkin/steps_parser.rb +1 -1
  56. data/lib/cucumber/glue/dsl.rb +19 -5
  57. data/lib/cucumber/glue/hook.rb +2 -1
  58. data/lib/cucumber/glue/invoke_in_world.rb +4 -4
  59. data/lib/cucumber/glue/proto_world.rb +12 -9
  60. data/lib/cucumber/glue/registry_and_more.rb +20 -5
  61. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  62. data/lib/cucumber/glue/step_definition.rb +9 -7
  63. data/lib/cucumber/hooks.rb +1 -0
  64. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -1
  65. data/lib/cucumber/multiline_argument/data_table.rb +58 -71
  66. data/lib/cucumber/platform.rb +2 -2
  67. data/lib/cucumber/rake/task.rb +10 -7
  68. data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
  69. data/lib/cucumber/running_test_case.rb +1 -0
  70. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  71. data/lib/cucumber/runtime/support_code.rb +3 -0
  72. data/lib/cucumber/runtime/user_interface.rb +5 -4
  73. data/lib/cucumber/runtime.rb +42 -23
  74. data/lib/cucumber/step_match.rb +6 -10
  75. data/lib/cucumber/step_match_search.rb +3 -2
  76. data/lib/cucumber/term/ansicolor.rb +74 -50
  77. data/lib/cucumber/term/banner.rb +1 -0
  78. data/lib/cucumber/version +1 -1
  79. data/lib/cucumber.rb +2 -1
  80. data/lib/simplecov_setup.rb +1 -1
  81. metadata +90 -89
  82. data/lib/cucumber/core_ext/string.rb +0 -11
@@ -24,8 +24,12 @@ module Cucumber
24
24
  "#{INDENT}the usage formatter, except that steps are not printed."],
25
25
  'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
26
26
  "#{INDENT}junit,fileattribute=true to include a file attribute."],
27
- 'json' => ['Cucumber::Formatter::Json', '[DEPRECATED] Prints the feature as JSON'],
28
- 'message' => ['Cucumber::Formatter::Message', 'Outputs protobuf messages'],
27
+ 'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
28
+ "#{INDENT}The JSON format is in maintenance mode.\n" \
29
+ "#{INDENT}Please consider using the message formatter\n"\
30
+ "#{INDENT}with the standalone json-formatter\n" \
31
+ "#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
32
+ 'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
29
33
  'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
30
34
  'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
31
35
  }.freeze
@@ -59,13 +63,13 @@ module Cucumber
59
63
  '--lines', '--port', '-I', '--snippet-type'
60
64
  ].freeze
61
65
  ORDER_TYPES = %w[defined random].freeze
62
- TAG_LIMIT_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x
66
+ TAG_LIMIT_MATCHER = /(?<tag_name>@\w+):(?<limit>\d+)/x
63
67
 
64
68
  def self.parse(args, out_stream, error_stream, options = {})
65
69
  new(out_stream, error_stream, options).parse!(args)
66
70
  end
67
71
 
68
- def initialize(out_stream = STDOUT, error_stream = STDERR, options = {})
72
+ def initialize(out_stream = $stdout, error_stream = $stderr, options = {})
69
73
  @out_stream = out_stream
70
74
  @error_stream = error_stream
71
75
 
@@ -176,6 +180,7 @@ Specify SEED to reproduce the shuffling from a previous run.
176
180
  def check_formatter_stream_conflicts
177
181
  streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
178
182
  return if streams == streams.uniq
183
+
179
184
  raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
180
185
  end
181
186
 
@@ -199,6 +204,7 @@ Specify SEED to reproduce the shuffling from a previous run.
199
204
 
200
205
  def truthy_string?(str)
201
206
  return false if str.nil?
207
+
202
208
  str !~ /^(false|no|0)$/i
203
209
  end
204
210
 
@@ -363,6 +369,7 @@ Specify SEED to reproduce the shuffling from a previous run.
363
369
  def require_files(v)
364
370
  @options[:require] << v
365
371
  return unless Cucumber::JRUBY && File.directory?(v)
372
+
366
373
  require 'java'
367
374
  $CLASSPATH << v
368
375
  end
@@ -381,6 +388,7 @@ Specify SEED to reproduce the shuffling from a previous run.
381
388
  require 'gherkin/dialect'
382
389
 
383
390
  return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.key?(lang)
391
+
384
392
  list_keywords_and_exit(lang)
385
393
  end
386
394
 
@@ -399,6 +407,7 @@ Specify SEED to reproduce the shuffling from a previous run.
399
407
  def add_tag(value)
400
408
  raise("Found tags option '#{value}'. '~@tag' is no longer supported, use 'not @tag' instead.") if value.include?('~')
401
409
  raise("Found tags option '#{value}'. '@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.") if value.include?(',')
410
+
402
411
  @options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
403
412
  add_tag_limits(value)
404
413
  end
@@ -411,6 +420,7 @@ Specify SEED to reproduce the shuffling from a previous run.
411
420
 
412
421
  def add_tag_limit(tag_limits, tag_name, limit)
413
422
  raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}" if tag_limits[tag_name] && tag_limits[tag_name] != limit
423
+
414
424
  tag_limits[tag_name] = limit
415
425
  end
416
426
 
@@ -75,11 +75,7 @@ Defined profiles in cucumber.yml:
75
75
  def process_configuration_file_with_erb
76
76
  require 'erb'
77
77
  begin
78
- @cucumber_erb = if RUBY_VERSION >= '2.6'
79
- ERB.new(IO.read(cucumber_file), trim_mode: '%').result(binding)
80
- else
81
- ERB.new(IO.read(cucumber_file), nil, '%').result(binding)
82
- end
78
+ @cucumber_erb = ERB.new(IO.read(cucumber_file), trim_mode: '%').result(binding)
83
79
  rescue StandardError
84
80
  raise(YmlLoadError, "cucumber.yml was found, but could not be parsed with ERB. Please refer to cucumber's documentation on correct profile usage.\n#{$ERROR_INFO.inspect}")
85
81
  end
@@ -10,7 +10,7 @@ module Cucumber
10
10
  end
11
11
 
12
12
  def self.real_path(path)
13
- path[1..-1] # remove leading @
13
+ path[1..]
14
14
  end
15
15
 
16
16
  def initialize(path)
@@ -176,12 +176,12 @@ module Cucumber
176
176
  end
177
177
 
178
178
  def support_to_load
179
- support_files = all_files_to_load.select { |f| f =~ %r{/support/} }
179
+ support_files = all_files_to_load.select { |f| f =~ /\/support\// }
180
180
 
181
181
  # env_files are separated from other_files so we can ensure env files
182
182
  # load first.
183
183
  #
184
- env_files = support_files.select { |f| f =~ %r{/support/env\..*} }
184
+ env_files = support_files.select { |f| f =~ /\/support\/env\..*/ }
185
185
  other_files = support_files - env_files
186
186
  env_files.reverse + other_files.reverse
187
187
  end
@@ -189,7 +189,7 @@ module Cucumber
189
189
  def all_files_to_load
190
190
  files = require_dirs.map do |path|
191
191
  path = path.tr('\\', '/') # In case we're on windows. Globs don't work with backslashes.
192
- path = path.gsub(/\/$/, '') # Strip trailing slash. # rubocop:disable Style/RegexpLiteral
192
+ path = path.gsub(/\/$/, '') # Strip trailing slash.
193
193
  File.directory?(path) ? Dir["#{path}/**/*"] : path
194
194
  end.flatten.uniq
195
195
  remove_excluded_files_from(files)
@@ -200,7 +200,7 @@ module Cucumber
200
200
  end
201
201
 
202
202
  def step_defs_to_load
203
- all_files_to_load.reject { |f| f =~ %r{/support/} }
203
+ all_files_to_load.reject { |f| f =~ /\/support\// }
204
204
  end
205
205
 
206
206
  def formatter_factories
@@ -283,6 +283,7 @@ module Cucumber
283
283
 
284
284
  def with_default_features_path(paths)
285
285
  return default_features_paths if paths.empty?
286
+
286
287
  paths
287
288
  end
288
289
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'cucumber/platform'
4
4
  module Cucumber
5
- module Constantize #:nodoc:
5
+ module Constantize # :nodoc:
6
6
  def constantize(camel_cased_word)
7
7
  try = 0
8
8
  begin
@@ -20,6 +20,7 @@ module Cucumber
20
20
  class CliOption
21
21
  def self.deprecate(stream, option, message, remove_after_version)
22
22
  return if stream.nil?
23
+
23
24
  stream.puts(
24
25
  AnsiString.failure_message(
25
26
  "\nWARNING: #{option} is deprecated" \
@@ -31,7 +32,7 @@ module Cucumber
31
32
 
32
33
  module ForUsers
33
34
  def self.call(message, method, remove_after_version)
34
- STDERR.puts AnsiString.failure_message(
35
+ $stderr.puts AnsiString.failure_message(
35
36
  "\nWARNING: ##{method} is deprecated" \
36
37
  " and will be removed after version #{remove_after_version}. #{message}.\n" \
37
38
  "(Called from #{caller(3..3).first})"
@@ -9,7 +9,7 @@ module Cucumber
9
9
  return result.with_message(with_prefix(result.message)) if result.is_a?(self)
10
10
 
11
11
  begin
12
- raise new(with_prefix(step_name)) # rubocop:disable Style/RaiseArgs
12
+ raise self, with_prefix(step_name)
13
13
  rescue StandardError => e
14
14
  e
15
15
  end
@@ -6,8 +6,7 @@ module Cucumber
6
6
  module Events
7
7
  # Event fired when a step is created from a hook
8
8
  class HookTestStepCreated < Core::Event.new(:test_step, :hook)
9
- attr_reader :test_step
10
- attr_reader :hook
9
+ attr_reader :test_step, :hook
11
10
  end
12
11
  end
13
12
  end
@@ -15,12 +15,6 @@ module Cucumber
15
15
  #
16
16
  # @return [Cucumber::StepMatch]
17
17
  attr_reader :step_match
18
-
19
- # @private
20
- def initialize(test_step, step_match)
21
- @test_step = test_step
22
- @step_match = step_match
23
- end
24
18
  end
25
19
  end
26
20
  end
@@ -10,11 +10,6 @@ module Cucumber
10
10
  #
11
11
  # @return [RbSupport::RbStepDefinition]
12
12
  attr_reader :step_definition
13
-
14
- # _@private
15
- def initialize(step_definition)
16
- @step_definition = step_definition
17
- end
18
13
  end
19
14
  end
20
15
  end
@@ -6,8 +6,7 @@ module Cucumber
6
6
  module Events
7
7
  # Event fired when a Test::Case is created from a Pickle
8
8
  class TestCaseCreated < Core::Event.new(:test_case, :pickle)
9
- attr_reader :test_case
10
- attr_reader :pickle
9
+ attr_reader :test_case, :pickle
11
10
  end
12
11
  end
13
12
  end
@@ -5,7 +5,8 @@ require 'cucumber/core/events'
5
5
  module Cucumber
6
6
  module Events
7
7
  # Event fired after all test cases have finished executing
8
- class TestRunFinished < Core::Event.new
8
+ class TestRunFinished < Core::Event.new(:success)
9
+ attr_reader :success
9
10
  end
10
11
  end
11
12
  end
@@ -6,8 +6,7 @@ module Cucumber
6
6
  module Events
7
7
  # Event fired when a TestStep is created from a PickleStep
8
8
  class TestStepCreated < Core::Event.new(:test_step, :pickle_step)
9
- attr_reader :test_step
10
- attr_reader :pickle_step
9
+ attr_reader :test_step, :pickle_step
11
10
  end
12
11
  end
13
12
  end
@@ -3,8 +3,7 @@ require 'cucumber/core/events'
3
3
  module Cucumber
4
4
  module Events
5
5
  class UndefinedParameterType < Core::Event.new(:type_name, :expression)
6
- attr_reader :type_name
7
- attr_reader :expression
6
+ attr_reader :type_name, :expression
8
7
  end
9
8
  end
10
9
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir[File.dirname(__FILE__) + '/events/*.rb'].map(&method(:require))
3
+ Dir["#{File.dirname(__FILE__)}/events/*.rb"].map(&method(:require))
4
4
 
5
5
  module Cucumber
6
6
  # Events tell you what's happening while Cucumber runs your features.
@@ -11,7 +11,7 @@ module Cucumber
11
11
  # To subscribe to an event, use {Cucumber::Configuration#on_event}
12
12
  #
13
13
  # @example
14
- # AfterConfiguration do |config|
14
+ # InstallPlugin do |config|
15
15
  # config.on_event :test_case_finished do |event|
16
16
  # puts event.result
17
17
  # end
@@ -5,7 +5,7 @@ require 'cucumber/core/test/location'
5
5
 
6
6
  module Cucumber
7
7
  class FileSpecs
8
- FILE_COLON_LINE_PATTERN = /^([\w\W]*?)(?::([\d:]+))?$/ #:nodoc:
8
+ FILE_COLON_LINE_PATTERN = /^([\w\W]*?)(?::([\d:]+))?$/ # :nodoc:
9
9
 
10
10
  def initialize(file_specs)
11
11
  Cucumber.logger.debug("Features:\n")
@@ -32,6 +32,7 @@ module Cucumber
32
32
 
33
33
  def locations
34
34
  return [Core::Test::Location.new(@file)] if @lines.empty?
35
+
35
36
  @lines.map { |line| Core::Test::Location.new(@file, line) }
36
37
  end
37
38
  end
@@ -52,6 +52,7 @@ module Cucumber
52
52
  end
53
53
  configuration.notify :step_activated, test_step, match
54
54
  return SkippingStepMatch.new if configuration.dry_run?
55
+
55
56
  match
56
57
  end
57
58
 
@@ -47,9 +47,7 @@ module Cucumber
47
47
  locations.count
48
48
  end
49
49
 
50
- attr_reader :tag_name
51
- attr_reader :limit
52
- attr_reader :locations
50
+ attr_reader :tag_name, :limit, :locations
53
51
  end
54
52
  end
55
53
  end
@@ -38,9 +38,7 @@ module Cucumber
38
38
 
39
39
  private
40
40
 
41
- attr_reader :gated_receiver
42
- attr_reader :test_case_index
43
- attr_reader :verifier
41
+ attr_reader :gated_receiver, :test_case_index, :verifier
44
42
  end
45
43
  end
46
44
  end
@@ -3,28 +3,48 @@
3
3
  require 'cucumber/platform'
4
4
  require 'cucumber/term/ansicolor'
5
5
 
6
- if Cucumber::WINDOWS_MRI
7
- unless ENV['ANSICON']
8
- STDERR.puts %{*** WARNING: You must use ANSICON 1.31 or higher (https://github.com/adoxa/ansicon/) to get coloured output on Windows}
9
- Cucumber::Term::ANSIColor.coloring = false
10
- end
11
- end
12
-
13
- Cucumber::Term::ANSIColor.coloring = false if !STDOUT.tty? && !ENV.key?('AUTOTEST')
6
+ Cucumber::Term::ANSIColor.coloring = false if !$stdout.tty? && !ENV.key?('AUTOTEST')
14
7
 
15
8
  module Cucumber
16
9
  module Formatter
17
- # Defines aliases for coloured output. You don't invoke any methods from this
18
- # module directly, but you can change the output colours by defining
19
- # a <tt>CUCUMBER_COLORS</tt> variable in your shell, very much like how you can
20
- # tweak the familiar POSIX command <tt>ls</tt> with
21
- # <a href="http://mipsisrisc.com/rambling/2008/06/27/lscolorsls_colors-now-with-linux-support/">$LSCOLORS/$LS_COLORS</a>
10
+ # This module allows to format cucumber related outputs using ANSI escape sequences.
11
+ #
12
+ # For example, it provides a `passed` method which returns the string with
13
+ # the ANSI escape sequence to format it green per default.
14
+ #
15
+ # To use this, include or extend it in your class.
16
+ #
17
+ # Example:
18
+ #
19
+ # require 'cucumber/formatter/ansicolor'
20
+ #
21
+ # class MyFormatter
22
+ # extend Cucumber::Term::ANSIColor
23
+ #
24
+ # def on_test_step_finished(event)
25
+ # $stdout.puts undefined(event.test_step) if event.result.undefined?
26
+ # $stdout.puts passed(event.test_step) if event.result.passed?
27
+ # end
28
+ # end
29
+ #
30
+ # This module also allows the user to customize the format of cucumber outputs
31
+ # using environment variables.
32
+ #
33
+ # For instance, if your shell has a black background and a green font (like the
34
+ # "Homebrew" settings for OS X' Terminal.app), you may want to override passed
35
+ # steps to be white instead of green.
36
+ #
37
+ # Example:
38
+ #
39
+ # export CUCUMBER_COLORS="passed=white,bold:passed_param=white,bold,underline"
22
40
  #
23
41
  # The colours that you can change are:
24
42
  #
25
43
  # * <tt>undefined</tt> - defaults to <tt>yellow</tt>
26
44
  # * <tt>pending</tt> - defaults to <tt>yellow</tt>
27
45
  # * <tt>pending_param</tt> - defaults to <tt>yellow,bold</tt>
46
+ # * <tt>flaky</tt> - defaults to <tt>yellow</tt>
47
+ # * <tt>flaky_param</tt> - defaults to <tt>yellow,bold</tt>
28
48
  # * <tt>failed</tt> - defaults to <tt>red</tt>
29
49
  # * <tt>failed_param</tt> - defaults to <tt>red,bold</tt>
30
50
  # * <tt>passed</tt> - defaults to <tt>green</tt>
@@ -36,26 +56,18 @@ module Cucumber
36
56
  # * <tt>comment</tt> - defaults to <tt>grey</tt>
37
57
  # * <tt>tag</tt> - defaults to <tt>cyan</tt>
38
58
  #
39
- # For instance, if your shell has a black background and a green font (like the
40
- # "Homebrew" settings for OS X' Terminal.app), you may want to override passed
41
- # steps to be white instead of green.
42
- #
43
- # Although not listed, you can also use <tt>grey</tt>.
44
- #
45
- # Examples: (On Windows, use SET instead of export.)
46
- #
47
- # export CUCUMBER_COLORS="passed=white"
48
- # export CUCUMBER_COLORS="passed=white,bold:passed_param=white,bold,underline"
49
- #
50
59
  # To see what colours and effects are available, just run this in your shell:
51
60
  #
52
- # ruby -e "require 'rubygems'; require 'term/ansicolor'; puts Cucumber::Term::ANSIColor.attributes"
61
+ # ruby -e "require 'rubygems'; require 'cucumber/term/ansicolor'; puts Cucumber::Term::ANSIColor.attributes"
53
62
  #
54
63
  module ANSIColor
55
64
  include Cucumber::Term::ANSIColor
56
65
 
66
+ # :stopdoc:
57
67
  ALIASES = Hash.new do |h, k|
58
- h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_param/
68
+ next unless k.to_s =~ /(.*)_param/
69
+
70
+ "#{h[Regexp.last_match(1)]},bold"
59
71
  end.merge(
60
72
  'undefined' => 'yellow',
61
73
  'pending' => 'yellow',
@@ -67,15 +79,22 @@ module Cucumber
67
79
  'comment' => 'grey',
68
80
  'tag' => 'cyan'
69
81
  )
82
+ # :startdoc:
70
83
 
71
- if ENV['CUCUMBER_COLORS'] # Example: export CUCUMBER_COLORS="passed=red:failed=yellow"
72
- ENV['CUCUMBER_COLORS'].split(':').each do |pair|
84
+ # Apply the custom color scheme
85
+ #
86
+ # example:
87
+ #
88
+ # apply_custom_colors('passed=white')
89
+ def apply_custom_colors(colors)
90
+ colors.split(':').each do |pair|
73
91
  a = pair.split('=')
74
92
  ALIASES[a[0]] = a[1]
75
93
  end
76
94
  end
95
+ apply_custom_colors(ENV['CUCUMBER_COLORS']) if ENV['CUCUMBER_COLORS']
77
96
 
78
- # Eval to define the color-named methods required by Term::ANSIColor.
97
+ # Define the color-named methods required by Term::ANSIColor.
79
98
  #
80
99
  # Examples:
81
100
  #
@@ -87,53 +106,18 @@ module Cucumber
87
106
  # red(bold(string, &proc)) + red
88
107
  # end
89
108
  ALIASES.each_key do |method_name|
90
- next if method_name =~ /.*_param/
91
- code = <<-COLOR
92
- def #{method_name}(string=nil, &proc)
93
- #{ALIASES[method_name].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name].split(',').length}
94
- end
95
- # This resets the colour to the non-param colour
96
- def #{method_name}_param(string=nil, &proc)
97
- #{ALIASES[method_name + '_param'].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name + '_param'].split(',').length} + #{ALIASES[method_name].split(',').join(' + ')}
98
- end
99
- COLOR
100
- eval(code) # rubocop:disable Security/Eval
101
- end
109
+ next if method_name.end_with?('_param')
102
110
 
103
- def self.define_grey #:nodoc:
104
- gem 'genki-ruby-terminfo'
105
- require 'terminfo'
106
- case TermInfo.default_object.tigetnum('colors')
107
- when 0
108
- raise "Your terminal doesn't support colours."
109
- when 1
110
- ::Cucumber::Term::ANSIColor.coloring = false
111
- alias_method :grey, :white
112
- when 2..8
113
- alias_method :grey, :white # rubocop:disable Lint/DuplicateMethods
114
- else
115
- define_real_grey
116
- end
117
- rescue Exception => e # rubocop:disable Lint/RescueException
118
- if e.class.name == 'TermInfo::TermInfoError'
119
- STDERR.puts '*** WARNING ***'
120
- STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
121
- STDERR.puts 'Try setting it to TERM=xterm-256color to get grey colour in output.'
122
- STDERR.puts "\n"
123
- alias_method :grey, :white
124
- else
125
- define_real_grey
111
+ define_method(method_name) do |text = nil, &proc|
112
+ apply_styles(ALIASES[method_name], text, &proc)
126
113
  end
127
- end
128
114
 
129
- def self.define_real_grey #:nodoc:
130
- define_method :grey do |string|
131
- ::Cucumber::Term::ANSIColor.coloring? ? "\e[90m#{string}\e[0m" : string
115
+ define_method("#{method_name}_param") do |text = nil, &proc|
116
+ apply_styles(ALIASES["#{method_name}_param"], text, &proc) + apply_styles(ALIASES[method_name])
132
117
  end
133
118
  end
134
119
 
135
- define_grey
136
-
120
+ # :stopdoc:
137
121
  def cukes(n)
138
122
  ('(::) ' * n).strip
139
123
  end
@@ -149,6 +133,15 @@ module Cucumber
149
133
  def yellow_cukes(n)
150
134
  blink(yellow(cukes(n)))
151
135
  end
136
+ # :startdoc:
137
+
138
+ private
139
+
140
+ def apply_styles(styles, text = nil, &proc)
141
+ styles.split(',').reverse.reduce(text) do |result, method_name|
142
+ send(method_name, result, &proc)
143
+ end
144
+ end
152
145
  end
153
146
  end
154
147
  end
@@ -45,11 +45,11 @@ module Cucumber
45
45
  break
46
46
  end
47
47
  break if node.previous_node.nil?
48
+
48
49
  node = node.previous_node
49
50
  end
50
51
  keyword = dialect.given_keywords.reject { |kw| kw == '* ' }[0] if keyword.nil?
51
- keyword = Cucumber::Gherkin::I18n.code_keyword_for(keyword)
52
- keyword
52
+ Cucumber::Gherkin::I18n.code_keyword_for(keyword)
53
53
  end
54
54
 
55
55
  ScenarioSource = Struct.new(:type, :scenario)
@@ -33,7 +33,7 @@ module Cucumber
33
33
  def exception
34
34
  return @exception if ::Cucumber.use_full_backtrace
35
35
 
36
- pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m # rubocop:disable Style/RegexpLiteral
36
+ pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m
37
37
  backtrace = @exception.backtrace.map { |line| line.gsub(pwd_pattern, './') }
38
38
 
39
39
  filtered = (backtrace || []).reject do |line|
@@ -34,7 +34,7 @@ module Cucumber
34
34
 
35
35
  def format_step(keyword, step_match, status, source_indent)
36
36
  comment = if source_indent
37
- c = ('# ' + step_match.location.to_s).indent(source_indent)
37
+ c = indent("# #{step_match.location}", source_indent)
38
38
  format_string(c, :comment)
39
39
  else
40
40
  ''
@@ -99,17 +99,20 @@ module Cucumber
99
99
  @io.puts(format_string(string, status))
100
100
  end
101
101
 
102
- def exception_message_string(e, indent)
102
+ def exception_message_string(e, indent_amount)
103
103
  message = "#{e.message} (#{e.class})".dup.force_encoding('UTF-8')
104
104
  message = linebreaks(message, ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
105
105
 
106
- "#{message}\n#{e.backtrace.join("\n")}".indent(indent)
106
+ indent("#{message}\n#{e.backtrace.join("\n")}", indent_amount)
107
107
  end
108
108
 
109
109
  # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
110
110
  def linebreaks(msg, max)
111
111
  return msg unless max && max > 0
112
- msg.gsub(/.{1,#{max}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }.rstrip
112
+
113
+ msg.gsub(/.{1,#{max}}(?:\s|\Z)/) do
114
+ (Regexp.last_match(0) + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n")
115
+ end.rstrip
113
116
  end
114
117
 
115
118
  def collect_snippet_data(test_step, ast_lookup)
@@ -150,6 +153,7 @@ module Cucumber
150
153
 
151
154
  def print_passing_wip(config, passed_test_cases, ast_lookup)
152
155
  return unless config.wip?
156
+
153
157
  messages = passed_test_cases.map do |test_case|
154
158
  scenario_source = ast_lookup.scenario_source(test_case)
155
159
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
@@ -170,6 +174,7 @@ module Cucumber
170
174
  def attach(src, media_type)
171
175
  return unless media_type == 'text/x.cucumber.log+plain'
172
176
  return unless @io
177
+
173
178
  @io.puts
174
179
  @io.puts(format_string(src, :tag))
175
180
  @io.flush
@@ -177,6 +182,7 @@ module Cucumber
177
182
 
178
183
  def print_profile_information
179
184
  return if @options[:skip_profile_information] || @options[:profiles].nil? || @options[:profiles].empty?
185
+
180
186
  do_print_profile_information(@options[:profiles])
181
187
  end
182
188
 
@@ -208,6 +214,14 @@ module Cucumber
208
214
  ].join("\n")
209
215
  end
210
216
 
217
+ def indent(string, padding)
218
+ if padding >= 0
219
+ string.gsub(/^/, ' ' * padding)
220
+ else
221
+ string.gsub(/^ {0,#{-padding}}/, '')
222
+ end
223
+ end
224
+
211
225
  private
212
226
 
213
227
  FORMATS = Hash.new { |hash, format| hash[format] = method(format).to_proc }
@@ -216,6 +230,7 @@ module Cucumber
216
230
  key = keys.join('_').to_sym
217
231
  fmt = FORMATS[key]
218
232
  raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
233
+
219
234
  fmt
220
235
  end
221
236
 
@@ -238,6 +253,7 @@ module Cucumber
238
253
 
239
254
  class SnippetData
240
255
  attr_reader :actual_keyword, :step
256
+
241
257
  def initialize(actual_keyword, step)
242
258
  @actual_keyword = actual_keyword
243
259
  @step = step
@@ -23,6 +23,7 @@ module Cucumber
23
23
 
24
24
  def to_s
25
25
  return if @issues.empty?
26
+
26
27
  result = Core::Test::Result::TYPES.map { |type| scenario_listing(type, @issues[type]) }
27
28
  result.flatten.join("\n")
28
29
  end
@@ -35,6 +36,7 @@ module Cucumber
35
36
 
36
37
  def scenario_listing(type, test_cases)
37
38
  return [] if test_cases.empty?
39
+
38
40
  [format_string("#{type_heading(type)} Scenarios:", type)] + test_cases.map do |test_case|
39
41
  scenario_source = @ast_lookup.scenario_source(test_case)
40
42
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
@@ -54,7 +56,10 @@ module Cucumber
54
56
 
55
57
  def profiles_string
56
58
  return if @config.custom_profiles.empty?
57
- @config.custom_profiles.map { |profile| "-p #{profile}" }.join(' ') + ' '
59
+
60
+ profiles = @config.custom_profiles.map { |profile| "-p #{profile}" }.join(' ')
61
+
62
+ "#{profiles} "
58
63
  end
59
64
  end
60
65
  end