cucumber 4.1.0 → 9.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +134 -21
  3. data/VERSION +1 -0
  4. data/lib/cucumber/cli/configuration.rb +27 -2
  5. data/lib/cucumber/cli/main.rb +5 -4
  6. data/lib/cucumber/cli/options.rb +102 -65
  7. data/lib/cucumber/cli/profile_loader.rb +6 -10
  8. data/lib/cucumber/cli/rerun_file.rb +1 -1
  9. data/lib/cucumber/configuration.rb +26 -13
  10. data/lib/cucumber/constantize.rb +1 -1
  11. data/lib/cucumber/deprecate.rb +6 -46
  12. data/lib/cucumber/errors.rb +4 -3
  13. data/lib/cucumber/events/envelope.rb +2 -0
  14. data/lib/cucumber/events/gherkin_source_parsed.rb +2 -0
  15. data/lib/cucumber/events/gherkin_source_read.rb +2 -0
  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_case_finished.rb +2 -0
  21. data/lib/cucumber/events/test_case_started.rb +2 -0
  22. data/lib/cucumber/events/test_run_finished.rb +2 -1
  23. data/lib/cucumber/events/test_step_created.rb +1 -2
  24. data/lib/cucumber/events/test_step_finished.rb +2 -0
  25. data/lib/cucumber/events/test_step_started.rb +2 -0
  26. data/lib/cucumber/events/undefined_parameter_type.rb +3 -2
  27. data/lib/cucumber/events.rb +2 -2
  28. data/lib/cucumber/file_specs.rb +2 -1
  29. data/lib/cucumber/filters/activate_steps.rb +1 -0
  30. data/lib/cucumber/filters/retry.rb +20 -1
  31. data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
  32. data/lib/cucumber/filters/tag_limits.rb +1 -3
  33. data/lib/cucumber/formatter/ansicolor.rb +70 -85
  34. data/lib/cucumber/formatter/ast_lookup.rb +16 -10
  35. data/lib/cucumber/formatter/backtrace_filter.rb +3 -1
  36. data/lib/cucumber/formatter/console.rb +34 -16
  37. data/lib/cucumber/formatter/console_counts.rb +3 -1
  38. data/lib/cucumber/formatter/console_issues.rb +10 -3
  39. data/lib/cucumber/formatter/curl_option_parser.rb +49 -0
  40. data/lib/cucumber/formatter/duration_extractor.rb +1 -0
  41. data/lib/cucumber/formatter/errors.rb +3 -0
  42. data/lib/cucumber/formatter/fail_fast.rb +1 -1
  43. data/lib/cucumber/formatter/fanout.rb +1 -1
  44. data/lib/cucumber/formatter/html.rb +3 -1
  45. data/lib/cucumber/formatter/http_io.rb +10 -136
  46. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  47. data/lib/cucumber/formatter/interceptor.rb +4 -3
  48. data/lib/cucumber/formatter/io.rb +50 -12
  49. data/lib/cucumber/formatter/io_http_buffer.rb +88 -0
  50. data/lib/cucumber/formatter/json.rb +36 -37
  51. data/lib/cucumber/formatter/junit.rb +29 -9
  52. data/lib/cucumber/formatter/message.rb +3 -2
  53. data/lib/cucumber/formatter/message_builder.rb +32 -16
  54. data/lib/cucumber/formatter/pretty.rb +44 -29
  55. data/lib/cucumber/formatter/progress.rb +2 -1
  56. data/lib/cucumber/formatter/publish_banner_printer.rb +75 -0
  57. data/lib/cucumber/formatter/query/hook_by_test_step.rb +3 -0
  58. data/lib/cucumber/formatter/query/pickle_by_test.rb +2 -0
  59. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +2 -0
  60. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +2 -0
  61. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +4 -0
  62. data/lib/cucumber/formatter/rerun.rb +7 -5
  63. data/lib/cucumber/formatter/steps.rb +6 -3
  64. data/lib/cucumber/formatter/summary.rb +2 -1
  65. data/lib/cucumber/formatter/unicode.rb +7 -7
  66. data/lib/cucumber/formatter/url_reporter.rb +19 -0
  67. data/lib/cucumber/formatter/usage.rb +9 -7
  68. data/lib/cucumber/gherkin/data_table_parser.rb +2 -1
  69. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +25 -27
  70. data/lib/cucumber/gherkin/steps_parser.rb +1 -1
  71. data/lib/cucumber/glue/dsl.rb +30 -16
  72. data/lib/cucumber/glue/hook.rb +6 -3
  73. data/lib/cucumber/glue/invoke_in_world.rb +5 -5
  74. data/lib/cucumber/glue/proto_world.rb +37 -57
  75. data/lib/cucumber/glue/registry_and_more.rb +31 -12
  76. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  77. data/lib/cucumber/glue/snippet.rb +4 -2
  78. data/lib/cucumber/glue/step_definition.rb +9 -7
  79. data/lib/cucumber/glue/world_factory.rb +2 -0
  80. data/lib/cucumber/hooks.rb +1 -0
  81. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +5 -2
  82. data/lib/cucumber/multiline_argument/data_table.rb +65 -79
  83. data/lib/cucumber/platform.rb +11 -16
  84. data/lib/cucumber/rake/task.rb +22 -17
  85. data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
  86. data/lib/cucumber/rspec/doubles.rb +3 -5
  87. data/lib/cucumber/running_test_case.rb +2 -1
  88. data/lib/cucumber/runtime/for_programming_languages.rb +1 -2
  89. data/lib/cucumber/runtime/meta_message_builder.rb +108 -0
  90. data/lib/cucumber/runtime/support_code.rb +3 -0
  91. data/lib/cucumber/runtime/user_interface.rb +7 -6
  92. data/lib/cucumber/runtime.rb +50 -24
  93. data/lib/cucumber/step_match.rb +7 -11
  94. data/lib/cucumber/step_match_search.rb +3 -2
  95. data/lib/cucumber/term/ansicolor.rb +74 -50
  96. data/lib/cucumber/term/banner.rb +59 -0
  97. data/lib/cucumber.rb +2 -1
  98. data/lib/simplecov_setup.rb +1 -1
  99. metadata +103 -229
  100. data/CHANGELOG.md +0 -2682
  101. data/CONTRIBUTING.md +0 -71
  102. data/lib/autotest/cucumber.rb +0 -8
  103. data/lib/autotest/cucumber_mixin.rb +0 -132
  104. data/lib/autotest/cucumber_rails.rb +0 -8
  105. data/lib/autotest/cucumber_rails_rspec.rb +0 -8
  106. data/lib/autotest/cucumber_rails_rspec2.rb +0 -8
  107. data/lib/autotest/cucumber_rspec.rb +0 -8
  108. data/lib/autotest/cucumber_rspec2.rb +0 -8
  109. data/lib/autotest/discover.rb +0 -13
  110. data/lib/cucumber/core_ext/string.rb +0 -11
  111. data/lib/cucumber/version +0 -1
