cucumber 3.2.0 → 5.2.0

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +280 -19
  3. data/CONTRIBUTING.md +11 -25
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +46 -53
  7. data/lib/cucumber/cli/configuration.rb +5 -5
  8. data/lib/cucumber/cli/main.rb +12 -12
  9. data/lib/cucumber/cli/options.rb +90 -73
  10. data/lib/cucumber/cli/profile_loader.rb +49 -26
  11. data/lib/cucumber/configuration.rb +44 -29
  12. data/lib/cucumber/constantize.rb +2 -5
  13. data/lib/cucumber/deprecate.rb +31 -7
  14. data/lib/cucumber/errors.rb +5 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  17. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  18. data/lib/cucumber/events/step_activated.rb +2 -1
  19. data/lib/cucumber/events/test_case_created.rb +13 -0
  20. data/lib/cucumber/events/test_case_ready.rb +12 -0
  21. data/lib/cucumber/events/test_step_created.rb +13 -0
  22. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  23. data/lib/cucumber/events.rb +13 -6
  24. data/lib/cucumber/file_specs.rb +6 -6
  25. data/lib/cucumber/filters/activate_steps.rb +5 -3
  26. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  27. data/lib/cucumber/filters/prepare_world.rb +5 -9
  28. data/lib/cucumber/filters/quit.rb +1 -3
  29. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  30. data/lib/cucumber/filters.rb +1 -0
  31. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  32. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  33. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  34. data/lib/cucumber/formatter/console.rb +58 -66
  35. data/lib/cucumber/formatter/console_counts.rb +4 -9
  36. data/lib/cucumber/formatter/console_issues.rb +6 -3
  37. data/lib/cucumber/formatter/duration.rb +1 -1
  38. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  39. data/lib/cucumber/formatter/errors.rb +6 -0
  40. data/lib/cucumber/formatter/fanout.rb +2 -0
  41. data/lib/cucumber/formatter/html.rb +11 -598
  42. data/lib/cucumber/formatter/http_io.rb +43 -42
  43. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  44. data/lib/cucumber/formatter/interceptor.rb +11 -30
  45. data/lib/cucumber/formatter/io.rb +46 -10
  46. data/lib/cucumber/formatter/json.rb +102 -116
  47. data/lib/cucumber/formatter/junit.rb +55 -55
  48. data/lib/cucumber/formatter/message.rb +22 -0
  49. data/lib/cucumber/formatter/message_builder.rb +255 -0
  50. data/lib/cucumber/formatter/pretty.rb +359 -153
  51. data/lib/cucumber/formatter/progress.rb +30 -32
  52. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  53. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  54. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  55. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  56. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  57. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  58. data/lib/cucumber/formatter/rerun.rb +22 -4
  59. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  60. data/lib/cucumber/formatter/steps.rb +3 -4
  61. data/lib/cucumber/formatter/summary.rb +16 -8
  62. data/lib/cucumber/formatter/unicode.rb +15 -17
  63. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  64. data/lib/cucumber/formatter/usage.rb +11 -10
  65. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  66. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  67. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  68. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  69. data/lib/cucumber/glue/hook.rb +34 -11
  70. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  71. data/lib/cucumber/glue/proto_world.rb +42 -33
  72. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  73. data/lib/cucumber/glue/snippet.rb +23 -22
  74. data/lib/cucumber/glue/step_definition.rb +42 -19
  75. data/lib/cucumber/glue/world_factory.rb +1 -1
  76. data/lib/cucumber/hooks.rb +11 -11
  77. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
  78. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  79. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  80. data/lib/cucumber/multiline_argument.rb +4 -6
  81. data/lib/cucumber/platform.rb +3 -3
  82. data/lib/cucumber/rake/task.rb +16 -18
  83. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  84. data/lib/cucumber/rspec/doubles.rb +3 -5
  85. data/lib/cucumber/running_test_case.rb +2 -53
  86. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  87. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  88. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  89. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  90. data/lib/cucumber/runtime/support_code.rb +13 -15
  91. data/lib/cucumber/runtime/user_interface.rb +6 -16
  92. data/lib/cucumber/runtime.rb +41 -58
  93. data/lib/cucumber/step_definition_light.rb +4 -3
  94. data/lib/cucumber/step_definitions.rb +2 -2
  95. data/lib/cucumber/step_match.rb +12 -11
  96. data/lib/cucumber/step_match_search.rb +2 -1
  97. data/lib/cucumber/term/ansicolor.rb +9 -9
  98. data/lib/cucumber/term/banner.rb +56 -0
  99. data/lib/cucumber/version +1 -1
  100. data/lib/cucumber.rb +1 -1
  101. metadata +253 -80
  102. data/lib/cucumber/formatter/cucumber.css +0 -286
  103. data/lib/cucumber/formatter/cucumber.sass +0 -247
  104. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  105. data/lib/cucumber/formatter/html_builder.rb +0 -121
  106. data/lib/cucumber/formatter/inline-js.js +0 -30
  107. data/lib/cucumber/formatter/jquery-min.js +0 -154
  108. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  109. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  110. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  111. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  112. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  113. data/lib/cucumber/step_argument.rb +0 -25
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gherkin/token_scanner'
4
- require 'gherkin/parser'
3
+ require 'gherkin'
5
4
  require 'gherkin/dialect'
