cucumber 3.1.2 → 5.2.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 (115) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +287 -14
  3. data/CONTRIBUTING.md +11 -25
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +46 -53
  7. data/lib/cucumber.rb +1 -1
  8. data/lib/cucumber/cli/configuration.rb +5 -5
  9. data/lib/cucumber/cli/main.rb +12 -12
  10. data/lib/cucumber/cli/options.rb +97 -76
  11. data/lib/cucumber/cli/profile_loader.rb +49 -26
  12. data/lib/cucumber/configuration.rb +44 -29
  13. data/lib/cucumber/constantize.rb +2 -5
  14. data/lib/cucumber/deprecate.rb +31 -7
  15. data/lib/cucumber/errors.rb +5 -7
  16. data/lib/cucumber/events.rb +13 -6
  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 +13 -0
  20. data/lib/cucumber/events/step_activated.rb +2 -1
  21. data/lib/cucumber/events/test_case_created.rb +13 -0
  22. data/lib/cucumber/events/test_case_ready.rb +12 -0
  23. data/lib/cucumber/events/test_step_created.rb +13 -0
  24. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  25. data/lib/cucumber/file_specs.rb +6 -6
  26. data/lib/cucumber/filters.rb +1 -0
  27. data/lib/cucumber/filters/activate_steps.rb +5 -3
  28. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  29. data/lib/cucumber/filters/prepare_world.rb +5 -9
  30. data/lib/cucumber/filters/quit.rb +1 -3
  31. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  32. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  33. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  34. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  35. data/lib/cucumber/formatter/console.rb +58 -66
  36. data/lib/cucumber/formatter/console_counts.rb +4 -9
  37. data/lib/cucumber/formatter/console_issues.rb +6 -3
  38. data/lib/cucumber/formatter/duration.rb +1 -1
  39. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  40. data/lib/cucumber/formatter/errors.rb +6 -0
  41. data/lib/cucumber/formatter/fanout.rb +2 -0
  42. data/lib/cucumber/formatter/html.rb +11 -598
  43. data/lib/cucumber/formatter/http_io.rb +147 -0
  44. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  45. data/lib/cucumber/formatter/interceptor.rb +11 -30
  46. data/lib/cucumber/formatter/io.rb +55 -13
  47. data/lib/cucumber/formatter/json.rb +102 -110
  48. data/lib/cucumber/formatter/junit.rb +55 -55
  49. data/lib/cucumber/formatter/message.rb +22 -0
  50. data/lib/cucumber/formatter/message_builder.rb +255 -0
  51. data/lib/cucumber/formatter/pretty.rb +359 -153
  52. data/lib/cucumber/formatter/progress.rb +30 -32
  53. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  54. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  55. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  56. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  57. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  58. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  59. data/lib/cucumber/formatter/rerun.rb +22 -4
  60. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  61. data/lib/cucumber/formatter/steps.rb +3 -4
  62. data/lib/cucumber/formatter/summary.rb +16 -8
  63. data/lib/cucumber/formatter/unicode.rb +15 -17
  64. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  65. data/lib/cucumber/formatter/usage.rb +11 -10
  66. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  67. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  68. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  69. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  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 +42 -33
  73. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  74. data/lib/cucumber/glue/snippet.rb +23 -22
  75. data/lib/cucumber/glue/step_definition.rb +42 -19
  76. data/lib/cucumber/glue/world_factory.rb +1 -1
  77. data/lib/cucumber/hooks.rb +11 -11
  78. data/lib/cucumber/multiline_argument.rb +4 -6
  79. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  80. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
  81. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  82. data/lib/cucumber/platform.rb +3 -3
  83. data/lib/cucumber/rake/task.rb +16 -18
  84. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  85. data/lib/cucumber/rspec/doubles.rb +3 -5
  86. data/lib/cucumber/running_test_case.rb +2 -53
  87. data/lib/cucumber/runtime.rb +41 -58
  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/step_definition_light.rb +4 -3
  95. data/lib/cucumber/step_definitions.rb +2 -2
  96. data/lib/cucumber/step_match.rb +12 -11
  97. data/lib/cucumber/step_match_search.rb +2 -1
  98. data/lib/cucumber/term/ansicolor.rb +9 -9
  99. data/lib/cucumber/term/banner.rb +56 -0
  100. data/lib/cucumber/version +1 -1
  101. metadata +254 -83
  102. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  103. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  104. data/lib/cucumber/formatter/cucumber.css +0 -286
  105. data/lib/cucumber/formatter/cucumber.sass +0 -247
  106. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  107. data/lib/cucumber/formatter/html_builder.rb +0 -121
  108. data/lib/cucumber/formatter/inline-js.js +0 -30
  109. data/lib/cucumber/formatter/jquery-min.js +0 -154
  110. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  111. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  112. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  113. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  114. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  115. data/lib/cucumber/step_argument.rb +0 -25
