cucumber 8.0.0 → 9.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -23
  3. data/VERSION +1 -0
  4. data/lib/cucumber/cli/main.rb +1 -1
  5. data/lib/cucumber/cli/options.rb +66 -66
  6. data/lib/cucumber/cli/profile_loader.rb +5 -5
  7. data/lib/cucumber/configuration.rb +7 -2
  8. data/lib/cucumber/deprecate.rb +6 -47
  9. data/lib/cucumber/errors.rb +2 -1
  10. data/lib/cucumber/events/envelope.rb +2 -0
  11. data/lib/cucumber/events/gherkin_source_parsed.rb +2 -0
  12. data/lib/cucumber/events/gherkin_source_read.rb +2 -0
  13. data/lib/cucumber/events/test_case_finished.rb +2 -0
  14. data/lib/cucumber/events/test_case_started.rb +2 -0
  15. data/lib/cucumber/events/test_step_finished.rb +2 -0
  16. data/lib/cucumber/events/test_step_started.rb +2 -0
  17. data/lib/cucumber/events/undefined_parameter_type.rb +2 -0
  18. data/lib/cucumber/file_specs.rb +1 -1
  19. data/lib/cucumber/filters/retry.rb +20 -1
  20. data/lib/cucumber/formatter/ansicolor.rb +19 -27
  21. data/lib/cucumber/formatter/ast_lookup.rb +14 -6
  22. data/lib/cucumber/formatter/console.rb +16 -14
  23. data/lib/cucumber/formatter/console_counts.rb +3 -1
  24. data/lib/cucumber/formatter/console_issues.rb +4 -2
  25. data/lib/cucumber/formatter/curl_option_parser.rb +49 -0
  26. data/lib/cucumber/formatter/errors.rb +2 -0
  27. data/lib/cucumber/formatter/fail_fast.rb +1 -1
  28. data/lib/cucumber/formatter/html.rb +2 -0
  29. data/lib/cucumber/formatter/http_io.rb +10 -142
  30. data/lib/cucumber/formatter/io_http_buffer.rb +88 -0
  31. data/lib/cucumber/formatter/json.rb +2 -6
  32. data/lib/cucumber/formatter/junit.rb +4 -4
  33. data/lib/cucumber/formatter/message_builder.rb +21 -6
  34. data/lib/cucumber/formatter/pretty.rb +9 -5
  35. data/lib/cucumber/formatter/publish_banner_printer.rb +0 -2
  36. data/lib/cucumber/formatter/query/hook_by_test_step.rb +2 -0
  37. data/lib/cucumber/formatter/query/pickle_by_test.rb +2 -0
  38. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +2 -0
  39. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +2 -0
  40. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
  41. data/lib/cucumber/formatter/rerun.rb +3 -3
  42. data/lib/cucumber/formatter/unicode.rb +3 -3
  43. data/lib/cucumber/formatter/url_reporter.rb +3 -1
  44. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +23 -25
  45. data/lib/cucumber/glue/invoke_in_world.rb +2 -2
  46. data/lib/cucumber/glue/proto_world.rb +20 -25
  47. data/lib/cucumber/glue/registry_and_more.rb +9 -5
  48. data/lib/cucumber/glue/snippet.rb +4 -2
  49. data/lib/cucumber/glue/world_factory.rb +2 -0
  50. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -0
  51. data/lib/cucumber/multiline_argument/data_table.rb +34 -35
  52. data/lib/cucumber/platform.rb +11 -16
  53. data/lib/cucumber/rake/task.rb +2 -6
  54. data/lib/cucumber/running_test_case.rb +1 -1
  55. data/lib/cucumber/runtime/for_programming_languages.rb +1 -2
  56. data/lib/cucumber/runtime/meta_message_builder.rb +4 -2
  57. data/lib/cucumber/runtime/user_interface.rb +2 -2
  58. data/lib/cucumber/runtime.rb +5 -5
  59. data/lib/cucumber/step_match.rb +1 -1
  60. data/lib/cucumber/term/ansicolor.rb +1 -1
  61. data/lib/cucumber/term/banner.rb +2 -0
  62. metadata +83 -239
  63. data/CHANGELOG.md +0 -3231
  64. data/CONTRIBUTING.md +0 -246
  65. data/lib/autotest/cucumber.rb +0 -8
  66. data/lib/autotest/cucumber_mixin.rb +0 -133
  67. data/lib/autotest/cucumber_rails.rb +0 -8
  68. data/lib/autotest/cucumber_rails_rspec.rb +0 -8
  69. data/lib/autotest/cucumber_rails_rspec2.rb +0 -8
  70. data/lib/autotest/cucumber_rspec.rb +0 -8
  71. data/lib/autotest/cucumber_rspec2.rb +0 -8
  72. data/lib/autotest/discover.rb +0 -14
  73. data/lib/cucumber/version +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08bb523de5193c764072689a42bbc2dd92faf1a01401277aefba7648bb7f3400'