@@ -2,10 +2,12 @@
2
2
 
3
3
  require 'cucumber/formatter/progress'
4
4
  require 'cucumber/step_definition_light'
5
+ require 'cucumber/formatter/console'
5
6
 
6
7
  module Cucumber
7
8
  module Formatter
8
9
  class Usage < Progress
10
+ include Console
9
11
  class StepDefKey < StepDefinitionLight
10
12
  attr_accessor :mean_duration, :status
11
13
  end
@@ -76,7 +78,7 @@ module Cucumber
76
78
  if @stepdef_to_match[stepdef_key].any?
77
79
  print_steps(stepdef_key)
78
80
  else
79
- @io.puts(' ' + format_string('NOT MATCHED BY ANY STEPS', :failed))
81
+ @io.puts(" #{format_string('NOT MATCHED BY ANY STEPS', :failed)}")
80
82
  end
81
83
  end
82
84
  @io.puts
@@ -84,11 +86,11 @@ module Cucumber
84
86
  end
85
87
 
86
88
  def print_step_definition(stepdef_key)
87
- @io.print format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped) + ' ' unless config.dry_run?
89
+ @io.print "#{format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped)} " unless config.dry_run?
88
90
  @io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
89
91
  if config.source?
90
- indent = max_length - stepdef_key.regexp_source.unpack('U*').length
91
- line_comment = " # #{stepdef_key.location}".indent(indent)
92
+ indent_amount = max_length - stepdef_key.regexp_source.unpack('U*').length
93
+ line_comment = indent(" # #{stepdef_key.location}", indent_amount)
92
94
  @io.print(format_string(line_comment, :comment))
93
95
  end
94
96
  @io.puts
@@ -97,11 +99,11 @@ module Cucumber
97
99
  def print_steps(stepdef_key)
98
100
  @stepdef_to_match[stepdef_key].each do |step|
99
101
  @io.print ' '