@@ -10,7 +10,7 @@ require 'cucumber/term/ansicolor'
10
10
 
11
11
  module Cucumber
12
12
  class << self
13
- attr_accessor :wants_to_quit
13
+ attr_accessor :wants_to_quit, :use_legacy_autoloader
14
14
 
15
15
  def logger
16
16
  return @log if @log
@@ -20,7 +20,7 @@ module Cucumber
20
20
  def initialize(out_stream = STDOUT, error_stream = STDERR)
21
21
  @out_stream = out_stream
22
22
  @error_stream = error_stream
23
- @options = Options.new(@out_stream, @error_stream, :default_profile => 'default')
23
+ @options = Options.new(@out_stream, @error_stream, default_profile: 'default')
24
24
  end
25
25
 
26
26
  def parse!(args)
@@ -64,7 +64,7 @@ module Cucumber
64
64
  end
65
65
 
66
66
  def fail_fast?
67
- !!@options[:fail_fast]
67
+ @options[:fail_fast]
68
68
  end
69
69
 
70
70
  def retry_attempts
@@ -79,7 +79,7 @@ module Cucumber
79
79
  logger = Logger.new(@out_stream)
80
80
  logger.formatter = LogFormatter.new
81
81
  logger.level = Logger::INFO
82
- logger.level = Logger::DEBUG if self.verbose?
82
+ logger.level = Logger::DEBUG if verbose?
83
83
  logger
84
84
  end
85
85
 
@@ -108,7 +108,7 @@ module Cucumber
108
108
  end
109
109
 
110
110
  def to_hash
111
- Hash(@options).merge(out_stream: @out_stream, error_stream: @error_stream)
111
+ Hash(@options).merge(out_stream: @out_stream, error_stream: @error_stream, seed: seed)
112
112
  end
113
113
 
114
114
  private
@@ -131,7 +131,7 @@ module Cucumber
131
131
  f[2] == @out_stream ? -1 : 1
132
132
  end
133
133
  @options[:formats].uniq!
134
- @options.check_formatter_stream_conflicts()
134
+ @options.check_formatter_stream_conflicts
135
135
  end
136
136
  end
137
137
  end
@@ -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)
@@ -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
@@ -93,8 +86,15 @@ module Cucumber
93
86
  exit_unable_to_finish! if Cucumber.wants_to_quit
94
87
  Cucumber.wants_to_quit = true
95
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
+ existing_runtime.configure(configuration)
96
+ existing_runtime
97
+ end
98
98
  end
99
99
  end
100
100
  end
@@ -9,26 +9,25 @@ 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
25
  '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'],
26
+ 'json' => ['Cucumber::Formatter::Json', '[DEPRECATED] Prints the feature as JSON'],
27
+ 'message' => ['Cucumber::Formatter::Message', 'Outputs protobuf messages'],
28
+ 'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
29
29
  'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
30
- }
31
- # rubocop:enable Layout/MultilineOperationIndentation
30
+ }.freeze
32
31
  max = BUILTIN_FORMATS.keys.map(&:length).max
33
32
  FORMAT_HELP_MSG = [
34
33
  'Use --format rerun --out rerun.txt to write out failing',
@@ -41,24 +40,24 @@ module Cucumber
41
40
  'foo/bar_zap.rb. You can place the file with this relative',
42
41
  'path underneath your features/support directory or anywhere',
43
42
  "on Ruby's LOAD_PATH, for example in a Ruby gem."
44
- ]
43
+ ].freeze
45
44
 
46
45
  FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
47
46
  " #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
48
47
  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'
48
+ PROFILE_SHORT_FLAG = '-p'.freeze
49
+ NO_PROFILE_SHORT_FLAG = '-P'.freeze
50
+ PROFILE_LONG_FLAG = '--profile'.freeze
51
+ NO_PROFILE_LONG_FLAG = '--no-profile'.freeze
52
+ FAIL_FAST_FLAG = '--fail-fast'.freeze
53
+ RETRY_FLAG = '--retry'.freeze
55
54
  OPTIONS_WITH_ARGS = [
56
55
  '-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
57
56
  '--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
58
57
  PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, '-l',
59
58
  '--lines', '--port', '-I', '--snippet-type'
60
- ]
61
- ORDER_TYPES = %w{defined random}
59
+ ].freeze
60
+ ORDER_TYPES = %w[defined random].freeze
62
61
  TAG_LIMIT_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x
