cucumber 3.1.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1880 -1146
  3. data/CONTRIBUTING.md +220 -61
  4. data/README.md +143 -22
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +49 -53
  7. data/lib/autotest/discover.rb +3 -2
  8. data/lib/cucumber/cli/configuration.rb +32 -7
  9. data/lib/cucumber/cli/main.rb +16 -15
  10. data/lib/cucumber/cli/options.rb +111 -79
  11. data/lib/cucumber/cli/profile_loader.rb +45 -26
  12. data/lib/cucumber/cli/rerun_file.rb +1 -1
  13. data/lib/cucumber/configuration.rb +47 -31
  14. data/lib/cucumber/constantize.rb +3 -6
  15. data/lib/cucumber/deprecate.rb +32 -7
  16. data/lib/cucumber/errors.rb +5 -7
  17. data/lib/cucumber/events/envelope.rb +9 -0
  18. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  19. data/lib/cucumber/events/hook_test_step_created.rb +12 -0
  20. data/lib/cucumber/events/step_activated.rb +0 -5
  21. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  22. data/lib/cucumber/events/test_case_created.rb +12 -0
  23. data/lib/cucumber/events/test_case_ready.rb +12 -0
  24. data/lib/cucumber/events/test_run_finished.rb +2 -1
  25. data/lib/cucumber/events/test_step_created.rb +12 -0
  26. data/lib/cucumber/events/undefined_parameter_type.rb +9 -0
  27. data/lib/cucumber/events.rb +15 -8
  28. data/lib/cucumber/file_specs.rb +8 -7
  29. data/lib/cucumber/filters/activate_steps.rb +6 -3
  30. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  31. data/lib/cucumber/filters/prepare_world.rb +5 -9
  32. data/lib/cucumber/filters/quit.rb +1 -3
  33. data/lib/cucumber/filters/tag_limits/verifier.rb +3 -7
  34. data/lib/cucumber/filters/tag_limits.rb +1 -3
  35. data/lib/cucumber/filters.rb +1 -0
  36. data/lib/cucumber/formatter/ansicolor.rb +74 -86
  37. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  38. data/lib/cucumber/formatter/backtrace_filter.rb +10 -7
  39. data/lib/cucumber/formatter/console.rb +76 -68
  40. data/lib/cucumber/formatter/console_counts.rb +4 -9
  41. data/lib/cucumber/formatter/console_issues.rb +12 -4
  42. data/lib/cucumber/formatter/duration.rb +1 -1
  43. data/lib/cucumber/formatter/duration_extractor.rb +4 -1
  44. data/lib/cucumber/formatter/errors.rb +7 -0
  45. data/lib/cucumber/formatter/fanout.rb +3 -1
  46. data/lib/cucumber/formatter/html.rb +11 -598
  47. data/lib/cucumber/formatter/http_io.rb +152 -0
  48. data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -2
  49. data/lib/cucumber/formatter/interceptor.rb +11 -30
  50. data/lib/cucumber/formatter/io.rb +57 -13
  51. data/lib/cucumber/formatter/json.rb +119 -124
  52. data/lib/cucumber/formatter/junit.rb +75 -55
  53. data/lib/cucumber/formatter/message.rb +23 -0
  54. data/lib/cucumber/formatter/message_builder.rb +256 -0
  55. data/lib/cucumber/formatter/pretty.rb +370 -153
  56. data/lib/cucumber/formatter/progress.rb +31 -32
  57. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  58. data/lib/cucumber/formatter/query/hook_by_test_step.rb +32 -0
  59. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  60. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  61. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  62. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +42 -0
  63. data/lib/cucumber/formatter/rerun.rb +24 -4
  64. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  65. data/lib/cucumber/formatter/steps.rb +8 -6
  66. data/lib/cucumber/formatter/summary.rb +17 -8
  67. data/lib/cucumber/formatter/unicode.rb +18 -20
  68. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  69. data/lib/cucumber/formatter/usage.rb +18 -15
  70. data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
  71. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +14 -18
  72. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  73. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  74. data/lib/cucumber/glue/dsl.rb +29 -15
  75. data/lib/cucumber/glue/hook.rb +37 -11
  76. data/lib/cucumber/glue/invoke_in_world.rb +17 -22
  77. data/lib/cucumber/glue/proto_world.rb +47 -53
  78. data/lib/cucumber/glue/registry_and_more.rb +62 -17
  79. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  80. data/lib/cucumber/glue/snippet.rb +23 -22
  81. data/lib/cucumber/glue/step_definition.rb +48 -23
  82. data/lib/cucumber/glue/world_factory.rb +1 -1
  83. data/lib/cucumber/hooks.rb +12 -11
  84. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +4 -3
  85. data/lib/cucumber/multiline_argument/data_table.rb +143 -123
  86. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  87. data/lib/cucumber/multiline_argument.rb +4 -6
  88. data/lib/cucumber/platform.rb +5 -5
  89. data/lib/cucumber/rake/task.rb +34 -25
  90. data/lib/cucumber/rspec/disable_option_parser.rb +15 -11
  91. data/lib/cucumber/rspec/doubles.rb +3 -5
  92. data/lib/cucumber/running_test_case.rb +3 -53
  93. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  94. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  95. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  96. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  97. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  98. data/lib/cucumber/runtime/support_code.rb +16 -15
  99. data/lib/cucumber/runtime/user_interface.rb +10 -19
  100. data/lib/cucumber/runtime.rb +78 -76
  101. data/lib/cucumber/step_definition_light.rb +4 -3
  102. data/lib/cucumber/step_definitions.rb +2 -2
  103. data/lib/cucumber/step_match.rb +17 -20
  104. data/lib/cucumber/step_match_search.rb +5 -3
  105. data/lib/cucumber/term/ansicolor.rb +72 -48
  106. data/lib/cucumber/term/banner.rb +57 -0
  107. data/lib/cucumber/version +1 -1
  108. data/lib/cucumber.rb +3 -2
  109. data/lib/simplecov_setup.rb +1 -1
  110. metadata +279 -81
  111. data/lib/cucumber/core_ext/string.rb +0 -11
  112. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  113. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  114. data/lib/cucumber/formatter/cucumber.css +0 -286
  115. data/lib/cucumber/formatter/cucumber.sass +0 -247
  116. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  117. data/lib/cucumber/formatter/html_builder.rb +0 -121
  118. data/lib/cucumber/formatter/inline-js.js +0 -30
  119. data/lib/cucumber/formatter/jquery-min.js +0 -154
  120. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  121. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  122. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  123. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  124. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  125. data/lib/cucumber/step_argument.rb +0 -25