6
5
 
7
6
  module Cucumber
@@ -12,15 +11,27 @@ module Cucumber
12
11
  end
13
12
 
14
13
  def parse(text)
15
- token_scanner = ::Gherkin::TokenScanner.new(feature_header + text)
16
- parser = ::Gherkin::Parser.new
17
- gherkin_document = parser.parse(token_scanner)
14
+ gherkin_document = nil
15
+ messages = ::Gherkin.from_source('dummy', feature_header + text, gherkin_options)
18
16
 
19
- gherkin_document[:feature][:children][0][:steps][0][:argument][:rows].each do |row|
17
+ messages.each do |message|
18
+ gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
19
+ end
20
+
21
+ return if gherkin_document.nil?
22
+ gherkin_document[:feature][:children][0][:scenario][:steps][0][:data_table][:rows].each do |row|
20
23
  @builder.row(row[:cells].map { |cell| cell[:value] })
21
24
  end
22
25
  end
23
26
 
27
+ def gherkin_options
28
+ {
29
+ include_source: false,
30
+ include_gherkin_document: true,
31
+ include_pickles: false
32
+ }
33
+ end
34
+
24
35
  def feature_header
25
36
  dialect = ::Gherkin::Dialect.for('en')
26
37
  %(#{dialect.feature_keywords[0]}:
@@ -51,23 +51,21 @@ module Cucumber
51
51
  'white' => "\e[37m",
52
52
  'grey' => "\e[90m",
53
53
  'bold' => "\e[1m"
54
- }
54
+ }.freeze
55
55
 
56
56
  ALIASES = Hash.new do |h, k|
57
- if k.to_s =~ /(.*)_arg/
58
- h[$1] + ',bold'
59
- end
60
- end.merge({
61
- 'undefined' => 'yellow',
62
- 'pending' => 'yellow',
63
- 'executing' => 'grey',
64
- 'failed' => 'red',
65
- 'passed' => 'green',
66
- 'outline' => 'cyan',
67
- 'skipped' => 'cyan',
68
- 'comments' => 'grey',
69
- 'tag' => 'cyan'
70
- })
57
+ h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_arg/
58
+ end.merge(
59
+ 'undefined' => 'yellow',
60
+ 'pending' => 'yellow',
61
+ 'executing' => 'grey',
62
+ 'failed' => 'red',
63
+ 'passed' => 'green',
64
+ 'outline' => 'cyan',
65
+ 'skipped' => 'cyan',
66
+ 'comments' => 'grey',
67
+ 'tag' => 'cyan'
68
+ )
71
69
 
72
70
  if ENV['GHERKIN_COLORS'] # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
73
71
  ENV['GHERKIN_COLORS'].split(':').each do |pair|
@@ -93,8 +91,6 @@ module Cucumber
93
91
  def up(n)
94
92
  "\e[#{n}A"
95
93
  end
96
-
97
- extend self
98
94
  end
99
95
  end
100
96
  end
@@ -10,8 +10,8 @@ module Cucumber
10
10
  # * \ becomes \\
11
11
  #
12
12
  # This is used in the pretty formatter.
13
- def escape_cell(s)
14
- s.gsub(/\\(?!\|)/, '\\\\\\\\').gsub(/\n/, '\\n').gsub(/\|/, '\\|')
13
+ def escape_cell(sym)
14
+ sym.gsub(/\\(?!\|)/, '\\\\\\\\').gsub(/\n/, '\\n').gsub(/\|/, '\\|')
15
15
  end