63
62
 
64
63
  def self.parse(args, out_stream, error_stream, options = {})
@@ -87,19 +86,21 @@ module Cucumber
87
86
  @options[key] = value
88
87
  end
89
88
 
90
- def parse!(args) # rubocop:disable Metrics/AbcSize
89
+ def parse!(args) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
91
90
  @args = args
92
91
  @expanded_args = @args.dup
93
92
 
94
93
  @args.extend(::OptionParser::Arguable)
95
94
 
96
- @args.options do |opts|
95
+ @args.options do |opts| # rubocop:disable Metrics/BlockLength
97
96
  opts.banner = banner
97
+ opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
98
+ set_option :publish_enabled, true
99
+ end
100
+ opts.on('--publish-quiet', 'Don\'t print information banner about publishing reports') { set_option :publish_quiet }
98
101
  opts.on('-r LIBRARY|DIR', '--require LIBRARY|DIR', *require_files_msg) { |lib| require_files(lib) }
99
102
 
100
- if Cucumber::JRUBY
101
- opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) }
102
- end
103
+ opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY
103
104
 
104
105
  opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
105
106
  opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
@@ -108,20 +109,20 @@ module Cucumber
108
109
  opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
109
110
  add_option :formats, [*parse_formats(v), @out_stream]
110
111
  end
111
- opts.on('--init', *init_msg) { |v| initialize_project }
112
- opts.on('-o', '--out [FILE|DIR]', *out_msg) { |v| out_stream v }
112
+ opts.on('--init', *init_msg) { |_v| initialize_project }
113
+ opts.on('-o', '--out [FILE|DIR|URL]', *out_msg) { |v| out_stream v }
113
114
  opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag v }
114
115
  opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option :name_regexps, /#{v}/ }
115
116
  opts.on('-e', '--exclude PATTERN', *exclude_msg) { |v| add_option :excludes, Regexp.new(v) }
116
117
  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 }
118
+ opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |_v| disable_profile_loading }
118
119
  opts.on('-c', '--[no-]color', *color_msg) { |v| color v }
119
120
  opts.on('-d', '--dry-run', *dry_run_msg) { set_dry_run_and_duration }
120
121
  opts.on('-m', '--no-multiline', "Don't print multiline strings and tables under steps.") { set_option :no_multiline }
121
122
  opts.on('-s', '--no-source', "Don't print the file and line of the step definition with the steps.") { set_option :source, false }
122
123
  opts.on('-i', '--no-snippets', "Don't print snippets for pending steps.") { set_option :snippets, false }
123
124
  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 }
125
+ opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source --no-duration --publish-quiet.') { shut_up }
125
126
  opts.on('--no-duration', "Don't print the duration at the end of the summary") { set_option :duration, false }
126
127
  opts.on('-b', '--backtrace', 'Show full backtrace for all errors.') { Cucumber.use_full_backtrace = true }
127
128
  opts.on('-S', '--[no-]strict', *strict_msg) { |setting| set_strict(setting) }
@@ -140,23 +141,23 @@ module Cucumber
140
141
  [random] Shuffle scenarios before running.
141
142
  Specify SEED to reproduce the shuffling from a previous run.
142
143
  e.g. --order random:5738
143
- TEXT
144
+ TEXT
144
145
  @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
146
+ raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
148
147
  end
149
148
 
150
149
  opts.on_tail('--version', 'Show version.') { exit_ok(Cucumber::VERSION) }
151
150
  opts.on_tail('-h', '--help', "You're looking at it.") { exit_ok(opts.help) }
152
151
  end.parse!
153
152
 
153
+ process_publish_options
154
+
154
155
  @args.map! { |a| "#{a}:#{@options[:lines]}" } if @options[:lines]
155
156
 
156
157
  extract_environment_variables
157
158
  @options[:paths] = @args.dup # whatver is left over
158
159
 
159
- check_formatter_stream_conflicts()
160
+ check_formatter_stream_conflicts
160
161
 
161
162
  merge_profiles
162
163
 