@@ -2,9 +2,10 @@
2
2
 
3
3
  Autotest.add_discovery do
4
4
  if File.directory?('features')
5
- if ENV['AUTOFEATURE'] =~ /true/i
5
+ case ENV['AUTOFEATURE']
6
+ when /true/i
6
7
  'cucumber'
7
- elsif ENV['AUTOFEATURE'] =~ /false/i
8
+ when /false/i
8
9
  # noop
9
10
  else
10
11
  puts '(Not running features. To run features in autotest, set AUTOFEATURE=true.)'
@@ -9,7 +9,9 @@ require 'cucumber'
9
9
  module Cucumber
10
10
  module Cli
11
11
  class YmlLoadError < StandardError; end
12
+
12
13
  class ProfilesNotDefinedError < YmlLoadError; end
14
+
13
15
  class ProfileNotFound < StandardError; end
14
16
 
15
17
  class Configuration
@@ -17,10 +19,10 @@ module Cucumber
17
19
 
18
20
  attr_reader :out_stream
19
21
 
20
- def initialize(out_stream = STDOUT, error_stream = STDERR)
22
+ def initialize(out_stream = $stdout, error_stream = $stderr)
21
23
  @out_stream = out_stream
22
24
  @error_stream = error_stream
23
- @options = Options.new(@out_stream, @error_stream, :default_profile => 'default')
25
+ @options = Options.new(@out_stream, @error_stream, default_profile: 'default')
24
26
  end