16
16
  end
17
17
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gherkin/token_scanner'
4
- require 'gherkin/token_matcher'
5
- require 'gherkin/parser'
3
+ require 'gherkin'
6
4
  require 'gherkin/dialect'
7
5
 
8
6
  module Cucumber
@@ -15,12 +13,23 @@ module Cucumber
15
13
 
16
14
  def parse(text)
17
15
  dialect = ::Gherkin::Dialect.for(@language)
18
- token_matcher = ::Gherkin::TokenMatcher.new(@language)
19
- token_scanner = ::Gherkin::TokenScanner.new(feature_header(dialect) + text)
20
- parser = ::Gherkin::Parser.new
21
- gherkin_document = parser.parse(token_scanner, token_matcher)
16
+ gherkin_document = nil
17
+ messages = ::Gherkin.from_source('dummy', feature_header(dialect) + text, gherkin_options)
22
18
 
23
- @builder.steps(gherkin_document[:feature][:children][0][:steps])
19
+ messages.each do |message|
20
+ gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
21
+ end
22
+
23
+ @builder.steps(gherkin_document[:feature][:children][0][:scenario][:steps])
24
+ end
25
+
26
+ def gherkin_options
27
+ {
28
+ default_dialect: @language,
29
+ include_source: false,
30
+ include_gherkin_document: true,
31
+ include_pickles: false
32
+ }
24
33
  end
25
34
 
26
35
  def feature_header(dialect)
@@ -6,14 +6,15 @@ 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 :tag_expressions, :location
9
+ attr_reader :id, :tag_expressions, :location
10
10
 
11
- def initialize(registry, tag_expressions, proc)
11
+ def initialize(id, registry, tag_expressions, proc)
12
+ @id = id
12
13
  @registry = registry
13
- @tag_expressions = tag_expressions
14
+ @tag_expressions = sanitize_tag_expressions(tag_expressions)
14
15
  @proc = proc
15
- @location = Cucumber::Core::Ast::Location.from_source_location(*@proc.source_location)
16
- warn_for_old_style_tag_expressions(tag_expressions)
16
+ @location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
17
+ fail_for_old_style_tag_expressions(@tag_expressions)
17
18
  end
18
19
 
19
20
  def invoke(pseudo_method, arguments, &block)
@@ -27,16 +28,38 @@ module Cucumber
27
28
  )
28
29
  end
29
30
 
31
+ def to_envelope
32
+ Cucumber::Messages::Envelope.new(
33
+ hook: Cucumber::Messages::Hook.new(
34
+ id: id,
35
+ tag_expression: tag_expressions.join(' '),
36
+ source_reference: Cucumber::Messages::SourceReference.new(
37
+ uri: location.file,
38
+ location: Cucumber::Messages::Location.new(
39
+ line: location.lines.first
40
+ )
41
+ )
42
+ )
43
+ )
44
+ end
45
+
30
46
  private
31
47
 
32
- def warn_for_old_style_tag_expressions(tag_expressions)
48
+ def sanitize_tag_expressions(tag_expressions)
49
+ # TODO: remove when '~@no-clobber' has been changed to 'not @no-clobber' in aruba
50
+ tag_expressions.map { |tag_expression| tag_expression == '~@no-clobber' ? 'not @no-clobber' : tag_expression }
51
+ end
52
+
53
+ def fail_for_old_style_tag_expressions(tag_expressions)
33
54
  tag_expressions.each do |tag_expression|
34
- if tag_expression.include?('~') && tag_expression != '~@no-clobber' # ~@no-clobber is used in aruba
35
- warn("Deprecated: Found tagged hook with '#{tag_expression}'. Support for '~@tag' will be removed from the next release of Cucumber. Please use 'not @tag' instead.")
36
- end
37
- if tag_expression.include?(',')
38
- warn("Deprecated: Found tagged hook with '#{tag_expression}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.")
55
+ if tag_expression.include?('~')
56
+ raise("Found tagged hook with '#{tag_expression}'." \
57
+ "'~@tag' is no longer supported, use 'not @tag' instead.")
39
58
  end
59
+
60
+ next unless tag_expression.include?(',')
61
+ warn("Found tagged hook with '#{tag_expression}'." \
62
+ "'@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.")
40
63
  end
41
64
  end
42
65
  end