@@ -171,7 +172,7 @@ TEXT
171
172
  @options[:filters] ||= []
172
173
  end
173
174
 
174
- def check_formatter_stream_conflicts()
175
+ def check_formatter_stream_conflicts
175
176
  streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
176
177
  return if streams == streams.uniq
177
178
  raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
@@ -188,6 +189,18 @@ TEXT
188
189
 
189
190
  private
190
191
 
192
+ def process_publish_options
193
+ @options[:publish_enabled] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_ENABLED']) || ENV['CUCUMBER_PUBLISH_TOKEN']
194
+ @options[:formats] << publisher if @options[:publish_enabled]
195
+
196
+ @options[:publish_quiet] = true if truthy_string?(ENV['CUCUMBER_PUBLISH_QUIET'])
197
+ end
198
+
199
+ def truthy_string?(str)
200
+ return false if str.nil?
201
+ str !~ /^(false|no|0)$/i
202
+ end
203
+
191
204
  def color_msg
192
205
  [
193
206
  'Whether or not to use ANSI color in the output. Cucumber decides',
@@ -196,10 +209,7 @@ TEXT
196
209
  end
197
210
 
198
211
  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
- ]
212
+ ['Invokes formatters without executing the steps.']
203
213
  end
204
214
 
205
215
  def exclude_msg
@@ -219,7 +229,7 @@ TEXT
219
229
  def i18n_keywords_msg
220
230
  [
221
231
  'List keywords for in a particular language',
222
- %{Run with "--i18n help" to see all languages}
232
+ %(Run with "--i18n help" to see all languages)
223
233
  ]
224
234
  end
225
235
 
@@ -305,10 +315,14 @@ TEXT
305
315
 
306
316
  def out_msg
307
317
  [
308
- 'Write output to a file/directory instead of STDOUT. This option',
318
+ 'Write output to a file/directory/URL instead of STDOUT. This option',
309
319
  'applies to the previously specified --format, or the',
310
320
  'default format if no format is specified. Check the specific',
311
- "formatter's docs to see whether to pass a file or a dir."
321
+ "formatter's docs to see whether to pass a file, dir or URL.",
322
+ "\n",
323
+ 'When using a URL, the output of the formatter will be sent as the HTTP request body.',
324
+ 'HTTP headers and request method can be set with cURL like options.',
325
+ 'Example: --out "http://example.com -X POST -H Content-Type:text/json"'
312
326
  ]
313
327
  end
314
328
 
@@ -316,11 +330,13 @@ TEXT
316
330
  [
317
331
  'Require files before executing the features. If this',
318
332
  'option is not specified, all *.rb files that are',
319
- 'siblings or below the features will be loaded auto-',
333
+ 'siblings of or below the features will be loaded auto-',
320
334
  '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.',
335
+ 'option is specified; all loading becomes explicit.',
336
+ 'Files in directories named "support" are still always',
337
+ 'loaded first when their parent directories are',
338
+ 'required or if the "support" directories themselves are',
339
+ 'explicitly required.',
324
340
  'This option can be specified multiple times.'
325
341
  ]
326
342
  end
@@ -351,13 +367,19 @@ TEXT
351
367
  end
352
368
 
353
369
  def require_jars(jars)
354
- Dir["#{jars}/**/*.jar"].each { |jar| require jar }
370
+ Dir["#{jars}/**/*.jar"].sort.each { |jar| require jar }
371
+ end
372
+
373
+ def publisher
374
+ url = CUCUMBER_PUBLISH_URL
375
+ url += %( -H "Authorization: Bearer #{ENV['CUCUMBER_PUBLISH_TOKEN']}") if ENV['CUCUMBER_PUBLISH_TOKEN']
376
+ ['message', {}, url]
355
377
  end
356
378
 
357
379
  def language(lang)
358
380
  require 'gherkin/dialect'
359
381
 
360
- return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.keys.include? lang
382
+ return indicate_invalid_language_and_exit(lang) unless ::Gherkin::DIALECTS.key?(lang)
361
383
  list_keywords_and_exit(lang)
362
384
  end
363
385
 
@@ -366,7 +388,7 @@ TEXT
366
388
  end
367
389
 
368
390
  def non_stdout_formats
369
- @options[:formats].select { |_, _, output| output != @out_stream }
391
+ @options[:formats].reject { |_, _, output| output == @out_stream }
370
392
  end
371
393
 
372
394
  def add_option(option, value)
@@ -374,8 +396,8 @@ TEXT
374
396
  end