100
- @io.print format_string(format('%<duration>.7f', duration: step[:duration]), :skipped) + ' ' unless config.dry_run?
102
+ @io.print "#{format_string(format('%<duration>.7f', duration: step[:duration]), :skipped)} " unless config.dry_run?
101
103
  @io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
102
104
  if config.source?
103
- indent = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
104
- line_comment = " # #{step[:location]}".indent(indent)
105
+ indent_amount = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
106
+ line_comment = indent(" # #{step[:location]}", indent_amount)
105
107
  @io.print(format_string(line_comment, :comment))
106
108
  end
107
109
  @io.puts
@@ -15,10 +15,11 @@ module Cucumber
15
15
  messages = ::Gherkin.from_source('dummy', feature_header + text, gherkin_options)
16
16
 
17
17
  messages.each do |message|
18
- gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
18
+ gherkin_document = message.gherkin_document.to_h unless message.gherkin_document.nil?
19
19
  end
20
20
 
21
21
  return if gherkin_document.nil?
22
+
22
23
  gherkin_document[:feature][:children][0][:scenario][:steps][0][:data_table][:rows].each do |row|
23
24
  @builder.row(row[:cells].map { |cell| cell[:value] })
24
25
  end
@@ -5,8 +5,7 @@ module Cucumber
5
5
  module Formatter
6
6
  # Defines aliases for ANSI coloured output. Default colours can be overridden by defining
7
7
  # a <tt>GHERKIN_COLORS</tt> variable in your shell, very much like how you can
8
- # tweak the familiar POSIX command <tt>ls</tt> with
9
- # $LSCOLORS: http://linux-sxs.org/housekeeping/lscolors.html
8
+ # tweak the familiar POSIX command <tt>ls</tt> with $LSCOLORS: http://linux-sxs.org/housekeeping/lscolors.html
10
9
  #
11
10
  # The colours that you can change are:
12
11
  #
@@ -41,40 +40,39 @@ module Cucumber
41
40
  # Although not listed, you can also use <tt>grey</tt>
42
41
  module AnsiEscapes
43
42
  COLORS = {
44
- 'black' => "\e[30m",
45
- 'red' => "\e[31m",
46
- 'green' => "\e[32m",
47
- 'yellow' => "\e[33m",
48
- 'blue' => "\e[34m",
43
+ 'black' => "\e[30m",
44
+ 'red' => "\e[31m",
45
+ 'green' => "\e[32m",
46
+ 'yellow' => "\e[33m",
47
+ 'blue' => "\e[34m",
49
48
  'magenta' => "\e[35m",
50
- 'cyan' => "\e[36m",
51
- 'white' => "\e[37m",
52
- 'grey' => "\e[90m",
53
- 'bold' => "\e[1m"
49
+ 'cyan' => "\e[36m",
50
+ 'white' => "\e[37m",
51
+ 'grey' => "\e[90m",
52
+ 'bold' => "\e[1m"
54
53
  }.freeze
55
54
 
56
55
  ALIASES = Hash.new do |h, k|
57
- h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_arg/
56
+ "#{h[Regexp.last_match(1)]},bold" if k.to_s =~ /(.*)_arg/
58
57
  end.merge(
59
58
  'undefined' => 'yellow',
60
- 'pending' => 'yellow',
59
+ 'pending' => 'yellow',
61
60
  'executing' => 'grey',
62
- 'failed' => 'red',
63
- 'passed' => 'green',
64
- 'outline' => 'cyan',
65
- 'skipped' => 'cyan',
66
- 'comments' => 'grey',
67
- 'tag' => 'cyan'
61
+ 'failed' => 'red',
62
+ 'passed' => 'green',
63
+ 'outline' => 'cyan',
64
+ 'skipped' => 'cyan',
65
+ 'comments' => 'grey',
66
+ 'tag' => 'cyan'
68
67
  )
69
68
 
70
- if ENV['GHERKIN_COLORS'] # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
71
- ENV['GHERKIN_COLORS'].split(':').each do |pair|
72
- a = pair.split('=')
73
- ALIASES[a[0]] = a[1]
74
- end
69
+ # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
70
+ ENV.fetch('GHERKIN_COLORS', '').split(':').each do |pair|
71
+ rule, colour = pair.split('=')
72
+ ALIASES[colour] = rule
75
73
  end
76
74
 
77
- ALIASES.keys.each do |key|
75
+ ALIASES.each_key do |key|
78
76
  define_method(key) do
79
77
  ALIASES[key].split(',').map { |color| COLORS[color] }.join('')
80
78
  end
@@ -88,8 +86,8 @@ module Cucumber
88
86
  "\e[0m"