@@ -11,16 +11,15 @@ module Cucumber
11
11
  return if Cucumber.use_full_backtrace
12
12
 
13
13
  instance_exec_pos = backtrace.index(instance_exec_invocation_line)
14
- if instance_exec_pos
15
- replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
16
- backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
14
+ return unless instance_exec_pos
15
+ replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
16
+ backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
17
17
 
18
- depth = backtrace.count { |line| line == instance_exec_invocation_line }
19
- end_pos = depth > 1 ? instance_exec_pos : -1
18
+ depth = backtrace.count { |line| line == instance_exec_invocation_line }
19
+ end_pos = depth > 1 ? instance_exec_pos : -1
20
20
 
21
- backtrace[replacement_line + 1..end_pos] = nil
22
- backtrace.compact!
23
- end
21
+ backtrace[replacement_line + 1..end_pos] = nil
22
+ backtrace.compact!
24
23
  end
25
24
 
26
25
  def self.cucumber_instance_exec_in(world, check_arity, pseudo_method, *args, &block)
@@ -31,9 +30,7 @@ module Cucumber
31
30
  ari = ari < 0 ? (ari.abs - 1).to_s + '+' : ari
32
31
  s1 = ari == 1 ? '' : 's'
33
32
  s2 = args.length == 1 ? '' : 's'
34
- raise ArityMismatchError.new(
35
- "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
36
- )
33
+ raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
37
34
  end
38
35
  else
39
36
  world.instance_exec(*args, &block)
@@ -50,13 +47,11 @@ module Cucumber
50
47
  end
51
48
 
52
49
  def self.cucumber_run_with_backtrace_filtering(pseudo_method)
53
- begin
54
- yield
55
- rescue Exception => e
56
- instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
57
- replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
58
- raise e
59
- end
50
+ yield
51
+ rescue Exception => e # rubocop:disable Lint/RescueException
52
+ instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
53
+ replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
54
+ raise e
60
55
  end
61
56
 
62
57
  INSTANCE_EXEC_OFFSET = -3
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cucumber/gherkin/formatter/ansi_escapes'
4
- require 'cucumber/core/ast/data_table'
4
+ require 'cucumber/core/test/data_table'
5
+ require 'cucumber/deprecate'
5
6
 
6
7
  module Cucumber
7
8
  module Glue
@@ -24,7 +25,7 @@ module Cucumber
24
25
  # @example Passing a multiline string
25
26
  # step "the email should contain:", "Dear sir,\nYou've won a prize!\n"
26
27
  # @param [String] name The name of the step
27
- # @param [String,Cucumber::Ast::DocString,Cucumber::Ast::Table] multiline_argument
28
+ # @param [String,Cucumber::Test::DocString,Cucumber::Ast::Table] multiline_argument
28
29
  def step(name, raw_multiline_arg = nil)
29
30
  super
30
31
  end
@@ -67,9 +68,8 @@ module Cucumber
67
68
  # %w{ CUC-101 Peeler 22 }
68
69
  # ])
69
70
  #
70
- def table(text_or_table, file = nil, line = 0)
71
- location = !file ? Core::Ast::Location.of_caller : Core::Ast::Location.new(file, line)
72
- MultilineArgument::DataTable.from(text_or_table, location)
71
+ def table(text_or_table)
72
+ MultilineArgument::DataTable.from(text_or_table)
73
73
  end
74
74
 
75
75
  # Print a message to the output.
@@ -80,7 +80,16 @@ module Cucumber
80
80
  #
81
81
  # If you'd prefer to see the message immediately, call {Kernel.puts} instead.
82
82
  def puts(*messages)
83
- super
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
+ '6.0.0'
91
+ )
92
+ messages.each { |message| log(message.to_s) }
84
93
  end
85
94
 
86
95
  # Pause the tests and ask the operator for input
@@ -89,22 +98,33 @@ module Cucumber
89
98
  end
90
99
 
91
100
  # Embed an image in the output
92
- def embed(file, mime_type, label = 'Screenshot')
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
+ '6.0.0'
106
+ )
107
+ attach(file, mime_type)
108
+ end
109
+
110
+ def log(*messages)
111
+ messages.each { |message| attach(message.to_s.dup, 'text/x.cucumber.log+plain') }
112
+ end
113
+
114
+ def attach(file, media_type)
93
115
  super
94
116
  end
95
117
 
96
118
  # Mark the matched step as pending.