25
27
 
26
28
  def parse!(args)
@@ -28,6 +30,7 @@ module Cucumber
28
30
  @options.parse!(args)
29
31
  arrange_formats
30
32
  raise("You can't use both --strict and --wip") if strict.strict? && wip?
33
+
31
34
  set_environment_variables
32
35
  end
33
36
 
@@ -64,7 +67,7 @@ module Cucumber
64
67
  end
65
68
 
66
69
  def fail_fast?
67
- !!@options[:fail_fast]
70
+ @options[:fail_fast]
68
71
  end
69
72
 
70
73
  def retry_attempts
@@ -79,7 +82,7 @@ module Cucumber
79
82
  logger = Logger.new(@out_stream)
80
83
  logger.formatter = LogFormatter.new
81
84
  logger.level = Logger::INFO
82
- logger.level = Logger::DEBUG if self.verbose?
85
+ logger.level = Logger::DEBUG if verbose?
83
86
  logger
84
87
  end
85
88
 
@@ -108,7 +111,7 @@ module Cucumber
108
111
  end
109
112
 
110
113
  def to_hash
111
- Hash(@options).merge(out_stream: @out_stream, error_stream: @error_stream)
114
+ Hash(@options).merge(out_stream: @out_stream, error_stream: @error_stream, seed: seed)
112
115
  end
113
116
 
114
117
  private
@@ -126,12 +129,34 @@ module Cucumber
126
129
  end
127
130
 
128
131
  def arrange_formats
129
- @options[:formats] << ['pretty', {}, @out_stream] if @options[:formats].empty?
132
+ add_default_formatter if needs_default_formatter?
133
+
130
134
  @options[:formats] = @options[:formats].sort_by do |f|
131
135
  f[2] == @out_stream ? -1 : 1
132
136
  end
133
137
  @options[:formats].uniq!
134
- @options.check_formatter_stream_conflicts()
138
+ @options.check_formatter_stream_conflicts
139
+ end
140
+
141
+ def add_default_formatter
142
+ @options[:formats] << ['pretty', {}, @out_stream]
143
+ end
144
+
145
+ def needs_default_formatter?
146
+ formatter_missing? || publish_only?
147
+ end
148
+
149
+ def formatter_missing?
150
+ @options[:formats].empty?
151
+ end
152
+
153
+ def publish_only?
154
+ @options[:formats]
155
+ .uniq
156
+ .map { |formatter, _, stream| [formatter, stream] }
157
+ .uniq
158
+ .reject { |formatter, stream| formatter == 'message' && stream != @out_stream }
159
+ .empty?
135
160
  end
136
161
  end
137
162
  end
@@ -14,7 +14,7 @@ module Cucumber
14
14
  end
15
15
  end
16
16
 
17
- def initialize(args, _ = nil, out = STDOUT, err = STDERR, kernel = Kernel)
17
+ def initialize(args, out = $stdout, err = $stderr, kernel = Kernel)
18
18
  @args = args
19
19
  @out = out
20
20
  @err = err
@@ -24,22 +24,15 @@ module Cucumber
24
24
  def execute!(existing_runtime = nil)
25
25
  trap_interrupt
26
26
 
27
- runtime = if existing_runtime
28
- existing_runtime.configure(configuration)
29
- existing_runtime
30
- else
31
- Runtime.new(configuration)
32
- end
27
+ runtime = runtime(existing_runtime)
33
28
 
34
29
  runtime.run!
35
30
  if Cucumber.wants_to_quit
36
31
  exit_unable_to_finish
32
+ elsif runtime.failure?
33
+ exit_tests_failed
37
34
  else
38
- if runtime.failure?
39
- exit_tests_failed
40
- else
41
- exit_ok
42
- end
35
+ exit_ok
43
36
  end
44
37
  rescue SystemExit => e
45
38
  @kernel.exit(e.status)
