cucumber 6.0.0 → 8.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +371 -168
  3. data/CONTRIBUTING.md +216 -55
  4. data/README.md +139 -21
  5. data/lib/autotest/cucumber_mixin.rb +5 -2
  6. data/lib/autotest/discover.rb +3 -2
  7. data/lib/cucumber/cli/configuration.rb +4 -1
  8. data/lib/cucumber/cli/main.rb +4 -3
  9. data/lib/cucumber/cli/options.rb +14 -4
  10. data/lib/cucumber/cli/profile_loader.rb +1 -5
  11. data/lib/cucumber/cli/rerun_file.rb +1 -1
  12. data/lib/cucumber/configuration.rb +5 -4
  13. data/lib/cucumber/constantize.rb +1 -1
  14. data/lib/cucumber/deprecate.rb +2 -1
  15. data/lib/cucumber/errors.rb +1 -1
  16. data/lib/cucumber/events/hook_test_step_created.rb +1 -2
  17. data/lib/cucumber/events/step_activated.rb +0 -6
  18. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  19. data/lib/cucumber/events/test_case_created.rb +1 -2
  20. data/lib/cucumber/events/test_run_finished.rb +2 -1
  21. data/lib/cucumber/events/test_step_created.rb +1 -2
  22. data/lib/cucumber/events/undefined_parameter_type.rb +1 -2
  23. data/lib/cucumber/events.rb +2 -2
  24. data/lib/cucumber/file_specs.rb +2 -1
  25. data/lib/cucumber/filters/activate_steps.rb +1 -0
  26. data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
  27. data/lib/cucumber/filters/tag_limits.rb +1 -3
  28. data/lib/cucumber/formatter/ansicolor.rb +63 -70
  29. data/lib/cucumber/formatter/ast_lookup.rb +2 -2
  30. data/lib/cucumber/formatter/backtrace_filter.rb +1 -1
  31. data/lib/cucumber/formatter/console.rb +20 -4
  32. data/lib/cucumber/formatter/console_issues.rb +6 -1
  33. data/lib/cucumber/formatter/duration_extractor.rb +1 -0
  34. data/lib/cucumber/formatter/errors.rb +1 -0
  35. data/lib/cucumber/formatter/fanout.rb +1 -1
  36. data/lib/cucumber/formatter/http_io.rb +6 -1
  37. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  38. data/lib/cucumber/formatter/io.rb +3 -1
  39. data/lib/cucumber/formatter/json.rb +32 -26
  40. data/lib/cucumber/formatter/junit.rb +6 -3
  41. data/lib/cucumber/formatter/message.rb +2 -1
  42. data/lib/cucumber/formatter/message_builder.rb +11 -10
  43. data/lib/cucumber/formatter/pretty.rb +34 -23
  44. data/lib/cucumber/formatter/progress.rb +1 -0
  45. data/lib/cucumber/formatter/publish_banner_printer.rb +1 -1
  46. data/lib/cucumber/formatter/query/hook_by_test_step.rb +1 -0
  47. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
  48. data/lib/cucumber/formatter/rerun.rb +2 -0
  49. data/lib/cucumber/formatter/steps.rb +5 -2
  50. data/lib/cucumber/formatter/summary.rb +1 -0
  51. data/lib/cucumber/formatter/unicode.rb +4 -4
  52. data/lib/cucumber/formatter/usage.rb +9 -7
  53. data/lib/cucumber/gherkin/data_table_parser.rb +2 -1
  54. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +2 -2
  55. data/lib/cucumber/gherkin/steps_parser.rb +1 -1
  56. data/lib/cucumber/glue/dsl.rb +19 -5
  57. data/lib/cucumber/glue/hook.rb +2 -1
  58. data/lib/cucumber/glue/invoke_in_world.rb +4 -4
  59. data/lib/cucumber/glue/proto_world.rb +12 -9
  60. data/lib/cucumber/glue/registry_and_more.rb +20 -5
  61. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  62. data/lib/cucumber/glue/step_definition.rb +9 -7
  63. data/lib/cucumber/hooks.rb +1 -0
  64. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -1
  65. data/lib/cucumber/multiline_argument/data_table.rb +58 -71
  66. data/lib/cucumber/platform.rb +2 -2
  67. data/lib/cucumber/rake/task.rb +10 -7
  68. data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
  69. data/lib/cucumber/running_test_case.rb +1 -0
  70. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  71. data/lib/cucumber/runtime/support_code.rb +3 -0
  72. data/lib/cucumber/runtime/user_interface.rb +5 -4
  73. data/lib/cucumber/runtime.rb +42 -23
  74. data/lib/cucumber/step_match.rb +6 -10
  75. data/lib/cucumber/step_match_search.rb +3 -2
  76. data/lib/cucumber/term/ansicolor.rb +74 -50
  77. data/lib/cucumber/term/banner.rb +1 -0
  78. data/lib/cucumber/version +1 -1
  79. data/lib/cucumber.rb +2 -1
  80. data/lib/simplecov_setup.rb +1 -1
  81. metadata +90 -89
  82. data/lib/cucumber/core_ext/string.rb +0 -11