4
- data.tar.gz: 27c902b720e405d1e683a0a49584e02ddfd2fe09ecff431183170d2505ddc603
3
+ metadata.gz: 1c84d3e0ab90cabd1d67121371c8a95c0e5f91bfa5f3ec42a1024c1a85130bde
4
+ data.tar.gz: 1386d49afc93d04c9b616f38ab4466c8aa3092aa60a293c82557465ad8b4538a
5
5
  SHA512:
6
- metadata.gz: 46678969dac502da8799cd01f8e3e319ba860ec9f7ae46c75200d18612a8d156ca3e1d68f3b79e65736f59aff76da1190b4725e14f8b45b162284f65363c07cb
7
- data.tar.gz: aa27290e26af3e380f1960b836b739445e83bb795fb277bfb4c5950161ad6bd5581e4d8612c063054b192d8a296dcd8cbd18fb47efbcb354b43cf669e4eac72c
6
+ metadata.gz: afd28a66515dbb07168f7c906d1de7feb5c780437a22b0492f218db943555bc996e7dfc77750257033c9ae8c843e350ca12c389963b78e6ddf1c58dc3f1df371
7
+ data.tar.gz: bd21e8e340afc215e887be2e353fd9060a4a4ccf1975298403ed6487e263b4103ef01a73e069f16758cc286ba6d507ae0e2f4b9d72916fb976cbbe440af9d154
data/README.md CHANGED
@@ -1,15 +1,11 @@
1
- <p align="center">
2
- <img src="./.github/img/cucumber-open-logo.png" alt="Cucumber Open - Supported by Smartbear" width="428" />
3
- </p>
1
+ <img src="docs/img/cucumber-open-logo.png" alt="Cucumber Open - Supported by Smartbear" width="428" />
4
2
 
5
3
  # Cucumber
6
4
 