@@ -48,7 +41,7 @@ module Cucumber
48
41
  @err.puts("Couldn't open #{e.path}")
49
42
  exit_unable_to_finish
50
43
  rescue FeatureFolderNotFoundException => e
51
- @err.puts(e.message + '. You can use `cucumber --init` to get started.')
44
+ @err.puts("#{e.message}. You can use `cucumber --init` to get started.")
52
45
  exit_unable_to_finish
53
46
  rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
54
47
  @err.puts(e.message)
@@ -56,7 +49,7 @@ module Cucumber
56
49
  rescue Errno::EACCES, Errno::ENOENT => e
57
50
  @err.puts("#{e.message} (#{e.class})")
58
51
  exit_unable_to_finish
59
- rescue Exception => e
52
+ rescue Exception => e # rubocop:disable Lint/RescueException
60
53
  @err.puts("#{e.message} (#{e.class})")
61
54
  @err.puts(e.backtrace.join("\n"))
62
55
  exit_unable_to_finish
@@ -92,9 +85,17 @@ module Cucumber
92
85
  trap('INT') do
93
86
  exit_unable_to_finish! if Cucumber.wants_to_quit
94
87
  Cucumber.wants_to_quit = true
95
- STDERR.puts "\nExiting... Interrupt again to exit immediately."
88
+ $stderr.puts "\nExiting... Interrupt again to exit immediately."
89
+ exit_unable_to_finish
96
90
  end
97
91
  end
92
+
93
+ def runtime(existing_runtime)
94
+ return Runtime.new(configuration) unless existing_runtime
95
+
96
+ existing_runtime.configure(configuration)
97
+ existing_runtime
98
+ end
98
99
  end
99
100
  end
100
101
  end
@@ -9,26 +9,30 @@ require 'cucumber/core/test/result'
9
9
  module Cucumber
10
10
  module Cli
11
11
  class Options
12
+ CUCUMBER_PUBLISH_URL = ENV['CUCUMBER_PUBLISH_URL'] || 'https://messages.cucumber.io/api/reports -X GET'
12
13
  INDENT = ' ' * 53
13
- # rubocop:disable Layout/MultilineOperationIndentation
14
14
  BUILTIN_FORMATS = {
15
- 'html' => ['Cucumber::Formatter::Html', 'Generates a nice looking HTML report.'],
16
15
  'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
17
16
  'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
18
17
  'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
19
- 'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" +
20
- "#{INDENT}The slowest step definitions (with duration) are\n" +
21
- "#{INDENT}listed first. If --dry-run is used the duration\n" +
22
- "#{INDENT}is not shown, and step definitions are sorted by\n" +
18
+ 'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" \
19
+ "#{INDENT}The slowest step definitions (with duration) are\n" \
20
+ "#{INDENT}listed first. If --dry-run is used the duration\n" \
21
+ "#{INDENT}is not shown, and step definitions are sorted by\n" \
23
22
  "#{INDENT}filename instead."],
24
- 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" +
23
+ 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
25
24
  "#{INDENT}the usage formatter, except that steps are not printed."],
26
- 'junit' => ['Cucumber::Formatter::Junit', 'Generates a report similar to Ant+JUnit.'],
27
- 'json' => ['Cucumber::Formatter::Json', 'Prints the feature as JSON'],
28
- 'json_pretty' => ['Cucumber::Formatter::JsonPretty', 'Prints the feature as prettified JSON'],
25
+ 'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
26
+ "#{INDENT}junit,fileattribute=true to include a file attribute."],
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.'],
33
+ 'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
29
34
  'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
30
- }
31
- # rubocop:enable Layout/MultilineOperationIndentation
35
+ }.freeze
32
36
  max = BUILTIN_FORMATS.keys.map(&:length).max
33
37
  FORMAT_HELP_MSG = [
34
38
  'Use --format rerun --out rerun.txt to write out failing',
@@ -41,31 +45,31 @@ module Cucumber
41
45
  'foo/bar_zap.rb. You can place the file with this relative',
42
46
  'path underneath your features/support directory or anywhere',
43
47
  "on Ruby's LOAD_PATH, for example in a Ruby gem."
44
- ]
48
+ ].freeze
45
49
 