@@ -27,7 +27,7 @@ module Cucumber
27
27
  # This will store <tt>[['a', 'b'], ['c', 'd']]</tt> in the <tt>data</tt> variable.
28
28
  #
29
29
  class DataTable
30
- def self.default_arg_name #:nodoc:
30
+ def self.default_arg_name # :nodoc:
31
31
  'table'
32
32
  end
33
33
 
@@ -78,11 +78,12 @@ module Cucumber
78
78
  NULL_CONVERSIONS = Hash.new(strict: false, proc: ->(cell_value) { cell_value }).freeze
79
79
 
80
80
  # @param data [Core::Test::DataTable] the data for the table
81
- # @param conversion_procs [Hash] see map_columns!
82
- # @param header_mappings [Hash] see map_headers!
83
- # @param header_conversion_proc [Proc] see map_headers!
81
+ # @param conversion_procs [Hash] see map_column
82
+ # @param header_mappings [Hash] see map_headers
83
+ # @param header_conversion_proc [Proc] see map_headers
84
84
  def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)
85
85
  raise ArgumentError, 'data must be a Core::Test::DataTable' unless data.is_a? Core::Test::DataTable
86
+
86
87
  ast_table = data
87
88
  # Verify that it's square
88
89
  ast_table.transpose
@@ -107,13 +108,6 @@ module Cucumber
107
108
  @ast_table.location
108
109
  end
109
110
 