97
119
  def pending(message = 'TODO')
98
- if block_given?
99
- begin
100
- yield
101
- rescue Exception
102
- raise Pending, message
103
- end
104
- raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
105
- else
120
+ raise Pending, message unless block_given?
121
+
122
+ begin
123
+ yield
124
+ rescue Exception # rubocop:disable Lint/RescueException
106
125
  raise Pending, message
107
126
  end
127
+ raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
108
128
  end
109
129
 
110
130
  # Skips this step and the remaining steps in the scenario
@@ -123,8 +143,8 @@ module Cucumber
123
143
  end
124
144
 
125
145
  # Dynamially generate the API module, closuring the dependencies
126
- def self.for(runtime, language)
127
- Module.new do
146
+ def self.for(runtime, language) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
147
+ Module.new do # rubocop:disable Metrics/BlockLength
128
148
  def self.extended(object)
129
149
  # wrap the dynamically generated module so that we can document the methods
130
150
  # for yardoc, which doesn't like define_method.
@@ -139,32 +159,21 @@ module Cucumber
139
159
  end
140
160
 
141
161
  define_method(:step) do |name, raw_multiline_arg = nil|
142
- location = Core::Ast::Location.of_caller
162
+ location = Core::Test::Location.of_caller
143
163
  runtime.invoke_dynamic_step(name, MultilineArgument.from(raw_multiline_arg, location))
144
164
  end
145
165
 
146
166
  define_method(:steps) do |steps_text|
147
- location = Core::Ast::Location.of_caller
167
+ location = Core::Test::Location.of_caller
148
168
  runtime.invoke_dynamic_steps(steps_text, language, location)
149
169
  end
150
170
 
151
- # rubocop:disable UnneededInterpolation
152
- define_method(:puts) do |*messages|
153
- # Even though they won't be output until later, converting the messages to
154
- # strings right away will protect them from modifications to their original
155
- # objects in the mean time
156
- messages.collect! { |message| "#{message}" }
157
-
158
- runtime.puts(*messages)
159
- end
160
- # rubocop:enable UnneededInterpolation
161
-
162
171
  define_method(:ask) do |question, timeout_seconds = 60|
163
172
  runtime.ask(question, timeout_seconds)
164
173
  end
165
174
 
166
- define_method(:embed) do |file, mime_type, label = 'Screenshot'|
167
- runtime.embed(file, mime_type, label)
175
+ define_method(:attach) do |file, media_type|
176
+ runtime.attach(file, media_type)
168
177
  end
169
178
 
170
179
  # Prints the list of modules that are included in the World
@@ -174,7 +183,7 @@ module Cucumber
174
183
  modules += included_modules
175
184
  end
176
185
  modules << stringify_namespaced_modules
177
- format('#<%s:0x%x>', modules.join('+'), self.object_id)
186
+ format('#<%<modules>s:0x%<object_id>x>', modules: modules.join('+'), object_id: object_id)
178
187
  end
179
188
 
180
189
  private
@@ -28,7 +28,7 @@ module Cucumber
28
28
  # Raised if there are 2 or more World blocks.
29
29
  class MultipleWorld < StandardError
30
30
  def initialize(first_proc, second_proc)
31
- message = String.new
31
+ message = String.new # rubocop:disable Style/EmptyLiteral
32
32
  message << "You can only pass a proc to #World once, but it's happening\n"
33
33
  message << "in 2 places:\n\n"
34
34
  message << Glue.backtrace_line(first_proc, 'World') << "\n"
@@ -53,7 +53,8 @@ module Cucumber
53
53
  end
54
54
 
55
55
  def initialize(runtime, configuration)
56
- @runtime, @configuration = runtime, configuration
56
+ @runtime = runtime
57
+ @configuration = configuration
57
58
  @step_definitions = []
58
59
  Glue::Dsl.rb_language = self
59
60
  @world_proc = @world_modules = nil
@@ -63,27 +64,36 @@ module Cucumber
63
64
  end
64
65
 
65
66
  def step_matches(name_to_match)
66
- @step_definitions.reduce([]) do |result, step_definition|
67
+ @step_definitions.each_with_object([]) do |step_definition, result|
67
68
  if (arguments = step_definition.arguments_from(name_to_match))
68
69
  result << StepMatch.new(step_definition, name_to_match, arguments)
69
70
  end
70
- result
71
71
  end