46
50
  FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
47
51
  " #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
48
52
  end) + FORMAT_HELP_MSG
49
- PROFILE_SHORT_FLAG = '-p'
50
- NO_PROFILE_SHORT_FLAG = '-P'
51
- PROFILE_LONG_FLAG = '--profile'
52
- NO_PROFILE_LONG_FLAG = '--no-profile'
53
- FAIL_FAST_FLAG = '--fail-fast'
54
- RETRY_FLAG = '--retry'
53
+ PROFILE_SHORT_FLAG = '-p'.freeze
54
+ NO_PROFILE_SHORT_FLAG = '-P'.freeze
55
+ PROFILE_LONG_FLAG = '--profile'.freeze
56
+ NO_PROFILE_LONG_FLAG = '--no-profile'.freeze
57
+ FAIL_FAST_FLAG = '--fail-fast'.freeze
58
+ RETRY_FLAG = '--retry'.freeze
55
59
  OPTIONS_WITH_ARGS = [
56
60
  '-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
57
61
  '--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
58
62
  PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, '-l',
59
63
  '--lines', '--port', '-I', '--snippet-type'
60
- ]
61
- ORDER_TYPES = %w{defined random}
62
- TAG_LIMIT_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x
64
+ ].freeze
65
+ ORDER_TYPES = %w[defined random].freeze
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
 
@@ -87,19 +91,21 @@ module Cucumber
87
91
  @options[key] = value
88
92
  end
89
93
 
90
- def parse!(args) # rubocop:disable Metrics/AbcSize
94
+ def parse!(args) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
91
95
  @args = args
92
96
  @expanded_args = @args.dup
93
97
 
94
98
  @args.extend(::OptionParser::Arguable)
95
99
 
96
- @args.options do |opts|
100
+ @args.options do |opts| # rubocop:disable Metrics/BlockLength
97
101
  opts.banner = banner
102
+ opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
103
+ set_option :publish_enabled, true
104
+ end
105
+ opts.on('--publish-quiet', 'Don\'t print information banner about publishing reports') { set_option :publish_quiet }
98
106
  opts.on('-r LIBRARY|DIR', '--require LIBRARY|DIR', *require_files_msg) { |lib| require_files(lib) }
99
107
 
100
- if Cucumber::JRUBY
101
- opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) }
102
- end
108
+ opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY
103
109
 
104
110
  opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
105
111
  opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
@@ -108,20 +114,20 @@ module Cucumber
108
114
  opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
109
115
  add_option :formats, [*parse_formats(v), @out_stream]
110
116
  end
111
- opts.on('--init', *init_msg) { |v| initialize_project }
112
- opts.on('-o', '--out [FILE|DIR]', *out_msg) { |v| out_stream v }
117
+ opts.on('--init', *init_msg) { |_v| initialize_project }
118
+ opts.on('-o', '--out [FILE|DIR|URL]', *out_msg) { |v| out_stream v }
113
119
  opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag v }
114
120
  opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option :name_regexps, /#{v}/ }
115
121
  opts.on('-e', '--exclude PATTERN', *exclude_msg) { |v| add_option :excludes, Regexp.new(v) }
116
122
  opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) { |v| add_profile v }
117
- opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |v| disable_profile_loading }
123
+ opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |_v| disable_profile_loading }
118
124
  opts.on('-c', '--[no-]color', *color_msg) { |v| color v }
119
125
  opts.on('-d', '--dry-run', *dry_run_msg) { set_dry_run_and_duration }
120
126
  opts.on('-m', '--no-multiline', "Don't print multiline strings and tables under steps.") { set_option :no_multiline }
121
127
  opts.on('-s', '--no-source', "Don't print the file and line of the step definition with the steps.") { set_option :source, false }
122
128
  opts.on('-i', '--no-snippets', "Don't print snippets for pending steps.") { set_option :snippets, false }