89
87
  end
90
88
 
91
- def up(n)
92
- "\e[#{n}A"
89
+ def up(amount)
90
+ "\e[#{amount}A"
93
91
  end
94
92
  end
95
93
  end
@@ -17,7 +17,7 @@ module Cucumber
17
17
  messages = ::Gherkin.from_source('dummy', feature_header(dialect) + text, gherkin_options)
18
18
 
19
19
  messages.each do |message|
20
- gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
20
+ gherkin_document = message.gherkin_document.to_h unless message.gherkin_document.nil?
21
21
  end
22
22
 
23
23
  @builder.steps(gherkin_document[:feature][:children][0][:scenario][:steps])
@@ -19,8 +19,8 @@ module Cucumber
19
19
  @rb_language.build_rb_world_factory(world_modules, namespaced_world_modules, proc)
20
20
  end
21
21
 
22
- def register_rb_hook(phase, tag_names, proc)
23
- @rb_language.register_rb_hook(phase, tag_names, proc)
22
+ def register_rb_hook(phase, tag_names, proc, name: nil)
23
+ @rb_language.register_rb_hook(phase, tag_names, proc, name: name)
24
24
  end
25
25
 
26
26
  def define_parameter_type(parameter_type)
@@ -62,14 +62,14 @@ module Cucumber
62
62
 
63
63
  # Registers a proc that will run before each Scenario. You can register as many
64
64
  # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
65
- def Before(*tag_expressions, &proc)
66
- Dsl.register_rb_hook('before', tag_expressions, proc)
65
+ def Before(*tag_expressions, name: nil, &proc)
66
+ Dsl.register_rb_hook('before', tag_expressions, proc, name: name)
67
67
  end
68
68
 
69
69
  # Registers a proc that will run after each Scenario. You can register as many
70
70
  # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
71
- def After(*tag_expressions, &proc)
72
- Dsl.register_rb_hook('after', tag_expressions, proc)
71
+ def After(*tag_expressions, name: nil, &proc)
72
+ Dsl.register_rb_hook('after', tag_expressions, proc, name: name)
73
73
  end
74
74
 
75
75
  # Registers a proc that will be wrapped around each scenario. The proc
@@ -77,14 +77,14 @@ module Cucumber
77
77
  # argument (but passed as a regular argument, since blocks cannot accept
78
78
  # blocks in 1.8), on which it should call the .call method. You can register
79
79
  # as many as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
80
- def Around(*tag_expressions, &proc)
81
- Dsl.register_rb_hook('around', tag_expressions, proc)
80
+ def Around(*tag_expressions, name: nil, &proc)
81
+ Dsl.register_rb_hook('around', tag_expressions, proc, name: name)
82
82
  end
83
83
 
84
84
  # Registers a proc that will run after each Step. You can register as
85
85
  # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
86
- def AfterStep(*tag_expressions, &proc)
87
- Dsl.register_rb_hook('after_step', tag_expressions, proc)
86
+ def AfterStep(*tag_expressions, name: nil, &proc)
87
+ Dsl.register_rb_hook('after_step', tag_expressions, proc, name: name)
88
88
  end
89
89
 
90
90
  def ParameterType(options)
@@ -107,10 +107,21 @@ module Cucumber
107
107
  value.nil? ? default : value
108
108
  end
109
109
 
110
- # Registers a proc that will run after Cucumber is configured. You can register as
111
- # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
112
- def AfterConfiguration(&proc)
113
- Dsl.register_rb_hook('after_configuration', [], proc)
110
+ # Registers a proc that will run after Cucumber is configured in order to install an external plugin.
111
+ def InstallPlugin(name: nil, &proc)
112
+ Dsl.register_rb_hook('install_plugin', [], proc, name: name)
113
+ end
114
+
115
+ # Registers a proc that will run before the execution of the scenarios.
116
+ # Use it for your final set-ups
117
+ def BeforeAll(name: nil, &proc)
118
+ Dsl.register_rb_hook('before_all', [], proc, name: name)
119
+ end
120
+
121
+ # Registers a proc that will run after the execution of the scenarios.
122
+ # Use it for your final clean-ups
123
+ def AfterAll(name: nil, &proc)
124
+ Dsl.register_rb_hook('after_all', [], proc, name: name)
114
125
  end
115
126
 
116
127
  # Registers a new Ruby StepDefinition. This method is aliased
@@ -136,5 +147,8 @@ module Cucumber
136
147
  end
137
148
  end
138
149
 