7
5
  [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://vshymanskyy.github.io/StandWithUkraine)
8
6
  [![OpenCollective](https://opencollective.com/cucumber/backers/badge.svg)](https://opencollective.com/cucumber)
9
7
  [![OpenCollective](https://opencollective.com/cucumber/sponsors/badge.svg)](https://opencollective.com/cucumber)
10
- [![pull requests](https://oselvar.com/api/badge?label=pull%20requests&csvUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fcucumber%2Foselvar-github-metrics%2Fmain%2Fdata%2Fcucumber%2Fcucumber-ruby%2FpullRequests.csv)](https://oselvar.com/github/cucumber/oselvar-github-metrics/main/cucumber/cucumber-ruby)
11
- [![issues](https://oselvar.com/api/badge?label=issues&csvUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fcucumber%2Foselvar-github-metrics%2Fmain%2Fdata%2Fcucumber%2Fcucumber-ruby%2Fissues.csv)](https://oselvar.com/github/cucumber/oselvar-github-metrics/main/cucumber/cucumber-ruby)
12
- [![Test cucumber](https://github.com/cucumber/cucumber-ruby/actions/workflows/cucumber-ruby.yml/badge.svg)](https://github.com/cucumber/cucumber-ruby/actions/workflows/cucumber-ruby.yml)
8
+ [![Test cucumber](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml/badge.svg)](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml)
13
9
  [![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby.svg)](https://codeclimate.com/github/cucumber/cucumber-ruby)
14
10
  [![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby/badge.svg?branch=main)](https://coveralls.io/r/cucumber/cucumber-ruby?branch=main)
15
11
 
@@ -18,9 +14,7 @@ written in plain language, they can be read by anyone on your team. Because they
18
14
  read by anyone, you can use them to help improve communication, collaboration and trust on
19
15
  your team.
20
16
 
21
- <p align="center">
22
- <img src="./.github/img/gherkin-example.png" alt="Cucumber Gherkin Example" width="728" />
23
- </p>
17
+ <img src="docs/img/gherkin-example.png" alt="Cucumber Gherkin Example" width="728" />
24
18
 
25
19
  This is the Ruby implementation of Cucumber. Cucumber is also available for [JavaScript](https://github.com/cucumber/cucumber-js),
26
20
  [Java](https://github.com/cucumber/cucumber-jvm), and a lot of other languages. You can find a list of implementations here: https://cucumber.io/docs/installation/.
@@ -51,19 +45,16 @@ Later in this document, bundler is considered being used so all commands are usi
51
45
 
52
46
  ### Supported platforms
53
47
 
48
+ - Ruby 3.2
54
49
  - Ruby 3.1
55
50
  - Ruby 3.0
56
51
  - Ruby 2.7
57
- - Ruby 2.6
58
52
  - TruffleRuby 22.0.0+
59
- - JRuby (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/main/docs/jruby-limitations.md))
60
- - 9.3 >= 9.3.1 (there is a known issue with JRuby 9.3.0. More info can
61
- be found in the [PR#1571](https://github.com/cucumber/cucumber-ruby/pull/1571).)
53
+ - JRuby 9.4+ (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/main/docs/jruby-limitations.md))
62
54
 
63
55
  ### Ruby on Rails
64
56
 
65
- Using Ruby on Rails? You can use [cucumber-rails](https://github.com/cucumber/cucumber-rails)
66
- to bring Cucumber into your Rails project.
57
+ Using Ruby on Rails? You can use [cucumber-rails](https://github.com/cucumber/cucumber-rails) to bring Cucumber into your Rails project.
67
58
 
68
59
  ## Usage
69
60
 
@@ -110,19 +101,20 @@ And a file named `steps.rb` in `features/step_definitions` with:
110
101
  ```ruby
111
102
  # features/step_definitions/steps.rb
112
103
 
113
- Given("this will pass") do
104
+ Given('this will pass') do
114
105
  @this_will_pass = true
115
106
  end
116
107
 
117
- Given("this will fail") do
108
+ Given('this will fail') do
118
109
  @this_will_pass = false
119
110
  end
120
111
 
121
- When("I do an action") do
112
+ When('I do an action') do
113
+ :no_op
122
114
  end
123
115
 
124
116
  Then("some results should be there") do
125
- expect(@this_will_pass)
117
+ expect(@this_will_pass).to be true
126
118
  end
127
119
  ```
128
120
 
@@ -136,9 +128,9 @@ To execute a single feature file:
136
128
 
137
129
  To execute a single example, indicates the line of the name of the example:
138
130
 
139
- $ bundle exec cucumber features/rule.feature:7
131
+ $ bundle exec cucumber features/rule.feature:5
140
132
 
141
- To summarize the results on the standard output, and writte a HTML report on disk:
133
+ To summarize the results on the standard output, and generate a HTML report on disk:
142
134
 
143
135
  $ bundle exec cucumber --format summary --format html --out report.html
144
136
 
@@ -146,8 +138,7 @@ For more command line options
146
138
 
147
139
  $ bundle exec cucumber --help
148
140
 
149
- You can also find documentation on the command line possibilities in
150
- [features/docs/cli](features/docs/cli).
141
+ You can also find documentation on the command line possibilities in [features/docs/cli](features/docs/cli).
151
142
 
152
143
  ## Documentation and support
153
144
 
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 9.2.0
@@ -49,7 +49,7 @@ module Cucumber
49
49
  rescue Errno::EACCES, Errno::ENOENT => e
50
50
  @err.puts("#{e.message} (#{e.class})")
51
51
  exit_unable_to_finish
52
- rescue Exception => e # rubocop:disable Lint/RescueException
52
+ rescue Exception => e
53
53
  @err.puts("#{e.message} (#{e.class})")
54
54
  @err.puts(e.backtrace.join("\n"))
55
55
  exit_unable_to_finish
@@ -12,26 +12,26 @@ module Cucumber
12
12
  CUCUMBER_PUBLISH_URL = ENV['CUCUMBER_PUBLISH_URL'] || 'https://messages.cucumber.io/api/reports -X GET'
13
13
  INDENT = ' ' * 53
14
14
  BUILTIN_FORMATS = {
15
- 'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
16
- 'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
17
- 'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
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" \
22
- "#{INDENT}filename instead."],
23
- 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
24
- "#{INDENT}the usage formatter, except that steps are not printed."],
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'],
34
- 'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
15
+ 'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
16
+ 'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
17
+ 'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
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" \
22
+ "#{INDENT}filename instead."],
23
+ 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
24
+ "#{INDENT}the usage formatter, except that steps are not printed."],
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'],
34
+ 'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
35
35
  }.freeze
36
36
  max = BUILTIN_FORMATS.keys.map(&:length).max
37
37
  FORMAT_HELP_MSG = [
@@ -50,20 +50,21 @@ module Cucumber
50
50
  FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
51
51
  " #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
52
52
  end) + FORMAT_HELP_MSG
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
53
+ PROFILE_SHORT_FLAG = '-p'
54
+ NO_PROFILE_SHORT_FLAG = '-P'
55
+ PROFILE_LONG_FLAG = '--profile'
56
+ NO_PROFILE_LONG_FLAG = '--no-profile'
57
+ FAIL_FAST_FLAG = '--fail-fast'
58
+ RETRY_FLAG = '--retry'
59
+ RETRY_TOTAL_FLAG = '--retry-total'
59
60
  OPTIONS_WITH_ARGS = [
60
61
  '-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
61
62
  '--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
62
- PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, '-l',
63
- '--lines', '--port', '-I', '--snippet-type'
63
+ PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, RETRY_TOTAL_FLAG,
64
+ '-l', '--lines', '--port', '-I', '--snippet-type'
64
65
  ].freeze
65
66
  ORDER_TYPES = %w[defined random].freeze
66
- TAG_LIMIT_MATCHER = /(?<tag_name>@\w+):(?<limit>\d+)/x
67
+ TAG_LIMIT_MATCHER = /(?<tag_name>@\w+):(?<limit>\d+)/x.freeze
67
68
 
68
69
  def self.parse(args, out_stream, error_stream, options = {})
69
70
  new(out_stream, error_stream, options).parse!(args)
@@ -91,13 +92,13 @@ module Cucumber
91
92
  @options[key] = value
92
93
  end
93
94
 
94
- def parse!(args) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
95
+ def parse!(args)
95
96
  @args = args
96
97
  @expanded_args = @args.dup
97
98
 
98
99
  @args.extend(::OptionParser::Arguable)
99
100
 
100
- @args.options do |opts| # rubocop:disable Metrics/BlockLength
101
+ @args.options do |opts|
101
102
  opts.banner = banner
102
103
  opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
103
104
  set_option :publish_enabled, true
@@ -108,16 +109,17 @@ module Cucumber
108
109
  opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY
109
110
 
110
111
  opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
112
+ opts.on("#{RETRY_TOTAL_FLAG} TESTS", *retry_total_msg) { |v| set_option :retry_total, v.to_i }
111
113
  opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
112
114
  opts.on('--i18n-keywords LANG', *i18n_keywords_msg) { |lang| language lang }
113
115
  opts.on(FAIL_FAST_FLAG, 'Exit immediately following the first failing scenario') { set_option :fail_fast }
114
116
  opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
115
117
  add_option :formats, [*parse_formats(v), @out_stream]
116
118
  end
117
- opts.on('--init', *init_msg) { |_v| initialize_project }
119
+ opts.on('--init', *init_msg) { initialize_project }
118
120
  opts.on('-o', '--out [FILE|DIR|URL]', *out_msg) { |v| out_stream v }
119
- opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag v }
120
- opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option :name_regexps, /#{v}/ }
121
+ opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag(v) }
122
+ opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option(:name_regexps, /#{v}/) }
121
123
  opts.on('-e', '--exclude PATTERN', *exclude_msg) { |v| add_option :excludes, Regexp.new(v) }
122
124
  opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) { |v| add_profile v }
123
125
  opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |_v| disable_profile_loading }
@@ -141,11 +143,11 @@ module Cucumber
141
143
  opts.on('-x', '--expand', 'Expand Scenario Outline Tables in output.') { set_option :expand }
142
144
 
143
145
  opts.on('--order TYPE[:SEED]', 'Run examples in the specified order. Available types:',
144
- *<<-TEXT.split("\n")) do |order|
145
- [defined] Run scenarios in the order they were defined (default).
146
- [random] Shuffle scenarios before running.
147
- Specify SEED to reproduce the shuffling from a previous run.
148
- e.g. --order random:5738
146
+ *<<~TEXT.split("\n")) do |order|
147
+ [defined] Run scenarios in the order they were defined (default).
148
+ [random] Shuffle scenarios before running.
149
+ Specify SEED to reproduce the shuffling from a previous run.
150
+ e.g. --order random:5738
149
151
  TEXT
150
152
  @options[:order], @options[:seed] = *order.split(':')
151
153
  raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
@@ -271,6 +273,13 @@ Specify SEED to reproduce the shuffling from a previous run.
271
273
  ['Specify the number of times to retry failing tests (default: 0)']
272
274
  end
273
275
 
276
+ def retry_total_msg
277
+ [
278
+ 'The total number of failing test after which retrying of tests is suspended.',
279
+ 'Example: --retry-total 10 -> Will stop retrying tests after 10 failing tests.'
280
+ ]
281
+ end
282
+
274
283
  def name_msg
275
284
  [
276
285
  'Only execute the feature elements which match part of the given name.',
@@ -286,15 +295,15 @@ Specify SEED to reproduce the shuffling from a previous run.
286
295
  ]
287
296
  end
288
297
 
289
- def parse_formats(v)
290
- formatter, *formatter_options = v.split(',')
291
- options_hash = Hash[formatter_options.map { |s| s.split('=') }]
298
+ def parse_formats(value)
299
+ formatter, *formatter_options = value.split(',')
300
+ options_hash = Hash[formatter_options.map { |string| string.split('=') }]
292
301
  [formatter, options_hash]
293
302
  end
294
303
 
295
- def out_stream(v)
304
+ def out_stream(value)
296
305
  @options[:formats] << ['pretty', {}, nil] if @options[:formats].empty?
297
- @options[:formats][-1][2] = v
306
+ @options[:formats][-1][2] = value
298
307
  end
299
308
 
300
309
  def tags_msg
@@ -366,12 +375,12 @@ Specify SEED to reproduce the shuffling from a previous run.
366
375
  ].join("\n")
367
376
  end
368
377
 
369
- def require_files(v)
370
- @options[:require] << v
371
- return unless Cucumber::JRUBY && File.directory?(v)
378
+ def require_files(filenames)
379
+ @options[:require] << filenames
380
+ return unless Cucumber::JRUBY && File.directory?(filenames)
372
381
 
373
382
  require 'java'
374
- $CLASSPATH << v
383
+ $CLASSPATH << filenames
375
384
  end
376
385
 
377
386
  def require_jars(jars)
@@ -432,8 +441,8 @@ Specify SEED to reproduce the shuffling from a previous run.
432
441
  ProjectInitializer.new.run && Kernel.exit(0)
433
442
  end
434
443
 
435
- def add_profile(p)
436
- @profiles << p
444
+ def add_profile(profile)
445
+ @profiles << profile
437
446
  end
438
447
 
439
448
  def set_option(option, value = nil)
@@ -446,7 +455,7 @@ Specify SEED to reproduce the shuffling from a previous run.
446
455
  end
447
456
 
448
457
  def exit_ok(text)
449
- @out_stream.puts text
458
+ @out_stream.puts(text)
450
459
  Kernel.exit(0)
451
460
  end
452
461
 
@@ -474,22 +483,11 @@ Specify SEED to reproduce the shuffling from a previous run.
474
483
  end
475
484
  end
476
485
 
477
- def disable_profile_loading?
478
- @disable_profile_loading
479
- end
480
-
481
486
  def merge_profiles
482
- if @disable_profile_loading
483
- @out_stream.puts 'Disabling profiles...'
484
- return
485
- end
487
+ return @out_stream.puts 'Disabling profiles...' if @disable_profile_loading
486
488
 
487
489
  @profiles << @default_profile if default_profile_should_be_used?
488
-
489
- @profiles.each do |profile|
490
- merge_with_profile(profile)
491
- end
492
-
490
+ @profiles.each { |profile| merge_with_profile(profile) }
493
491
  @options[:profiles] = @profiles
494
492
  end
495
493
 
@@ -513,7 +511,7 @@ Specify SEED to reproduce the shuffling from a previous run.
513
511
  @profile_loader ||= ProfileLoader.new
514
512
  end
515
513
 
516
- def reverse_merge(other_options) # rubocop:disable Metrics/AbcSize
514
+ def reverse_merge(other_options)
517
515
  @options = other_options.options.merge(@options)
518
516
  @options[:require] += other_options[:require]
519
517
  @options[:excludes] += other_options[:excludes]
@@ -543,6 +541,7 @@ Specify SEED to reproduce the shuffling from a previous run.
543
541
  end
544
542
 
545
543
  @options[:retry] = other_options[:retry] if @options[:retry].zero?
544
+ @options[:retry_total] = other_options[:retry_total] if @options[:retry_total].infinite?
546
545
 
547
546
  self
548
547
  end
@@ -616,7 +615,8 @@ Specify SEED to reproduce the shuffling from a previous run.
616
615
  snippets: true,
617
616
  source: true,
618
617
  duration: true,
619
- retry: 0
618
+ retry: 0,
619
+ retry_total: Float::INFINITY
620
620
  }
621
621
  end
622
622
  end
@@ -11,11 +11,11 @@ module Cucumber
11
11
 
12
12
  def args_from(profile)
13
13
  unless cucumber_yml.key?(profile)
14
- raise(ProfileNotFound, <<-END_OF_ERROR)
15
- Could not find profile: '#{profile}'
14
+ raise(ProfileNotFound, <<~END_OF_ERROR)
15
+ Could not find profile: '#{profile}'
16
16
 
17
- Defined profiles in cucumber.yml:
18
- * #{cucumber_yml.keys.sort.join("\n * ")}
17
+ Defined profiles in cucumber.yml:
18
+ * #{cucumber_yml.keys.sort.join("\n * ")}
19
19
  END_OF_ERROR
20
20
  end
21
21
 
@@ -84,7 +84,7 @@ Defined profiles in cucumber.yml:
84
84
  def load_configuration
85
85
  require 'yaml'
86
86
  begin
87
- @cucumber_yml = YAML.load(@cucumber_erb) # rubocop:disable Security/YAMLLoad
87
+ @cucumber_yml = YAML.load(@cucumber_erb)
88
88
  rescue StandardError
89
89
  raise(YmlLoadError, "cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentation on correct profile usage.\n")
90
90
  end
@@ -78,6 +78,10 @@ module Cucumber
78
78
  @options[:retry]
79
79
  end
80
80
 
81
+ def retry_total_tests
82
+ @options[:retry_total]
83
+ end
84
+
81
85
  def guess?
82
86
  @options[:guess]
83
87
  end
@@ -209,7 +213,7 @@ module Cucumber
209
213
  yield factory,
210
214
  formatter_options,
211
215
  path_or_io
212
- rescue Exception => e # rubocop:disable Lint/RescueException
216
+ rescue Exception => e
213
217
  raise e, "#{e.message}\nError creating formatter: #{format}", e.backtrace
214
218
  end
215
219
  end
@@ -273,7 +277,8 @@ module Cucumber
273
277
  snippets: true,
274
278
  source: true,
275
279
  duration: true,
276
- event_bus: Cucumber::Events.make_event_bus
280
+ event_bus: Cucumber::Events.make_event_bus,
281
+ retry_total: Float::INFINITY
277
282
  }
278
283
  end
279
284
 
@@ -4,52 +4,11 @@ require 'cucumber/platform'
4
4
  require 'cucumber/gherkin/formatter/ansi_escapes'
5
5
 
6
6
  module Cucumber
7
- module Deprecate
8
- class AnsiString
9
- include Cucumber::Gherkin::Formatter::AnsiEscapes
10
-
11
- def self.failure_message(message)
12
- AnsiString.new.failure_message(message)
13
- end
14
-
15
- def failure_message(message)
16
- failed + message + reset
17
- end
18
- end
19
-
20
- class CliOption
21
- def self.deprecate(stream, option, message, remove_after_version)
22
- return if stream.nil?
23
-
24
- stream.puts(
25
- AnsiString.failure_message(
26
- "\nWARNING: #{option} is deprecated" \
27
- " and will be removed after version #{remove_after_version}.\n#{message}.\n"
28
- )
29
- )
30
- end
31
- end
32
-
33
- module ForUsers
34
- def self.call(message, method, remove_after_version)
35
- $stderr.puts AnsiString.failure_message(
36
- "\nWARNING: ##{method} is deprecated" \
37
- " and will be removed after version #{remove_after_version}. #{message}.\n" \
38
- "(Called from #{caller(3..3).first})"
39
- )
40
- end
41
- end
42
-
43
- module ForDevelopers
44
- def self.call(_message, _method, remove_after_version)
45
- raise "This method is due for removal after version #{remove_after_version}" if Cucumber::VERSION >= remove_after_version
46
- end
47
- end
48
-
49
- STRATEGY = $PROGRAM_NAME =~ /rspec$/ ? ForDevelopers : ForUsers
50
- end
51
-
52
- def self.deprecate(*args)
53
- Deprecate::STRATEGY.call(*args)
7
+ def self.deprecate(message, method, remove_after_version)
8
+ $stderr.puts(
9
+ "\nWARNING: ##{method} is deprecated" \
10
+ " and will be removed after version #{remove_after_version}. #{message}.\n" \
11
+ "(Called from #{caller(3..3).first})"
12
+ )
54
13
  end
55
14
  end
@@ -35,7 +35,8 @@ module Cucumber
35
35
  # Raised when a step matches 2 or more StepDefinitions
36
36
  class Ambiguous < StandardError
37
37
  def initialize(step_name, step_definitions, used_guess)
38
- message = String.new # rubocop:disable Style/EmptyLiteral
38
+ # TODO: [LH] - Just use a heredoc here to fix this up
39
+ message = String.new
39
40
  message << "Ambiguous match of \"#{step_name}\":\n\n"
40
41
  message << step_definitions.map(&:backtrace_line).join("\n")
41
42
  message << "\n\n"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/core/events'
2
4
 
3
5
  module Cucumber
@@ -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:]+))?$/.freeze # :nodoc:
9
9
 
10
10
  def initialize(file_specs)
11
11
  Cucumber.logger.debug("Features:\n")
@@ -7,6 +7,11 @@ require 'cucumber/events'
7
7
  module Cucumber
8
8
  module Filters
9
9
  class Retry < Core::Filter.new(:configuration)
10
+ def initialize(*_args)
11
+ super
12
+ @total_permanently_failed = 0
13
+ end
14
+
10
15
  def test_case(test_case)
11
16
  configuration.on_event(:test_case_finished) do |event|
12
17
  next unless retry_required?(test_case, event)
@@ -21,7 +26,21 @@ module Cucumber
21
26
  private
22
27
 
23
28
  def retry_required?(test_case, event)
24
- event.test_case == test_case && event.result.failed? && test_case_counts[test_case] < configuration.retry_attempts
29
+ return false unless event.test_case == test_case
30
+
31
+ return false unless event.result.failed?
32
+
33
+ return false if @total_permanently_failed >= configuration.retry_total_tests
34
+
35
+ retry_required = test_case_counts[test_case] < configuration.retry_attempts
36
+ if retry_required
37
+ # retry test
38
+ true
39
+ else
40
+ # test failed after max. attempts
41
+ @total_permanently_failed += 1
42
+ false
43
+ end
25
44
  end
26
45
 
27
46
  def test_case_counts