110
- # Creates a copy of this table, inheriting any column and header mappings
111
- # registered with #map_column! and #map_headers!.
112
- #
113
- def dup
114
- self.class.new(Core::Test::DataTable.new(raw), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
115
- end
116
-
117
111
  # Returns a new, transposed table. Example:
118
112
  #
119
113
  # | a | 7 | 4 |
@@ -141,7 +135,7 @@ module Cucumber
141
135
  #
142
136
  # [{'a' => '2', 'b' => '3', 'sum' => '5'}, {'a' => '7', 'b' => '9', 'sum' => '16'}]
143
137
  #
144
- # Use #map_column! to specify how values in a column are converted.
138
+ # Use #map_column to specify how values in a column are converted.
145
139
  #
146
140
  def hashes
147
141
  @hashes ||= build_hashes
@@ -161,7 +155,7 @@ module Cucumber
161
155
  def symbolic_hashes
162
156
  @symbolic_hashes ||=
163
157
  hashes.map do |string_hash|
164
- Hash[string_hash.map { |a, b| [symbolize_key(a), b] }]
158
+ string_hash.transform_keys { |a| symbolize_key(a) }
165
159
  end
166
160
  end
167
161
 
@@ -179,6 +173,7 @@ module Cucumber
179
173
  #
180
174
  def rows_hash
181
175
  return @rows_hash if @rows_hash
176
+
182
177
  verify_table_width(2)
183
178
  @rows_hash = transpose.hashes[0]
184
179
  end
@@ -199,7 +194,7 @@ module Cucumber
199
194
  end
200
195
  end
201
196
 
202
- def column_names #:nodoc:
197
+ def column_names # :nodoc:
203
198
  @column_names ||= cell_matrix[0].map(&:value)
204
199
  end
205
200
 
@@ -209,7 +204,7 @@ module Cucumber
209
204
  end
210
205
  end
211
206
 
212
- def each_cells_row(&proc) #:nodoc:
207
+ def each_cells_row(&proc) # :nodoc:
213
208
  cells_rows.each(&proc)
214
209
  end
215
210
 
@@ -228,7 +223,8 @@ module Cucumber
228
223
  pattern.match(header_to_match)
229
224
  end
230
225
 
231
- # Redefines the table headers. This makes it possible to use
226
+ # Returns a new Table where the headers are redefined.
227
+ # This makes it possible to use
232
228
  # prettier and more flexible header names in the features. The
233
229
  # keys of +mappings+ are Strings or regular expressions
234
230
  # (anything that responds to #=== will work) that may match
@@ -244,54 +240,41 @@ module Cucumber
244
240
  # A StepDefinition receiving this table can then map the columns
245
241
  # with both Regexp and String:
246
242
  #
247
- # table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
243
+ # table.map_headers(/phone( number)?/i => :phone, 'Address' => :address)
248
244
  # table.hashes
249
245
  # # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
250
246
  #
251
247
  # You may also pass in a block if you wish to convert all of the headers:
252
248
  #
253
- # table.map_headers! { |header| header.downcase }
249
+ # table.map_headers { |header| header.downcase }
254
250
  # table.hashes.keys
255
251
  # # => ['phone number', 'address']
256
252
  #
257
253
  # When a block is passed in along with a hash then the mappings in the hash take precendence:
258
254
  #
259
- # table.map_headers!('Address' => 'ADDRESS') { |header| header.downcase }
255
+ # table.map_headers('Address' => 'ADDRESS') { |header| header.downcase }
260
256
  # table.hashes.keys
261
257
  # # => ['phone number', 'ADDRESS']
262
258
  #
263
- def map_headers!(mappings = {}, &block)
264
- # TODO: Remove this method for 2.0
265
- clear_cache!
266
- @header_mappings = mappings
267
- @header_conversion_proc = block
268
- end
269
-
270
- # Returns a new Table where the headers are redefined. See #map_headers!
271
259
  def map_headers(mappings = {}, &block)
272
260
  self.class.new(Core::Test::DataTable.new(raw), @conversion_procs.dup, mappings, block)
273
261
  end
274
262
 
263
+ # Returns a new Table with an additional column mapping.
264
+ #
275
265
  # Change how #hashes converts column values. The +column_name+ argument identifies the column
276
266
  # and +conversion_proc+ performs the conversion for each cell in that column. If +strict+ is
277
267
  # true, an error will be raised if the column named +column_name+ is not found. If +strict+
278
268
  # is false, no error will be raised. Example:
279
269
  #
280
270
  # Given /^an expense report for (.*) with the following posts:$/ do |table|
281
- # posts_table.map_column!('amount') { |a| a.to_i }
271
+ # posts_table = posts_table.map_column('amount') { |a| a.to_i }
282
272
  # posts_table.hashes.each do |post|
283
273
  # # post['amount'] is a Fixnum, rather than a String
284
274
  # end
285
275
  # end
286
276
  #
287
- def map_column!(column_name, strict = true, &conversion_proc)
288
- # TODO: Remove this method for 2.0
289
- @conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
290
- self
291
- end
292
-
293
- # Returns a new Table with an additional column mapping. See #map_column!
294
- def map_column(column_name, strict = true, &conversion_proc)
277
+ def map_column(column_name, strict: true, &conversion_proc)
295
278
  conversion_procs = @conversion_procs.dup
296
279
  conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
297
280
  self.class.new(Core::Test::DataTable.new(raw), conversion_procs, @header_mappings.dup, @header_conversion_proc)
@@ -312,8 +295,8 @@ module Cucumber
312
295
  # where the difference actually is.
313
296
  #
314
297
  # Since all tables that are passed to StepDefinitions always have String
315
- # objects in their cells, you may want to use #map_column! before calling
316
- # #diff!. You can use #map_column! on either of the tables.
298
+ # objects in their cells, you may want to use #map_column before calling
299
+ # #diff!. You can use #map_column on either of the tables.
317
300
  #
318
301
  # A Different error is raised if there are missing rows or columns, or
319
302
  # surplus rows. An error is <em>not</em> raised for surplus columns. An
@@ -346,6 +329,7 @@ module Cucumber
346
329
 
347
330
  class Different < StandardError
348
331
  attr_reader :table
332
+
349
333
  def initialize(table)
350
334
  @table = table
351
335
  super("Tables were not identical:\n#{table}")
@@ -356,7 +340,7 @@ module Cucumber
356
340
  cells_rows.map { |cells| cells.map(&:value) }
357
341
  end
358
342
 
359
- def cells_to_hash(cells) #:nodoc:
343
+ def cells_to_hash(cells) # :nodoc:
360
344
  hash = Hash.new do |hash_inner, key|
361
345
  hash_inner[key.to_s] if key.is_a?(Symbol)
362
346
  end
@@ -366,51 +350,51 @@ module Cucumber
366
350
  hash
367
351
  end
368
352
 
369
- def index(cells) #:nodoc:
353
+ def index(cells) # :nodoc:
370
354
  cells_rows.index(cells)
371
355
  end
372
356
 
373
- def verify_column(column_name) #:nodoc:
357
+ def verify_column(column_name) # :nodoc:
374
358
  raise %(The column named "#{column_name}" does not exist) unless raw[0].include?(column_name)
375
359
  end
376
360
 
377
- def verify_table_width(width) #:nodoc:
361
+ def verify_table_width(width) # :nodoc:
378
362
  raise %(The table must have exactly #{width} columns) unless raw[0].size == width
379
363
  end
380
364
 
381
365
  # TODO: remove the below function if it's not actually being used.
382
366
  # Nothing else in this repo calls it.
383
- def text?(text) #:nodoc:
367
+ def text?(text) # :nodoc:
384
368
  raw.flatten.compact.detect { |cell_value| cell_value.index(text) }
385
369
  end
386
370
 
387
- def cells_rows #:nodoc:
371
+ def cells_rows # :nodoc:
388
372
  @rows ||= cell_matrix.map do |cell_row| # rubocop:disable Naming/MemoizedInstanceVariableName
389
373
  Cells.new(self, cell_row)
390
374
  end
391
375
  end
392
376
 
393
- def headers #:nodoc:
377
+ def headers # :nodoc:
394
378
  raw.first
395
379
  end
396
380
 
397
- def header_cell(col) #:nodoc:
381
+ def header_cell(col) # :nodoc:
398
382
  cells_rows[0][col]
399
383
  end
400
384
 
401
385
  attr_reader :cell_matrix
402
386
 
403
- def col_width(col) #:nodoc:
387
+ def col_width(col) # :nodoc:
404
388
  columns[col].__send__(:width)
405
389
  end
406
390
 
407
- def to_s(options = {}) #:nodoc:
391
+ def to_s(options = {}) # :nodoc:
408
392
  indentation = options.key?(:indent) ? options[:indent] : 2
409
393
  prefixes = options.key?(:prefixes) ? options[:prefixes] : TO_S_PREFIXES
410
394
  DataTablePrinter.new(self, indentation, prefixes).to_s
411
395
  end
412
396
 
413
- class DataTablePrinter #:nodoc:
397
+ class DataTablePrinter # :nodoc:
414
398
  include Cucumber::Gherkin::Formatter::Escaping
415
399
  attr_reader :data_table, :indentation, :prefixes
416
400
  private :data_table, :indentation, :prefixes
@@ -424,7 +408,7 @@ module Cucumber
424
408
  def to_s
425
409
  leading_row = "\n"
426
410
  end_indentation = indentation - 2
427
- trailing_row = "\n" + (' ' * end_indentation)
411
+ trailing_row = "\n#{' ' * end_indentation}"
428
412
  table_rows = data_table.cell_matrix.map { |row| format_row(row) }
429
413
  leading_row + table_rows.join("\n") + trailing_row
430
414
  end
@@ -432,7 +416,7 @@ module Cucumber
432
416
  private
433
417
 
434
418
  def format_row(row)
435
- row_start = (' ' * indentation) + '| '
419
+ row_start = "#{' ' * indentation}| "
436
420
  row_end = '|'
437
421
  cells = row.map.with_index do |cell, i|
438
422
  format_cell(cell, data_table.col_width(i))
@@ -449,7 +433,7 @@ module Cucumber
449
433
  end
450
434
  end
451
435
 
452
- def columns #:nodoc:
436
+ def columns # :nodoc:
453
437
  @columns ||= cell_matrix.transpose.map do |cell_row|
454
438
  Cells.new(self, cell_row)
455
439
  end
@@ -469,23 +453,23 @@ module Cucumber
469
453
  def build_hashes
470
454
  convert_headers!
471
455
  convert_columns!
472
- cells_rows[1..-1].map(&:to_hash)
456
+ cells_rows[1..].map(&:to_hash)
473
457
  end
474
458
 
475
- def create_cell_matrix(ast_table) #:nodoc:
459
+ def create_cell_matrix(ast_table) # :nodoc:
476
460
  ast_table.raw.map do |raw_row|
477
461
  line = begin
478
- raw_row.line
479
- rescue StandardError
480
- -1
481
- end
462
+ raw_row.line
463
+ rescue StandardError
464
+ -1
465
+ end
482
466
  raw_row.map do |raw_cell|
483
467
  Cell.new(raw_cell, self, line)
484
468
  end
485
469
  end
486
470
  end
487
471
 
488
- def convert_columns! #:nodoc:
472
+ def convert_columns! # :nodoc:
489
473
  @conversion_procs.each do |column_name, conversion_proc|
490
474
  verify_column(column_name) if conversion_proc[:strict]
491
475
  end
@@ -493,13 +477,13 @@ module Cucumber
493
477
  cell_matrix.transpose.each do |col|
494
478
  column_name = col[0].value
495
479
  conversion_proc = @conversion_procs[column_name][:proc]
496
- col[1..-1].each do |cell|
480
+ col[1..].each do |cell|
497
481
  cell.value = conversion_proc.call(cell.value)
498
482
  end
499
483
  end
500
484
  end
501
485
 
502
- def convert_headers! #:nodoc:
486
+ def convert_headers! # :nodoc:
503
487
  header_cells = cell_matrix[0]
504
488
 
505
489
  if @header_conversion_proc
@@ -508,20 +492,22 @@ module Cucumber
508
492
  end
509
493
 
510
494
  @header_mappings.each_pair do |pre, post|
511
- mapped_cells = header_cells.reject { |cell| cell.value.match(pre).nil? }
495
+ mapped_cells = header_cells.select { |cell| pre.is_a?(Regexp) ? cell.value.match?(pre) : cell.value == pre }
512
496
  raise "No headers matched #{pre.inspect}" if mapped_cells.empty?
513
497
  raise "#{mapped_cells.length} headers matched #{pre.inspect}: #{mapped_cells.map(&:value).inspect}" if mapped_cells.length > 1
498
+
514
499
  mapped_cells[0].value = post
515
500
  @conversion_procs[post] = @conversion_procs.delete(pre) if @conversion_procs.key?(pre)
516
501
  end
517
502
  end
518
503
 
519
- def clear_cache! #:nodoc:
504
+ def clear_cache! # :nodoc:
520
505
  @hashes = @rows_hash = @column_names = @rows = @columns = nil
521
506
  end
522
507
 
523
- def ensure_table(table_or_array) #:nodoc:
508
+ def ensure_table(table_or_array) # :nodoc:
524
509
  return table_or_array if DataTable == table_or_array.class
510
+
525
511
  DataTable.from(table_or_array)
526
512
  end
527
513
 
@@ -530,7 +516,7 @@ module Cucumber
530
516
  end
531
517
 
532
518
  # Represents a row of cells or columns of cells
533
- class Cells #:nodoc:
519
+ class Cells # :nodoc:
534
520
  include Enumerable
535
521
  include Cucumber::Gherkin::Formatter::Escaping
536
522
 
@@ -543,6 +529,7 @@ module Cucumber
543
529
 
544
530
  def accept(visitor)
545
531
  return if Cucumber.wants_to_quit
532
+
546
533
  each do |cell|
547
534
  visitor.visit_table_cell(cell)
548
535
  end
@@ -550,15 +537,15 @@ module Cucumber
550
537
  end
551
538
 
552
539
  # For testing only
553
- def to_sexp #:nodoc:
540
+ def to_sexp # :nodoc:
554
541
  [:row, line, *@cells.map(&:to_sexp)]
555
542
  end
556
543
 
557
- def to_hash #:nodoc:
544
+ def to_hash # :nodoc:
558
545
  @to_hash ||= @table.cells_to_hash(self)
559
546
  end
560
547
 
561
- def value(n) #:nodoc:
548
+ def value(n) # :nodoc:
562
549
  self[n].value
563
550
  end
564
551
 
@@ -589,7 +576,7 @@ module Cucumber
589
576
  end
590
577
  end
591
578
 
592
- class Cell #:nodoc:
579
+ class Cell # :nodoc:
593
580
  attr_reader :line, :table
594
581
  attr_accessor :status, :value
595
582
 
@@ -616,12 +603,12 @@ module Cucumber
616
603
  end
617
604
 
618
605
  # For testing only
619
- def to_sexp #:nodoc:
606
+ def to_sexp # :nodoc:
620
607
  [:cell, @value]
621
608
  end
622
609
  end
623
610
 
624
- class SurplusCell < Cell #:nodoc:
611
+ class SurplusCell < Cell # :nodoc:
625
612
  def status
626
613
  :comment
627
614
  end
@@ -8,8 +8,8 @@ require 'cucumber/core/platform'
8
8
  module Cucumber
9
9
  unless defined?(Cucumber::VERSION)
10
10
  VERSION = File.read(File.expand_path('version', __dir__)).strip
11
- BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
12
- LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
11
+ BINARY = File.expand_path("#{File.dirname(__FILE__)}/../../bin/cucumber")
12
+ LIBDIR = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
13
13
  RAILS = defined?(Rails)
14
14
  RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
15
15
  RUBY = defined? RUBY_VERSION
@@ -30,13 +30,14 @@ module Cucumber
30
30
  include Cucumber::Gherkin::Formatter::AnsiEscapes
31
31
  include ::Rake::DSL if defined?(::Rake::DSL)
32
32
 
33
- class InProcessCucumberRunner #:nodoc:
33
+ class InProcessCucumberRunner # :nodoc:
34
34
  include ::Rake::DSL if defined?(::Rake::DSL)
35
35
 
36
36
  attr_reader :args
37
37
 
38
38
  def initialize(libs, cucumber_opts, feature_files)
39
39
  raise 'libs must be an Array when running in-process' unless Array == libs.class
40
+
40
41
  libs.reverse_each { |lib| $LOAD_PATH.unshift(lib) }
41
42
  @args = (
42
43
  cucumber_opts +
@@ -51,7 +52,7 @@ module Cucumber
51
52
  end
52
53
  end
53
54
 
54
- class ForkedCucumberRunner #:nodoc:
55
+ class ForkedCucumberRunner # :nodoc:
55
56
  include ::Rake::DSL if defined?(::Rake::DSL)
56
57
 
57
58
  def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files)
@@ -110,7 +111,8 @@ module Cucumber
110
111
  # Extra options to pass to the cucumber binary. Can be overridden by the CUCUMBER_OPTS environment variable.
111
112
  # It's recommended to pass an Array, but if it's a String it will be #split by ' '.
112
113
  attr_reader :cucumber_opts
113
- def cucumber_opts=(opts) #:nodoc:
114
+
115
+ def cucumber_opts=(opts) # :nodoc:
114
116
  @cucumber_opts = String == opts.class ? opts.split(' ') : opts
115
117
  end
116
118
 
@@ -145,24 +147,25 @@ module Cucumber
145
147
  define_task
146
148
  end
147
149
 
148
- def define_task #:nodoc:
150
+ def define_task # :nodoc:
149
151
  desc @desc
150
152
  task @task_name do
151
153
  runner.run
152
154
  end
153
155
  end
154
156
 
155
- def runner(_task_args = nil) #:nodoc:
157
+ def runner(_task_args = nil) # :nodoc:
156
158
  cucumber_opts = [(ENV['CUCUMBER_OPTS'] ? ENV['CUCUMBER_OPTS'].split(/\s+/) : nil) || cucumber_opts_with_profile]
157
159
  return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
160
+
158
161
  InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
159
162
  end
160
163
 
161
- def cucumber_opts_with_profile #:nodoc:
164
+ def cucumber_opts_with_profile # :nodoc:
162
165
  Array(cucumber_opts).concat(Array(@profile).flat_map { |p| ['--profile', p] })
163
166
  end
164
167
 
165
- def feature_files #:nodoc:
168
+ def feature_files # :nodoc:
166
169
  make_command_line_safe(FileList[ENV['FEATURE'] || []])
167
170
  end
168
171
 
@@ -2,12 +2,12 @@
2
2
 
3
3
  require 'optparse'
4
4
 
5
- module Spec #:nodoc:
6
- module Runner #:nodoc:
5
+ module Spec # :nodoc:
6
+ module Runner # :nodoc:
7
7
  # Neuters RSpec's option parser.
8
8
  # (RSpec's option parser tries to parse ARGV, which
9
9
  # will fail when running cucumber)
10
- class OptionParser < ::OptionParser #:nodoc:
10
+ class OptionParser < ::OptionParser # :nodoc:
11
11
  NEUTERED_RSPEC = Object.new
12
12
  def NEUTERED_RSPEC.method_missing(_method, *_args) # rubocop:disable Style/MissingRespondToMissing
13
13
  self || super
@@ -15,11 +15,14 @@ module Spec #:nodoc:
15
15
 
16
16
  def self.method_added(method)
17
17
  return if @__neutering_rspec
18
+
18
19
  @__neutering_rspec = true
19
20
  define_method(method) do |*_a|
20
21
  NEUTERED_RSPEC
21
22
  end
22
23
  @__neutering_rspec = false
24
+
25
+ super
23
26
  end
24
27
  end
25
28
  end
@@ -33,6 +33,7 @@ module Cucumber
33
33
 
34
34
  def exception
35
35
  return unless @result.failed?
36
+
36
37
  @result.exception
37
38
  end
38
39
 
@@ -0,0 +1,106 @@
1
+ require 'cucumber/messages'
2
+ require 'cucumber/ci_environment'
3
+
4
+ module Cucumber
5
+ class Runtime
6
+ # Builder to instanciate a Cucumber::Messages::Meta message filled-in with
7
+ # the runtime meta-data:
8
+ # - protocol version: the version of the Cucumber::Messages protocol
9
+ # - implementation: the name and version of the implementation (e.g. cucumber-ruby 8.0.0)
10
+ # - runtime: the name and version of the runtime (e.g. ruby 3.0.1)
11
+ # - os: the name and version of the operating system (e.g. linux 3.13.0-45-generic)
12
+ # - cpu: the name of the CPU (e.g. x86_64)
13
+ # - ci: informtion about the CI environment if any, including:
14
+ # - name: the name of the CI environment (e.g. Jenkins)
15
+ # - url: the URL of the CI environment (e.g. https://ci.example.com)
16
+ # - build_number: the build number of the CI environment (e.g. 123)
17
+ # - git: the git information of the CI environment if any
18
+ # - remote: the remote of the git repository (e.g. git@github.com:cucumber/cucumber-ruby.git)
19
+ # - revision: the revision of the git repository (e.g. abcdef)
20
+ # - branch: the name of the git branch (e.g. main)
21
+ # - tag: the name of the git tag (e.g. v1.0.0)
22
+ class MetaMessageBuilder
23
+ class << self
24
+ # Builds a Cucumber::Messages::Meta filled-in with the runtime meta-data
25
+ #
26
+ # @param [env] environment data from which the CI information will be
27
+ # retrieved (default ENV). Can be used to mock the environment for
28
+ # testing purpose.
29
+ #
30
+ # @return [Cucumber::Messages::Meta] the meta message
31
+ #
32
+ # @see Cucumber::Runtime::MetaMessageBuilder
33
+ #
34
+ # @example
35
+ # Cucumber::Runtime::MetaMessageBuilder.build_meta_message
36
+ #
37
+ def build_meta_message(env = ENV)
38
+ Cucumber::Messages::Meta.new(
39
+ protocol_version: protocol_version,
40
+ implementation: implementation,
41
+ runtime: runtime,
42
+ os: os,
43
+ cpu: cpu,
44
+ ci: ci(env)
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def protocol_version
51
+ Cucumber::Messages::VERSION
52
+ end
53
+
54
+ def implementation
55
+ Cucumber::Messages::Product.new(
56
+ name: 'cucumber-ruby',
57
+ version: Cucumber::VERSION
58
+ )
59
+ end
60
+
61
+ def runtime
62
+ Cucumber::Messages::Product.new(
63
+ name: RUBY_ENGINE,
64
+ version: RUBY_VERSION
65
+ )
66
+ end
67
+
68
+ def os
69
+ Cucumber::Messages::Product.new(
70
+ name: RbConfig::CONFIG['target_os'],
71
+ version: Sys::Uname.uname.version
72
+ )
73
+ end
74
+
75
+ def cpu
76
+ Cucumber::Messages::Product.new(
77
+ name: RbConfig::CONFIG['target_cpu']
78
+ )
79
+ end
80
+
81
+ def ci(env)
82
+ ci_data = Cucumber::CiEnvironment.detect_ci_environment(env)
83
+ return nil unless ci_data
84
+
85
+ Cucumber::Messages::Ci.new(
86
+ name: ci_data[:name],
87
+ url: ci_data[:url],
88
+ build_number: ci_data[:buildNumber],
89
+ git: git_info(ci_data)
90
+ )
91
+ end
92
+
93
+ def git_info(ci_data)
94
+ return nil unless ci_data[:git]
95
+
96
+ Cucumber::Messages::Git.new(
97
+ remote: ci_data[:git][:remote],
98
+ revision: ci_data[:git][:revision],
99
+ branch: ci_data[:git][:branch],
100
+ tag: ci_data[:git][:tag]
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -72,6 +72,7 @@ module Cucumber
72
72
  def invoke_dynamic_step(step_name, multiline_argument, _location = nil)
73
73
  matches = step_matches(step_name)
74
74
  raise UndefinedDynamicStep, step_name if matches.empty?
75
+
75
76
  matches.first.invoke(multiline_argument)
76
77
  end
77
78
 
@@ -109,6 +110,7 @@ module Cucumber
109
110
 
110
111
  def apply_before_hooks(test_case)
111
112
  return test_case if test_case.test_steps.empty?
113
+
112
114
  scenario = RunningTestCase.new(test_case)
113
115
  hooks = registry.hooks_for(:before, scenario)
114
116
  BeforeHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
@@ -116,6 +118,7 @@ module Cucumber
116
118
 
117
119
  def apply_after_hooks(test_case)
118
120
  return test_case if test_case.test_steps.empty?
121
+
119
122
  scenario = RunningTestCase.new(test_case)
120
123
  hooks = registry.hooks_for(:after, scenario)
121
124
  AfterHooks.new(@configuration.id_generator, hooks, scenario, @configuration.event_bus).apply_to(test_case)
@@ -21,8 +21,8 @@ module Cucumber
21
21
  # that makes a sound before invoking #ask.
22
22
  #
23
23
  def ask(question, timeout_seconds)
24
- STDOUT.puts(question)
25
- STDOUT.flush
24
+ $stdout.puts(question)
25
+ $stdout.flush
26
26
  puts(question)
27
27
 
28
28
  answer = if Cucumber::JRUBY
@@ -32,6 +32,7 @@ module Cucumber
32
32
  end
33
33
 
34
34
  raise("Waited for input for #{timeout_seconds} seconds, then timed out.") unless answer
35
+
35
36
  puts(answer)
36
37
  answer
37
38
  end
@@ -48,7 +49,7 @@ module Cucumber
48
49
 
49
50
  def mri_gets(timeout_seconds)
50
51
  Timeout.timeout(timeout_seconds) do
51
- STDIN.gets
52
+ $stdin.gets
52
53
  end
53
54
  rescue Timeout::Error
54
55
  nil
@@ -57,7 +58,7 @@ module Cucumber
57
58
  def jruby_gets(timeout_seconds)
58
59
  answer = nil
59
60
  t = java.lang.Thread.new do
60
- answer = STDIN.gets
61
+ answer = $stdin.gets
61
62
  end
62
63
  t.start
63
64
  t.join(timeout_seconds * 1000)