72
72
  end
73
73
 
74
74
  def register_rb_hook(phase, tag_expressions, proc)
75
- add_hook(phase, Hook.new(self, tag_expressions, proc))
75
+ hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc))
76
+ @configuration.notify :envelope, hook.to_envelope
77
+ hook
76
78
  end
77
79
 
78
80
  def define_parameter_type(parameter_type)
81
+ @configuration.notify :envelope, parameter_type_envelope(parameter_type)
82
+
79
83
  @parameter_type_registry.define_parameter_type(parameter_type)
80
84
  end
81
85
 
82
86
  def register_rb_step_definition(string_or_regexp, proc_or_sym, options)
83
- step_definition = StepDefinition.new(self, string_or_regexp, proc_or_sym, options)
87
+ step_definition = StepDefinition.new(@configuration.id_generator.new_id, self, string_or_regexp, proc_or_sym, options)
84
88
  @step_definitions << step_definition
85
89
  @configuration.notify :step_definition_registered, step_definition
90
+ @configuration.notify :envelope, step_definition.to_envelope
86
91
  step_definition
92
+ rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e
93
+ # TODO: add a way to extract the parameter type directly from the error.
94
+ type_name = e.message.match(/^Undefined parameter type \{(.*)\}$/)[1]
95
+
96
+ @configuration.notify :undefined_parameter_type, type_name, string_or_regexp
87
97
  end
88
98
 
89
99
  def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
@@ -96,15 +106,20 @@ module Cucumber
96
106
 
97
107
  @namespaced_world_modules ||= Hash.new { |h, k| h[k] = [] }
98
108
  namespaced_world_modules.each do |namespace, world_module|
99
- unless @namespaced_world_modules[namespace].include?(world_module)
100
- @namespaced_world_modules[namespace] << world_module
101
- end
109
+ @namespaced_world_modules[namespace] << world_module unless @namespaced_world_modules[namespace].include?(world_module)
102
110
  end
103
111
  end
104
112
 
105
113
  def load_code_file(code_file)
106
114
  return unless File.extname(code_file) == '.rb'
107
- load File.expand_path(code_file) # This will cause self.add_step_definition, self.add_hook, and self.define_parameter_type to be called from Glue::Dsl
115
+
116
+ # This will cause self.add_step_definition, self.add_hook, and self.define_parameter_type to be called from Glue::Dsl
117
+
118
+ if Cucumber.use_legacy_autoloader
119
+ load File.expand_path(code_file)
120
+ else
121
+ require File.expand_path(code_file)
122
+ end
108
123
  end
109
124
 
110
125
  def begin_scenario(test_case)
@@ -153,7 +168,7 @@ module Cucumber
153
168
  def create_expression(string_or_regexp)
154
169
  return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
155
170
  return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
156
- raise ArgumentError.new('Expression must be a String or Regexp')
171
+ raise ArgumentError, 'Expression must be a String or Regexp'
157
172
  end
158
173
 
159
174
  def self.cli_snippet_type_options
@@ -166,6 +181,21 @@ module Cucumber
166
181
 
167
182
  private
168
183
 
184
+ def parameter_type_envelope(parameter_type)
185
+ # TODO: should me moved to Cucumber::Expression::ParameterType#to_envelope ?
186
+ # Note: that would mean that cucumber-expression would depend on cucumber-messages
187
+
188
+ Cucumber::Messages::Envelope.new(
189
+ parameter_type: Cucumber::Messages::ParameterType.new(
190
+ id: @configuration.id_generator.new_id,
191
+ name: parameter_type.name,
192
+ 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?
195
+ )
196
+ )
197
+ end
198
+
169
199
  def available_step_definition_hash
170
200
  @available_step_definition_hash ||= {}
171
201
  end
@@ -180,7 +210,7 @@ module Cucumber
180
210
  end
181
211
 
182
212
  def self.backtrace_line(proc, name)
183
- location = Cucumber::Core::Ast::Location.from_source_location(*proc.source_location)
213
+ location = Cucumber::Core::Test::Location.from_source_location(*proc.source_location)
184
214
  "#{location}:in `#{name}'"
185
215
  end
186
216
  end
@@ -3,7 +3,7 @@
3
3
  module Cucumber
4
4
  module Glue
5
5
  module Snippet
6
- ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
6
+ ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)'].freeze
7
7
 
8
8
  class Generator