139
- # TODO: can we avoid adding methods to the global namespace (Kernel)
140
- extend(Cucumber::Glue::Dsl) # rubocop:disable Style/MixinUsage
150
+ # rubocop:disable Style/MixinUsage
151
+ # This "should" always be present, because it allows users to write `Before` and `After`
152
+ # See. https://github.com/cucumber/cucumber-ruby/pull/1566#discussion_r683235396
153
+ extend(Cucumber::Glue::Dsl)
154
+ # rubocop:enable Style/MixinUsage
@@ -6,11 +6,12 @@ module Cucumber
6
6
  module Glue
7
7
  # TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire
8
8
  class Hook
9
- attr_reader :id, :tag_expressions, :location
9
+ attr_reader :id, :tag_expressions, :location, :name
10
10
 
11
- def initialize(id, registry, tag_expressions, proc)
11
+ def initialize(id, registry, tag_expressions, proc, name: nil)
12
12
  @id = id
13
13
  @registry = registry
14
+ @name = name
14
15
  @tag_expressions = sanitize_tag_expressions(tag_expressions)
15
16
  @proc = proc
16
17
  @location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
@@ -32,7 +33,8 @@ module Cucumber
32
33
  Cucumber::Messages::Envelope.new(
33
34
  hook: Cucumber::Messages::Hook.new(
34
35
  id: id,
35
- tag_expression: tag_expressions.join(' '),
36
+ name: name,
37
+ tag_expression: tag_expressions.empty? ? nil : tag_expressions.join(' '),
36
38
  source_reference: Cucumber::Messages::SourceReference.new(
37
39
  uri: location.file,
38
40
  location: Cucumber::Messages::Location.new(
@@ -58,6 +60,7 @@ module Cucumber
58
60
  end
59
61
 
60
62
  next unless tag_expression.include?(',')
63
+
61
64
  warn("Found tagged hook with '#{tag_expression}'." \
62
65
  "'@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.")
63
66
  end
@@ -12,6 +12,7 @@ module Cucumber
12
12
 
13
13
  instance_exec_pos = backtrace.index(instance_exec_invocation_line)
14
14
  return unless instance_exec_pos
15
+
15
16
  replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
16
17
  backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
17
18
 
@@ -27,7 +28,7 @@ module Cucumber
27
28
  if check_arity && !cucumber_compatible_arity?(args, block)
28
29
  world.instance_exec do
29
30
  ari = block.arity
30
- ari = ari < 0 ? (ari.abs - 1).to_s + '+' : ari
31
+ ari = ari.negative? ? "#{ari.abs - 1}+" : ari
31
32
  s1 = ari == 1 ? '' : 's'
32
33
  s2 = args.length == 1 ? '' : 's'
33
34
  raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
@@ -40,15 +41,14 @@ module Cucumber
40
41
 
41
42
  def self.cucumber_compatible_arity?(args, block)
42
43
  return true if block.arity == args.length
43
- if block.arity < 0
44
- return true if args.length >= (block.arity.abs - 1)
45
- end
44
+ return true if block.arity.negative? && args.length >= (block.arity.abs - 1)
45
+
46
46
  false
47
47
  end
48
48
 
49
49
  def self.cucumber_run_with_backtrace_filtering(pseudo_method)
50
50
  yield
51
- rescue Exception => e # rubocop:disable Lint/RescueException
51
+ rescue Exception => e
52
52
  instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
53
53
  replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
54
54
  raise e
@@ -3,10 +3,11 @@
3
3
  require 'cucumber/gherkin/formatter/ansi_escapes'
4
4
  require 'cucumber/core/test/data_table'
5
5
  require 'cucumber/deprecate'
6
+ require 'mini_mime'
6
7
 
7
8
  module Cucumber
8
9
  module Glue
9
- # Defines the basic API methods availlable in all Cucumber step definitions.
10
+ # Defines the basic API methods available in all Cucumber step definitions.
10
11
  #
11
12
  # You can, and probably should, extend this API with your own methods that
12
13
  # make sense in your domain. For more on that, see {Cucumber::Glue::Dsl#World}
@@ -25,7 +26,7 @@ module Cucumber
25
26
  # @example Passing a multiline string
26
27
  # step "the email should contain:", "Dear sir,\nYou've won a prize!\n"
27
28
  # @param [String] name The name of the step
28
- # @param [String,Cucumber::Test::DocString,Cucumber::Ast::Table] multiline_argument
29
+ # @param [String, Cucumber::Test::DocString, Cucumber::Ast::Table] raw_multiline_arg
29
30
  def step(name, raw_multiline_arg = nil)
30
31
  super
31
32
  end
@@ -72,46 +73,31 @@ module Cucumber
72
73
  MultilineArgument::DataTable.from(text_or_table)
73
74
  end
74
75
 
75
- # Print a message to the output.
76
- #
77
- # @note Cucumber might surprise you with the behaviour of this method. Instead
78
- # of sending the output directly to STDOUT, Cucumber will intercept and cache
79
- # the message until the current step has finished, and then display it.
80
- #
81
- # If you'd prefer to see the message immediately, call {Kernel.puts} instead.
82
- def puts(*messages)
83
- Cucumber.deprecate(
84
- 'Messages emitted with "puts" will no longer be caught by Cucumber ' \
85
- 'and sent to the formatter. If you want message to be in the formatted output, ' \
86
- "please use log(message) instead.\n" \
87
- 'If you simply want it in the console, '\
88
- 'keep using "puts" (or Kernel.puts to avoid this message)',
89
- 'puts(message)',
90
- '5.0.0'
91
- )
92
- messages.each { |message| log(message.to_s) }
93
- end
94
-
95
76
  # Pause the tests and ask the operator for input
96
77
  def ask(question, timeout_seconds = 60)
97
78
  super
98
79
  end
99
80
 
100
- # Embed an image in the output
101
- def embed(file, mime_type, _label = 'Screenshot')
102
- Cucumber.deprecate(
103
- 'Please use attach(file, media_type) instead',
104
- 'embed(file, mime_type, label)',
105
- '5.0.0'
106
- )
107
- attach(file, mime_type)
108
- end
109
-
110
81
  def log(*messages)
111
82
  messages.each { |message| attach(message.to_s.dup, 'text/x.cucumber.log+plain') }
112
83
  end
113
84
 
114
- def attach(file, media_type)
85
+ # Attach a file to the output
86
+ # @param file [string|io] the file to attach.
87
+ # It can be a string containing the file content itself, the file path, or an IO ready to be read.
88
+ # @param media_type [string] the media type.
89
+ # If file is a valid path, media_type can be omitted, it will then be inferred from the file name.
90
+ # @param filename [string] the name of the file you wish to specify.
91
+ # This is only needed in situations where you want to rename a PDF download e.t.c. - In most situations
92
+ # you should not need to pass a filename
93
+ def attach(file, media_type = nil, filename = nil)
94
+ return super unless File.file?(file)
95
+
96
+ content = File.read(file, mode: 'rb')
97
+ media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?
98
+
99
+ super(content, media_type.to_s, filename)
100
+ rescue StandardError
115
101
  super
116
102
  end
117
103
 
@@ -119,12 +105,9 @@ module Cucumber
119
105
  def pending(message = 'TODO')
120
106
  raise Pending, message unless block_given?
121
107
 
122
- begin
123
- yield
124
- rescue Exception # rubocop:disable Lint/RescueException
125
- raise Pending, message
126
- end
127
- raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
108
+ yield
109
+ rescue Exception
110
+ raise Pending, message
128
111
  end
129
112
 
130
113
  # Skips this step and the remaining steps in the scenario
@@ -142,9 +125,9 @@ module Cucumber
142
125
  inspect
143
126
  end
144
127
 
145
- # Dynamially generate the API module, closuring the dependencies
146
- def self.for(runtime, language) # rubocop:disable Metrics/MethodLength
147
- Module.new do # rubocop:disable Metrics/BlockLength
128
+ # Dynamically generate the API module, closuring the dependencies
129
+ def self.for(runtime, language)
130
+ Module.new do
148
131
  def self.extended(object)
149
132
  # wrap the dynamically generated module so that we can document the methods
150
133
  # for yardoc, which doesn't like define_method.
@@ -154,8 +137,8 @@ module Cucumber
154
137
  # TODO: pass these in when building the module, instead of mutating them later
155
138
  # Extend the World with user-defined modules
156
139
  def add_modules!(world_modules, namespaced_world_modules)
157
- add_world_modules!(world_modules)
158
- add_namespaced_modules!(namespaced_world_modules)
140
+ add_world_modules!(world_modules) if world_modules.any?
141
+ add_namespaced_modules!(namespaced_world_modules) if namespaced_world_modules.any?
159
142
  end
160
143
 
161
144
  define_method(:step) do |name, raw_multiline_arg = nil|
@@ -172,8 +155,8 @@ module Cucumber
172
155
  runtime.ask(question, timeout_seconds)
173
156
  end
174
157
 
175
- define_method(:attach) do |file, media_type|
176
- runtime.attach(file, media_type)
158
+ define_method(:attach) do |file, media_type, filename|
159
+ runtime.attach(file, media_type, filename)
177
160
  end
178
161
 
179
162
  # Prints the list of modules that are included in the World
@@ -188,27 +171,24 @@ module Cucumber
188
171
 
189
172
  private
190
173
 
191
- # @private
192
174
  def add_world_modules!(modules)
193
175
  modules.each do |world_module|
194
176
  extend(world_module)
195
177
  end
196
178
  end
197
179
 
198
- # @private
199
180
  def add_namespaced_modules!(modules)
200
181
  @__namespaced_modules = modules
201
182
  modules.each do |namespace, world_modules|
202
183
  world_modules.each do |world_module|
203
184
  variable_name = "@__#{namespace}_world"
185
+ inner_world = instance_variable_get(variable_name) || Object.new
186
+
187
+ instance_variable_set(
188
+ variable_name,
189
+ inner_world.extend(world_module)
190
+ )
204
191
 
205
- inner_world = if self.class.respond_to?(namespace)
206
- instance_variable_get(variable_name)
207
- else
208
- Object.new
209
- end
210
- instance_variable_set(variable_name,
211
- inner_world.extend(world_module))
212
192
  self.class.send(:define_method, namespace) do
213
193
  instance_variable_get(variable_name)
214
194
  end
@@ -216,14 +196,14 @@ module Cucumber
216
196
  end
217
197
  end
218
198
 
219
- # @private
220
199
  def stringify_namespaced_modules
200
+ return '' if @__namespaced_modules.nil?
201
+
221
202
  @__namespaced_modules.map { |k, v| "#{v.join(',')} (as #{k})" }.join('+')
222
203
  end
223
204
  end
224
205
  end
225
206
 
226
- # @private
227
207
  AnsiEscapes = Cucumber::Gherkin::Formatter::AnsiEscapes
228
208
  end
229
209
  end
@@ -4,6 +4,7 @@ require 'cucumber/cucumber_expressions/parameter_type_registry'
4
4
  require 'cucumber/cucumber_expressions/cucumber_expression'
5
5
  require 'cucumber/cucumber_expressions/regular_expression'
6
6
  require 'cucumber/cucumber_expressions/cucumber_expression_generator'
7
+ require 'cucumber/deprecate'
7
8
  require 'cucumber/glue/dsl'
8
9
  require 'cucumber/glue/snippet'
9
10
  require 'cucumber/glue/hook'
@@ -28,7 +29,8 @@ module Cucumber
28
29
  # Raised if there are 2 or more World blocks.
29
30
  class MultipleWorld < StandardError
30
31
  def initialize(first_proc, second_proc)
31
- message = String.new # rubocop:disable Style/EmptyLiteral
32
+ # TODO: [LH] - Just use a heredoc here to fix this up
33
+ message = String.new
32
34
  message << "You can only pass a proc to #World once, but it's happening\n"
33
35
  message << "in 2 places:\n\n"
34
36
  message << Glue.backtrace_line(first_proc, 'World') << "\n"
@@ -71,8 +73,8 @@ module Cucumber
71
73
  end
72
74
  end
73
75
 
74
- def register_rb_hook(phase, tag_expressions, proc)
75
- hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc))
76
+ def register_rb_hook(phase, tag_expressions, proc, name: nil)
77
+ hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc, name: name))
76
78
  @configuration.notify :envelope, hook.to_envelope