375
397
 
376
398
  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?(',')
399
+ raise("Found tags option '#{value}'. '~@tag' is no longer supported, use 'not @tag' instead.") if value.include?('~')
400
+ raise("Found tags option '#{value}'. '@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.") if value.include?(',')
379
401
  @options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
380
402
  add_tag_limits(value)
381
403
  end
@@ -387,9 +409,7 @@ TEXT
387
409
  end
388
410
 
389
411
  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
412
+ raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}" if tag_limits[tag_name] && tag_limits[tag_name] != limit
393
413
  tag_limits[tag_name] = limit
394
414
  end
395
415
 
@@ -420,6 +440,7 @@ TEXT
420
440
  end
421
441
 
422
442
  def shut_up
443
+ @options[:publish_quiet] = true
423
444
  @options[:snippets] = false
424
445
  @options[:source] = false
425
446
  @options[:duration] = false
@@ -436,7 +457,7 @@ TEXT
436
457
  def extract_environment_variables
437
458
  @args.delete_if do |arg|
438
459
  if arg =~ /^(\w+)=(.*)$/
439
- @options[:env_vars][$1] = $2
460
+ @options[:env_vars][Regexp.last_match(1)] = Regexp.last_match(2)
440
461
  true
441
462
  end
442
463
  end
@@ -465,8 +486,8 @@ TEXT
465
486
  profile_args = profile_loader.args_from(profile)
466
487
  profile_options = Options.parse(
467
488
  profile_args, @out_stream, @error_stream,
468
- :skip_profile_information => true,
469
- :profile_loader => profile_loader
489
+ skip_profile_information: true,
490
+ profile_loader: profile_loader
470
491
  )
471
492
  reverse_merge(profile_options)
472
493
  end
@@ -474,14 +495,14 @@ TEXT
474
495
  def default_profile_should_be_used?
475
496
  @profiles.empty? &&
476
497
  profile_loader.cucumber_yml_defined? &&
477
- profile_loader.has_profile?(@default_profile)
498
+ profile_loader.profile?(@default_profile)
478
499
  end
479
500
 
480
501
  def profile_loader
481
502
  @profile_loader ||= ProfileLoader.new
482
503
  end
483
504
 
484
- def reverse_merge(other_options)
505
+ def reverse_merge(other_options) # rubocop:disable Metrics/AbcSize
485
506
  @options = other_options.options.merge(@options)
486
507
  @options[:require] += other_options[:require]
487
508
  @options[:excludes] += other_options[:excludes]
@@ -510,7 +531,7 @@ TEXT
510
531
  @options[:formats] = stdout_formats[0..0] + non_stdout_formats
511
532
  end
512
533
 
513
- @options[:retry] = other_options[:retry] if @options[:retry] == 0
534
+ @options[:retry] = other_options[:retry] if @options[:retry].zero?
514
535
 
515
536
  self
516
537
  end
@@ -546,7 +567,7 @@ TEXT
546
567
  ['but (code)', to_code_keywords_string(language.but_keywords)]
547
568
  ]
548
569
  )
549
- @out_stream.write(data.to_s({ color: false, prefixes: Hash.new('') }))
570
+ @out_stream.write(data.to_s(color: false, prefixes: Hash.new('')))
550
571
  Kernel.exit(0)
551
572
  end
552
573
 
@@ -557,7 +578,7 @@ TEXT
557
578
  [key, ::Gherkin::DIALECTS[key].fetch('name'), ::Gherkin::DIALECTS[key].fetch('native')]
558
579
  end
559
580
  )
560
- @out_stream.write(data.to_s({ color: false, prefixes: Hash.new('') }))
581
+ @out_stream.write(data.to_s(color: false, prefixes: Hash.new('')))
561
582
  Kernel.exit(0)
562
583
  end
563
584
 
@@ -571,20 +592,20 @@ TEXT
571
592
 
572
593
  def default_options
573
594
  {
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
595
+ strict: Cucumber::Core::Test::Result::StrictConfiguration.new,
596
+ require: [],
597
+ dry_run: false,
598
+ formats: [],
599
+ excludes: [],
600
+ tag_expressions: [],
601
+ tag_limits: {},
602
+ name_regexps: [],
603
+ env_vars: {},
604
+ diff_enabled: true,
605
+ snippets: true,
606
+ source: true,
607
+ duration: true,
608
+ retry: 0
588
609
  }
589
610
  end
590
611
  end