123
129
  opts.on('-I', '--snippet-type TYPE', *snippet_type_msg) { |v| set_option :snippet_type, v.to_sym }
124
- opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source.') { shut_up }
130
+ opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source --no-duration --publish-quiet.') { shut_up }
125
131
  opts.on('--no-duration', "Don't print the duration at the end of the summary") { set_option :duration, false }
126
132
  opts.on('-b', '--backtrace', 'Show full backtrace for all errors.') { Cucumber.use_full_backtrace = true }
127
133
  opts.on('-S', '--[no-]strict', *strict_msg) { |setting| set_strict(setting) }
@@ -140,23 +146,23 @@ module Cucumber
140
146
  [random] Shuffle scenarios before running.
141
147
  Specify SEED to reproduce the shuffling from a previous run.
142
148
  e.g. --order random:5738
143
- TEXT
149
+ TEXT
144
150
  @options[:order], @options[:seed] = *order.split(':')
145
- unless ORDER_TYPES.include?(@options[:order])
146
- fail "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(", ")}."
147
- end
151
+ raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
148
152
  end
149
153
 
150
154
  opts.on_tail('--version', 'Show version.') { exit_ok(Cucumber::VERSION) }
151
155
  opts.on_tail('-h', '--help', "You're looking at it.") { exit_ok(opts.help) }
152
156
  end.parse!
153
157
 
158
+ process_publish_options
159
+
154
160
  @args.map! { |a| "#{a}:#{@options[:lines]}" } if @options[:lines]
155
161
 
156
162
  extract_environment_variables
157
163
  @options[:paths] = @args.dup # whatver is left over
158
164
 
159
- check_formatter_stream_conflicts()
165
+ check_formatter_stream_conflicts
160
166
 
161
167
  merge_profiles
162
168
 
@@ -171,9 +177,10 @@ TEXT
171
177
  @options[:filters] ||= []
172
178
  end
173
179
 
174
- def check_formatter_stream_conflicts()
180
+ def check_formatter_stream_conflicts
175
181
  streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
176
182
  return if streams == streams.uniq
183
+
177
184
  raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
178
185
  end
179
186
 
@@ -188,6 +195,19 @@ TEXT
188
195
 
189
196
  private
190
197
 
198
+ def process_publish_options
199
+ @options[:publish_enabled] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_ENABLED']) || ENV['CUCUMBER_PUBLISH_TOKEN']
200
+ @options[:formats] << publisher if @options[:publish_enabled]
201
+
202
+ @options[:publish_quiet] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_QUIET'])
203
+ end
204
+
205
+ def truthy_string?(str)
206
+ return false if str.nil?
207
+
208
+ str !~ /^(false|no|0)$/i
209
+ end
210
+
191
211
  def color_msg