77
79
  hook
78
80
  end
@@ -91,7 +93,7 @@ module Cucumber
91
93
  step_definition
92
94
  rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e
93
95
  # TODO: add a way to extract the parameter type directly from the error.
94
- type_name = e.message.match(/^Undefined parameter type \{(.*)\}$/)[1]
96
+ type_name = e.message.match(/^Undefined parameter type ['|{](.*)['|}].?$/)[1]
95
97
 
96
98
  @configuration.notify :undefined_parameter_type, type_name, string_or_regexp
97
99
  end
@@ -99,6 +101,7 @@ module Cucumber
99
101
  def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
100
102
  if proc
101
103
  raise MultipleWorld.new(@world_proc, proc) if @world_proc
104
+
102
105
  @world_proc = proc
103
106
  end
104
107
  @world_modules ||= []
@@ -134,9 +137,21 @@ module Cucumber
134
137
  @current_world = nil
135
138
  end
136
139
 
137
- def after_configuration(configuration)
138
- hooks[:after_configuration].each do |hook|
139
- hook.invoke('AfterConfiguration', configuration)
140
+ def install_plugin(configuration, registry)
141
+ hooks[:install_plugin].each do |hook|
142
+ hook.invoke('InstallPlugin', [configuration, registry])
143
+ end
144
+ end
145
+
146
+ def before_all
147
+ hooks[:before_all].each do |hook|
148
+ hook.invoke('BeforeAll', [])
149
+ end
150
+ end
151
+
152
+ def after_all
153
+ hooks[:after_all].each do |hook|
154
+ hook.invoke('AfterAll', [])
140
155
  end