9
9
  def self.register_on(configuration)
@@ -42,7 +42,7 @@ module Cucumber
42
42
  end
43
43
 
44
44
  def self.cli_option_string(type, cucumber_expression_generator)
45
- format('%-7s: %-28s e.g. %s', type, description, example(cucumber_expression_generator))
45
+ format('%<type>-7s: %<description>-28s e.g. %<example>s', type: type, description: description, example: example(cucumber_expression_generator))
46
46
  end
47
47
 
48
48
  private
@@ -61,7 +61,7 @@ module Cucumber
61
61
  end
62
62
 
63
63
  def do_block
64
- do_block = String.new
64
+ do_block = String.new # rubocop:disable Style/EmptyLiteral
65
65
  do_block << "do#{parameters}\n"
66
66
  multiline_argument.append_comment_to(do_block)
67
67
  do_block << " pending # Write code here that turns the phrase above into concrete actions\n"
@@ -72,11 +72,15 @@ module Cucumber
72
72
  def parameters
73
73
  block_args = (0...number_of_arguments).map { |n| "arg#{n + 1}" }
74
74
  multiline_argument.append_block_parameter_to(block_args)
75
- block_args.empty? ? '' : " |#{block_args.join(", ")}|"
75
+ block_args.empty? ? '' : " |#{block_args.join(', ')}|"
76
76
  end
77
77
 
78
- def self.example(cucumber_expression_generator)
79
- new(cucumber_expression_generator, 'Given', 'I have 2 cukes', MultilineArgument::None.new).step
78
+ class << self
79
+ private
80
+
81
+ def example(cucumber_expression_generator)
82
+ new(cucumber_expression_generator, 'Given', 'I have 2 cukes', MultilineArgument::None.new).step
83
+ end
80
84
  end
81
85
  end
82
86
 
@@ -87,11 +91,11 @@ module Cucumber
87
91
 
88
92
  def to_s
89
93
  header = generated_expressions.each_with_index.map do |expr, i|
90
- prefix = i == 0 ? '' : '# '
91
- "#{prefix}#{code_keyword}(\"#{expr.source}\") do#{parameters(expr)}"
94
+ prefix = i.zero? ? '' : '# '
95
+ "#{prefix}#{code_keyword}('#{expr.source}') do#{parameters(expr)}"
92
96
  end.join("\n")
93
97
 
94
- body = String.new
98
+ body = String.new # rubocop:disable Style/EmptyLiteral
95
99
  multiline_argument.append_comment_to(body)
96
100
  body << " pending # Write code here that turns the phrase above into concrete actions\n"
97
101
  body << 'end'
@@ -102,7 +106,7 @@ module Cucumber
102
106
  def parameters(expr)
103
107
  parameter_names = expr.parameter_names
104
108
  multiline_argument.append_block_parameter_to(parameter_names)
105
- parameter_names.empty? ? '' : " |#{parameter_names.join(", ")}|"
109
+ parameter_names.empty? ? '' : " |#{parameter_names.join(', ')}|"
106
110
  end
107
111
 
108
112
  def self.description
@@ -141,11 +145,11 @@ module Cucumber
141
145
  end
142
146
 
143
147
  SNIPPET_TYPES = {
144
- :cucumber_expression => CucumberExpression,
145
- :regexp => Regexp,
146
- :classic => Classic,
147
- :percent => Percent
148
- }
148
+ cucumber_expression: CucumberExpression,
149
+ regexp: Regexp,
150
+ classic: Classic,
151
+ percent: Percent
152
+ }.freeze
149
153
 
150
154
  module MultilineArgumentSnippet
151
155
  def self.new(multiline_argument)
@@ -170,11 +174,10 @@ module Cucumber
170
174
 
171
175
  class DocString
172
176
  def append_block_parameter_to(array)
173
- array << 'string'
177
+ array << 'doc_string'
174
178
  end
175
179
 
176
- def append_comment_to(string)
177
- end
180
+ def append_comment_to(string); end
178
181
  end
179
182
 
180
183
  class DataTable
@@ -192,11 +195,9 @@ module Cucumber
192
195
  end
193
196
 
194
197
  class None
195
- def append_block_parameter_to(array)
196
- end
198
+ def append_block_parameter_to(array); end
197
199
 
198
- def append_comment_to(string)
199
- end
200
+ def append_comment_to(string); end
200
201
  end
201
202
  end
202
203
  end