192
212
  [
193
213
  'Whether or not to use ANSI color in the output. Cucumber decides',
@@ -196,10 +216,7 @@ TEXT
196
216
  end
197
217
 
198
218
  def dry_run_msg
199
- [
200
- 'Invokes formatters without executing the steps.',
201
- 'This also omits the loading of your support/env.rb file if it exists.'
202
- ]
219
+ ['Invokes formatters without executing the steps.']
203
220
  end
204
221
 
205
222
  def exclude_msg
@@ -219,7 +236,7 @@ TEXT
219
236
  def i18n_keywords_msg
220
237
  [
221
238
  'List keywords for in a particular language',
222
- %{Run with "--i18n help" to see all languages}
239
+ %(Run with "--i18n help" to see all languages)
223
240
  ]
224
241
  end
225
242
 
@@ -305,10 +322,14 @@ TEXT
305
322
 
306
323
  def out_msg
307
324
  [
308
- 'Write output to a file/directory instead of STDOUT. This option',
325
+ 'Write output to a file/directory/URL instead of STDOUT. This option',
309
326
  'applies to the previously specified --format, or the',
310
327
  'default format if no format is specified. Check the specific',
311
- "formatter's docs to see whether to pass a file or a dir."
328
+ "formatter's docs to see whether to pass a file, dir or URL.",
329
+ "\n",
330
+ 'When using a URL, the output of the formatter will be sent as the HTTP request body.',
331
+ 'HTTP headers and request method can be set with cURL like options.',
332
+ 'Example: --out "http://example.com -X POST -H Content-Type:text/json"'
312
333
  ]
313
334
  end
314
335
 
@@ -316,11 +337,13 @@ TEXT
316
337
  [
317
338
  'Require files before executing the features. If this',
318
339
  'option is not specified, all *.rb files that are',
319
- 'siblings or below the features will be loaded auto-',
340
+ 'siblings of or below the features will be loaded auto-',
320
341
  'matically. Automatic loading is disabled when this',
321
- 'option is specified, and all loading becomes explicit.',
322
- 'Files under directories named "support" are always',
323
- 'loaded first.',
342
+ 'option is specified; all loading becomes explicit.',
343
+ 'Files in directories named "support" are still always',
344
+ 'loaded first when their parent directories are',
345
+ 'required or if the "support" directories themselves are',
346
+ 'explicitly required.',
324
347
  'This option can be specified multiple times.'
325
348
  ]
326
349
  end
@@ -346,18 +369,26 @@ TEXT
346
369
  def require_files(v)
347
370
  @options[:require] << v
348
371
  return unless Cucumber::JRUBY && File.directory?(v)
372
+
349
373
  require 'java'
350
374
  $CLASSPATH << v
351
375
  end
352
376
 
353
377
  def require_jars(jars)
354
- Dir["#{jars}/**/*.jar"].each { |jar| require jar }
378
+ Dir["#{jars}/**/*.jar"].sort.each { |jar| require jar }
379
+ end
380
+
381
+ def publisher
382
+ url = CUCUMBER_PUBLISH_URL
383
+ url += %( -H "Authorization: Bearer #{ENV['CUCUMBER_PUBLISH_TOKEN']}") if ENV['CUCUMBER_PUBLISH_TOKEN']
384
+ ['message', {}, url]
355
385
  end
356
386
 
357
387
  def language(lang)
358
388
  require 'gherkin/dialect'
359
389
 
360
- return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.keys.include? lang
390
+ return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.key?(lang)
391
+
361
392
  list_keywords_and_exit(lang)
362
393
  end
363
394
 
@@ -366,7 +397,7 @@ TEXT
366
397
  end
367
398
 
368
399
  def non_stdout_formats
369
- @options[:formats].select { |_, _, output| output != @out_stream }
400
+ @options[:formats].reject { |_, _, output| output == @out_stream }
370
401
  end
371
402
 
372
403
  def add_option(option, value)
@@ -374,8 +405,9 @@ TEXT
374
405
  end
375
406
 
376
407
  def add_tag(value)
377
- warn("Deprecated: Found tags option '#{value}'. Support for '~@tag' will be removed from the next release of Cucumber. Please use 'not @tag' instead.") if value.include?('~')
378
- warn("Deprecated: Found tags option '#{value}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.") if value.include?(',')
408
+ raise("Found tags option '#{value}'. '~@tag' is no longer supported, use 'not @tag' instead.") if value.include?('~')
409
+ raise("Found tags option '#{value}'. '@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.") if value.include?(',')
410
+
379
411
  @options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
380
412
  add_tag_limits(value)
381
413
  end
@@ -387,9 +419,8 @@ TEXT
387
419
  end
388
420
 
389
421
  def add_tag_limit(tag_limits, tag_name, limit)
390
- if tag_limits[tag_name] && tag_limits[tag_name] != limit
391
- raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}"
392
- end
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
+
393
424
  tag_limits[tag_name] = limit
394
425
  end
395
426
 
@@ -420,6 +451,7 @@ TEXT
420
451
  end
421
452
 
422
453
  def shut_up
454
+ @options[:publish_quiet] = true
423
455
  @options[:snippets] = false
424
456
  @options[:source] = false
425
457
  @options[:duration] = false