141
156
  end
142
157
 
@@ -149,7 +164,7 @@ module Cucumber
149
164
  @hooks = nil
150
165
  end
151
166
 
152
- def hooks_for(phase, scenario) #:nodoc:
167
+ def hooks_for(phase, scenario) # :nodoc:
153
168
  hooks[phase.to_sym].select { |hook| scenario.accept_hook?(hook) }
154
169
  end
155
170
 
@@ -168,6 +183,7 @@ module Cucumber
168
183
  def create_expression(string_or_regexp)
169
184
  return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
170
185
  return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
186
+
171
187
  raise ArgumentError, 'Expression must be a String or Regexp'
172
188
  end
173
189
 
@@ -182,16 +198,19 @@ module Cucumber
182
198
  private
183
199
 
184
200
  def parameter_type_envelope(parameter_type)
185
- # TODO: should me moved to Cucumber::Expression::ParameterType#to_envelope ?
201
+ # TODO: should this be moved to Cucumber::Expression::ParameterType#to_envelope ??
186
202
  # Note: that would mean that cucumber-expression would depend on cucumber-messages
187
-
188
203
  Cucumber::Messages::Envelope.new(
189
204
  parameter_type: Cucumber::Messages::ParameterType.new(
190
205
  id: @configuration.id_generator.new_id,
191
206
  name: parameter_type.name,
192
207
  regular_expressions: parameter_type.regexps.map(&:to_s),
193
- prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match?,
194
- use_for_snippets: parameter_type.use_for_snippets?
208
+ prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match,
209
+ use_for_snippets: parameter_type.use_for_snippets,
210
+ source_reference: Cucumber::Messages::SourceReference.new(
211
+ uri: parameter_type.transformer.source_location[0],
212
+ location: Cucumber::Messages::Location.new(line: parameter_type.transformer.source_location[1])
213
+ )
195
214
  )
196
215
  )
