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
@@ -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