@@ -436,7 +468,7 @@ TEXT
436
468
  def extract_environment_variables
437
469
  @args.delete_if do |arg|
438
470
  if arg =~ /^(\w+)=(.*)$/
439
- @options[:env_vars][$1] = $2
471
+ @options[:env_vars][Regexp.last_match(1)] = Regexp.last_match(2)
440
472
  true
441
473
  end
442
474
  end
@@ -465,8 +497,8 @@ TEXT
465
497
  profile_args = profile_loader.args_from(profile)
466
498
  profile_options = Options.parse(
467
499
  profile_args, @out_stream, @error_stream,
468
- :skip_profile_information => true,
469
- :profile_loader => profile_loader
500
+ skip_profile_information: true,
501
+ profile_loader: profile_loader
470
502
  )
471
503
  reverse_merge(profile_options)
472
504
  end
@@ -474,14 +506,14 @@ TEXT
474
506
  def default_profile_should_be_used?
475
507
  @profiles.empty? &&
476
508
  profile_loader.cucumber_yml_defined? &&
477
- profile_loader.has_profile?(@default_profile)
509
+ profile_loader.profile?(@default_profile)
478
510
  end
479
511
 
480
512
  def profile_loader
481
513
  @profile_loader ||= ProfileLoader.new
482
514
  end
483
515
 
484
- def reverse_merge(other_options)
516
+ def reverse_merge(other_options) # rubocop:disable Metrics/AbcSize
485
517
  @options = other_options.options.merge(@options)
486
518
  @options[:require] += other_options[:require]
487
519
  @options[:excludes] += other_options[:excludes]
@@ -510,7 +542,7 @@ TEXT
510
542
  @options[:formats] = stdout_formats[0..0] + non_stdout_formats
511
543
  end
512
544
 
513
- @options[:retry] = other_options[:retry] if @options[:retry] == 0
545
+ @options[:retry] = other_options[:retry] if @options[:retry].zero?
514
546
 
515
547
  self
516
548
  end
@@ -546,7 +578,7 @@ TEXT
546
578
  ['but (code)', to_code_keywords_string(language.but_keywords)]
547
579
  ]
548
580
  )
549
- @out_stream.write(data.to_s({ color: false, prefixes: Hash.new('') }))
581
+ @out_stream.write(data.to_s(color: false, prefixes: Hash.new('')))
550
582
  Kernel.exit(0)
551
583
  end
552
584
 
@@ -557,7 +589,7 @@ TEXT
557
589
  [key, ::Gherkin::DIALECTS[key].fetch('name'), ::Gherkin::DIALECTS[key].fetch('native')]
558
590
  end
559
591
  )
560
- @out_stream.write(data.to_s({ color: false, prefixes: Hash.new('') }))
592
+ @out_stream.write(data.to_s(color: false, prefixes: Hash.new('')))
561
593
  Kernel.exit(0)
562
594
  end
563
595
 
@@ -571,20 +603,20 @@ TEXT
571
603
 
572
604
  def default_options
573
605
  {
574
- :strict => Cucumber::Core::Test::Result::StrictConfiguration.new,
575
- :require => [],
576
- :dry_run => false,
577
- :formats => [],
578
- :excludes => [],
579
- :tag_expressions => [],
580
- :tag_limits => {},
581
- :name_regexps => [],
582
- :env_vars => {},
583
- :diff_enabled => true,
584
- :snippets => true,
585
- :source => true,
586
- :duration => true,
587
- :retry => 0
606
+ strict: Cucumber::Core::Test::Result::StrictConfiguration.new,
607
+ require: [],
608
+ dry_run: false,
609
+ formats: [],
610
+ excludes: [],
611
+ tag_expressions: [],
612
+ tag_limits: {},
613
+ name_regexps: [],
614
+ env_vars: {},
615
+ diff_enabled: true,
616
+ snippets: true,
617
+ source: true,
618
+ duration: true,
619
+ retry: 0
588
620
  }
589
621
  end
590
622
  end