197
216
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Glue
5
+ ##
6
+ # This class wraps some internals methods to expose them to external plugins.
7
+ class RegistryWrapper
8
+ def initialize(registry)
9
+ @registry = registry
10
+ end
11
+
12
+ ##
13
+ # Creates a new CucumberExpression from the given +string_or_regexp+.
14
+ #
15
+ # If +string_or_regexp+ is a string, it will return a new CucumberExpression::CucumberExpression
16
+ #
17
+ # If +string_or_regexp+ is a regexp, it will return a new CucumberExpressions::RegularExpression
18
+ #
19
+ # An ArgumentError is raised if +string_or_regexp+ is not a string or a regexp
20
+ def create_expression(string_or_regexp)
21
+ @registry.create_expression(string_or_regexp)
22
+ end
23
+
24
+ ##
25
+ # Return the current execution environment - AKA an isntance of World
26
+ def current_world
27
+ @registry.current_world
28
+ end
29
+ end
30
+ end
31
+ end
@@ -61,7 +61,8 @@ module Cucumber
61
61
  end
62
62
 
63
63
  def do_block
64
- do_block = String.new # rubocop:disable Style/EmptyLiteral
64
+ # TODO: [LH] - Just use a heredoc here to fix this up
65
+ do_block = String.new
65
66
  do_block << "do#{parameters}\n"
66
67
  multiline_argument.append_comment_to(do_block)
67
68
  do_block << " pending # Write code here that turns the phrase above into concrete actions\n"
@@ -95,7 +96,8 @@ module Cucumber
95
96
  "#{prefix}#{code_keyword}('#{expr.source}') do#{parameters(expr)}"
96
97
  end.join("\n")
97
98
 
98
- body = String.new # rubocop:disable Style/EmptyLiteral
99
+ # TODO: [LH] - Just use a heredoc here to fix this up
100
+ body = String.new
99
101
  multiline_argument.append_comment_to(body)
100
102
  body << " pending # Write code here that turns the phrase above into concrete actions\n"
101
103
  body << 'end'