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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +371 -168
- data/CONTRIBUTING.md +216 -55
- data/README.md +139 -21
- data/lib/autotest/cucumber_mixin.rb +5 -2
- data/lib/autotest/discover.rb +3 -2
- data/lib/cucumber/cli/configuration.rb +4 -1
- data/lib/cucumber/cli/main.rb +4 -3
- data/lib/cucumber/cli/options.rb +14 -4
- data/lib/cucumber/cli/profile_loader.rb +1 -5
- data/lib/cucumber/cli/rerun_file.rb +1 -1
- data/lib/cucumber/configuration.rb +5 -4
- data/lib/cucumber/constantize.rb +1 -1
- data/lib/cucumber/deprecate.rb +2 -1
- data/lib/cucumber/errors.rb +1 -1
- data/lib/cucumber/events/hook_test_step_created.rb +1 -2
- data/lib/cucumber/events/step_activated.rb +0 -6
- data/lib/cucumber/events/step_definition_registered.rb +0 -5
- data/lib/cucumber/events/test_case_created.rb +1 -2
- data/lib/cucumber/events/test_run_finished.rb +2 -1
- data/lib/cucumber/events/test_step_created.rb +1 -2
- data/lib/cucumber/events/undefined_parameter_type.rb +1 -2
- data/lib/cucumber/events.rb +2 -2
- data/lib/cucumber/file_specs.rb +2 -1
- data/lib/cucumber/filters/activate_steps.rb +1 -0
- data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
- data/lib/cucumber/filters/tag_limits.rb +1 -3
- data/lib/cucumber/formatter/ansicolor.rb +63 -70
- data/lib/cucumber/formatter/ast_lookup.rb +2 -2
- data/lib/cucumber/formatter/backtrace_filter.rb +1 -1
- data/lib/cucumber/formatter/console.rb +20 -4
- data/lib/cucumber/formatter/console_issues.rb +6 -1
- data/lib/cucumber/formatter/duration_extractor.rb +1 -0
- data/lib/cucumber/formatter/errors.rb +1 -0
- data/lib/cucumber/formatter/fanout.rb +1 -1
- data/lib/cucumber/formatter/http_io.rb +6 -1
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/io.rb +3 -1
- data/lib/cucumber/formatter/json.rb +32 -26
- data/lib/cucumber/formatter/junit.rb +6 -3
- data/lib/cucumber/formatter/message.rb +2 -1
- data/lib/cucumber/formatter/message_builder.rb +11 -10
- data/lib/cucumber/formatter/pretty.rb +34 -23
- data/lib/cucumber/formatter/progress.rb +1 -0
- data/lib/cucumber/formatter/publish_banner_printer.rb +1 -1
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +1 -0
- data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
- data/lib/cucumber/formatter/rerun.rb +2 -0
- data/lib/cucumber/formatter/steps.rb +5 -2
- data/lib/cucumber/formatter/summary.rb +1 -0
- data/lib/cucumber/formatter/unicode.rb +4 -4
- data/lib/cucumber/formatter/usage.rb +9 -7
- data/lib/cucumber/gherkin/data_table_parser.rb +2 -1
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +2 -2
- data/lib/cucumber/gherkin/steps_parser.rb +1 -1
- data/lib/cucumber/glue/dsl.rb +19 -5
- data/lib/cucumber/glue/hook.rb +2 -1
- data/lib/cucumber/glue/invoke_in_world.rb +4 -4
- data/lib/cucumber/glue/proto_world.rb +12 -9
- data/lib/cucumber/glue/registry_and_more.rb +20 -5
- data/lib/cucumber/glue/registry_wrapper.rb +31 -0
- data/lib/cucumber/glue/step_definition.rb +9 -7
- data/lib/cucumber/hooks.rb +1 -0
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -1
- data/lib/cucumber/multiline_argument/data_table.rb +58 -71
- data/lib/cucumber/platform.rb +2 -2
- data/lib/cucumber/rake/task.rb +10 -7
- data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
- data/lib/cucumber/running_test_case.rb +1 -0
- data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
- data/lib/cucumber/runtime/support_code.rb +3 -0
- data/lib/cucumber/runtime/user_interface.rb +5 -4
- data/lib/cucumber/runtime.rb +42 -23
- data/lib/cucumber/step_match.rb +6 -10
- data/lib/cucumber/step_match_search.rb +3 -2
- data/lib/cucumber/term/ansicolor.rb +74 -50
- data/lib/cucumber/term/banner.rb +1 -0
- data/lib/cucumber/version +1 -1
- data/lib/cucumber.rb +2 -1
- data/lib/simplecov_setup.rb +1 -1
- metadata +90 -89
- 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
|
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
|
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
|
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
|
-
|
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
|
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)
|
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
|
-
#
|
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
|
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
|
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
|
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
|
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
|
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
|
316
|
-
# #diff!. You can use #map_column
|
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)
|
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)
|
353
|
+
def index(cells) # :nodoc:
|
370
354
|
cells_rows.index(cells)
|
371
355
|
end
|
372
356
|
|
373
|
-
def verify_column(column_name)
|
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)
|
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)
|
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
|
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
|
377
|
+
def headers # :nodoc:
|
394
378
|
raw.first
|
395
379
|
end
|
396
380
|
|
397
|
-
def header_cell(col)
|
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)
|
387
|
+
def col_width(col) # :nodoc:
|
404
388
|
columns[col].__send__(:width)
|
405
389
|
end
|
406
390
|
|
407
|
-
def to_s(options = {})
|
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
|
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
|
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 =
|
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
|
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
|
456
|
+
cells_rows[1..].map(&:to_hash)
|
473
457
|
end
|
474
458
|
|
475
|
-
def create_cell_matrix(ast_table)
|
459
|
+
def create_cell_matrix(ast_table) # :nodoc:
|
476
460
|
ast_table.raw.map do |raw_row|
|
477
461
|
line = begin
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
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!
|
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
|
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!
|
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.
|
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!
|
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)
|
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
|
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
|
540
|
+
def to_sexp # :nodoc:
|
554
541
|
[:row, line, *@cells.map(&:to_sexp)]
|
555
542
|
end
|
556
543
|
|
557
|
-
def to_hash
|
544
|
+
def to_hash # :nodoc:
|
558
545
|
@to_hash ||= @table.cells_to_hash(self)
|
559
546
|
end
|
560
547
|
|
561
|
-
def value(n)
|
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
|
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
|
606
|
+
def to_sexp # :nodoc:
|
620
607
|
[:cell, @value]
|
621
608
|
end
|
622
609
|
end
|
623
610
|
|
624
|
-
class SurplusCell < Cell
|
611
|
+
class SurplusCell < Cell # :nodoc:
|
625
612
|
def status
|
626
613
|
:comment
|
627
614
|
end
|
data/lib/cucumber/platform.rb
CHANGED
@@ -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__)
|
12
|
-
LIBDIR = File.expand_path(File.dirname(__FILE__)
|
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
|
data/lib/cucumber/rake/task.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
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)
|
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
|
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
|
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
|
6
|
-
module Runner
|
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
|
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
|
@@ -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
|
-
|
25
|
-
|
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
|
-
|
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 =
|
61
|
+
answer = $stdin.gets
|
61
62
|
end
|
62
63
|
t.start
|
63
64
|
t.join(timeout_seconds * 1000)
|