cucumber 3.0.2 → 4.0.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 (145) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +216 -17
  3. data/CONTRIBUTING.md +4 -21
  4. data/README.md +8 -10
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber.rb +1 -0
  7. data/lib/autotest/cucumber_mixin.rb +35 -39
  8. data/lib/autotest/cucumber_rails.rb +1 -0
  9. data/lib/autotest/cucumber_rails_rspec.rb +1 -0
  10. data/lib/autotest/cucumber_rails_rspec2.rb +1 -0
  11. data/lib/autotest/cucumber_rspec.rb +1 -0
  12. data/lib/autotest/cucumber_rspec2.rb +1 -0
  13. data/lib/autotest/discover.rb +1 -0
  14. data/lib/cucumber.rb +2 -1
  15. data/lib/cucumber/cli/configuration.rb +6 -5
  16. data/lib/cucumber/cli/main.rb +14 -14
  17. data/lib/cucumber/cli/options.rb +113 -116
  18. data/lib/cucumber/cli/profile_loader.rb +50 -29
  19. data/lib/cucumber/cli/rerun_file.rb +1 -0
  20. data/lib/cucumber/configuration.rb +38 -29
  21. data/lib/cucumber/constantize.rb +8 -10
  22. data/lib/cucumber/core_ext/string.rb +1 -0
  23. data/lib/cucumber/deprecate.rb +32 -8
  24. data/lib/cucumber/encoding.rb +2 -1
  25. data/lib/cucumber/errors.rb +6 -7
  26. data/lib/cucumber/events.rb +14 -7
  27. data/lib/cucumber/events/envelope.rb +9 -0
  28. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  29. data/lib/cucumber/events/gherkin_source_read.rb +1 -4
  30. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  31. data/lib/cucumber/events/step_activated.rb +6 -6
  32. data/lib/cucumber/events/step_definition_registered.rb +4 -8
  33. data/lib/cucumber/events/test_case_created.rb +13 -0
  34. data/lib/cucumber/events/test_case_finished.rb +0 -4
  35. data/lib/cucumber/events/test_case_ready.rb +12 -0
  36. data/lib/cucumber/events/test_case_started.rb +0 -4
  37. data/lib/cucumber/events/test_run_finished.rb +2 -3
  38. data/lib/cucumber/events/test_run_started.rb +2 -4
  39. data/lib/cucumber/events/test_step_created.rb +13 -0
  40. data/lib/cucumber/events/test_step_finished.rb +0 -4
  41. data/lib/cucumber/events/test_step_started.rb +1 -5
  42. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  43. data/lib/cucumber/file_specs.rb +7 -6
  44. data/lib/cucumber/filters.rb +2 -0
  45. data/lib/cucumber/filters/activate_steps.rb +6 -4
  46. data/lib/cucumber/filters/apply_after_hooks.rb +1 -0
  47. data/lib/cucumber/filters/apply_after_step_hooks.rb +1 -0
  48. data/lib/cucumber/filters/apply_around_hooks.rb +1 -0
  49. data/lib/cucumber/filters/apply_before_hooks.rb +1 -0
  50. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  51. data/lib/cucumber/filters/broadcast_test_run_started_event.rb +2 -1
  52. data/lib/cucumber/filters/gated_receiver.rb +1 -2
  53. data/lib/cucumber/filters/prepare_world.rb +6 -13
  54. data/lib/cucumber/filters/quit.rb +3 -6
  55. data/lib/cucumber/filters/randomizer.rb +6 -7
  56. data/lib/cucumber/filters/retry.rb +2 -2
  57. data/lib/cucumber/filters/tag_limits.rb +2 -2
  58. data/lib/cucumber/filters/tag_limits/test_case_index.rb +1 -2
  59. data/lib/cucumber/filters/tag_limits/verifier.rb +3 -6
  60. data/lib/cucumber/formatter/ansicolor.rb +33 -37
  61. data/lib/cucumber/formatter/ast_lookup.rb +165 -0
  62. data/lib/cucumber/formatter/backtrace_filter.rb +10 -10
  63. data/lib/cucumber/formatter/console.rb +65 -74
  64. data/lib/cucumber/formatter/console_counts.rb +4 -9
  65. data/lib/cucumber/formatter/console_issues.rb +9 -6
  66. data/lib/cucumber/formatter/duration.rb +2 -1
  67. data/lib/cucumber/formatter/duration_extractor.rb +4 -2
  68. data/lib/cucumber/formatter/errors.rb +6 -0
  69. data/lib/cucumber/formatter/fail_fast.rb +9 -6
  70. data/lib/cucumber/formatter/fanout.rb +3 -3
  71. data/lib/cucumber/formatter/html.rb +11 -602
  72. data/lib/cucumber/formatter/http_io.rb +146 -0
  73. data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -3
  74. data/lib/cucumber/formatter/interceptor.rb +11 -18
  75. data/lib/cucumber/formatter/io.rb +18 -11
  76. data/lib/cucumber/formatter/json.rb +102 -109
  77. data/lib/cucumber/formatter/junit.rb +73 -68
  78. data/lib/cucumber/formatter/message.rb +22 -0
  79. data/lib/cucumber/formatter/message_builder.rb +255 -0
  80. data/lib/cucumber/formatter/pretty.rb +360 -153
  81. data/lib/cucumber/formatter/progress.rb +31 -32
  82. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  83. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  84. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  85. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  86. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  87. data/lib/cucumber/formatter/rerun.rb +23 -4
  88. data/lib/cucumber/formatter/stepdefs.rb +2 -2
  89. data/lib/cucumber/formatter/steps.rb +4 -5
  90. data/lib/cucumber/formatter/summary.rb +17 -9
  91. data/lib/cucumber/formatter/unicode.rb +16 -18
  92. data/lib/cucumber/formatter/usage.rb +30 -26
  93. data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
  94. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +83 -86
  95. data/lib/cucumber/gherkin/formatter/escaping.rb +13 -12
  96. data/lib/cucumber/gherkin/i18n.rb +1 -0
  97. data/lib/cucumber/gherkin/steps_parser.rb +18 -8
  98. data/lib/cucumber/glue/dsl.rb +2 -1
  99. data/lib/cucumber/glue/hook.rb +35 -11
  100. data/lib/cucumber/glue/invoke_in_world.rb +15 -20
  101. data/lib/cucumber/glue/proto_world.rb +47 -39
  102. data/lib/cucumber/glue/registry_and_more.rb +54 -23
  103. data/lib/cucumber/glue/snippet.rb +24 -27
  104. data/lib/cucumber/glue/step_definition.rb +51 -28
  105. data/lib/cucumber/glue/world_factory.rb +1 -3
  106. data/lib/cucumber/hooks.rb +24 -14
  107. data/lib/cucumber/load_path.rb +1 -0
  108. data/lib/cucumber/multiline_argument.rb +6 -8
  109. data/lib/cucumber/multiline_argument/data_table.rb +106 -73
  110. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +8 -11
  111. data/lib/cucumber/multiline_argument/doc_string.rb +2 -1
  112. data/lib/cucumber/platform.rb +4 -3
  113. data/lib/cucumber/project_initializer.rb +1 -1
  114. data/lib/cucumber/rake/task.rb +21 -18
  115. data/lib/cucumber/rspec/disable_option_parser.rb +10 -8
  116. data/lib/cucumber/rspec/doubles.rb +1 -0
  117. data/lib/cucumber/running_test_case.rb +4 -54
  118. data/lib/cucumber/runtime.rb +57 -61
  119. data/lib/cucumber/runtime/after_hooks.rb +9 -4
  120. data/lib/cucumber/runtime/before_hooks.rb +9 -4
  121. data/lib/cucumber/runtime/for_programming_languages.rb +12 -9
  122. data/lib/cucumber/runtime/step_hooks.rb +5 -2
  123. data/lib/cucumber/runtime/support_code.rb +16 -22
  124. data/lib/cucumber/runtime/user_interface.rb +8 -19
  125. data/lib/cucumber/step_definition_light.rb +6 -4
  126. data/lib/cucumber/step_definitions.rb +3 -2
  127. data/lib/cucumber/step_match.rb +20 -18
  128. data/lib/cucumber/step_match_search.rb +9 -9
  129. data/lib/cucumber/term/ansicolor.rb +39 -39
  130. data/lib/cucumber/unit.rb +1 -0
  131. data/lib/cucumber/version +1 -1
  132. data/lib/simplecov_setup.rb +1 -0
  133. metadata +214 -127
  134. data/lib/cucumber/formatter/cucumber.css +0 -286
  135. data/lib/cucumber/formatter/cucumber.sass +0 -247
  136. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -41
  137. data/lib/cucumber/formatter/html_builder.rb +0 -120
  138. data/lib/cucumber/formatter/inline-js.js +0 -30
  139. data/lib/cucumber/formatter/jquery-min.js +0 -154
  140. data/lib/cucumber/formatter/json_pretty.rb +0 -10
  141. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  142. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  143. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  144. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  145. data/lib/cucumber/step_argument.rb +0 -24
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'gherkin/token_scanner'
3
- require 'gherkin/token_matcher'
4
- require 'gherkin/parser'
2
+
3
+ require 'gherkin'
5
4
  require 'gherkin/dialect'
6
5
 
7
6
  module Cucumber
@@ -14,12 +13,23 @@ module Cucumber
14
13
 
15
14
  def parse(text)
16
15
  dialect = ::Gherkin::Dialect.for(@language)
17
- token_matcher = ::Gherkin::TokenMatcher.new(@language)
18
- token_scanner = ::Gherkin::TokenScanner.new(feature_header(dialect) + text)
19
- parser = ::Gherkin::Parser.new
20
- gherkin_document = parser.parse(token_scanner, token_matcher)
16
+ gherkin_document = nil
17
+ messages = ::Gherkin.from_source('dummy', feature_header(dialect) + text, gherkin_options)
18
+
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
21
25
 
22
- @builder.steps(gherkin_document[:feature][:children][0][:steps])
26
+ def gherkin_options
27
+ {
28
+ default_dialect: @language,
29
+ include_source: false,
30
+ include_gherkin_document: true,
31
+ include_pickles: false
32
+ }
23
33
  end
24
34
 
25
35
  def feature_header(dialect)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/cucumber_expressions/parameter_type'
3
4
  require 'cucumber/deprecate'
4
5
 
@@ -136,4 +137,4 @@ module Cucumber
136
137
  end
137
138
 
138
139
  # TODO: can we avoid adding methods to the global namespace (Kernel)
139
- extend(Cucumber::Glue::Dsl)
140
+ extend(Cucumber::Glue::Dsl) # rubocop:disable Style/MixinUsage
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/glue/invoke_in_world'
3
4
 
4
5
  module Cucumber
5
6
  module Glue
6
7
  # TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire
7
8
  class Hook
8
- attr_reader :tag_expressions, :location
9
+ attr_reader :id, :tag_expressions, :location
9
10
 
10
- def initialize(registry, tag_expressions, proc)
11
+ def initialize(id, registry, tag_expressions, proc)
12
+ @id = id
11
13
  @registry = registry
12
- @tag_expressions = tag_expressions
14
+ @tag_expressions = sanitize_tag_expressions(tag_expressions)
13
15
  @proc = proc
14
- @location = Cucumber::Core::Ast::Location.from_source_location(*@proc.source_location)
15
- 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)
16
18
  end
17
19
 
18
20
  def invoke(pseudo_method, arguments, &block)
@@ -26,16 +28,38 @@ module Cucumber
26
28
  )
27
29
  end
28
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
+
29
46
  private
30
47
 
31
- 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)
32
54
  tag_expressions.each do |tag_expression|
33
- if tag_expression.include?('~') && tag_expression != '~@no-clobber' # ~@no-clobber is used in aruba
34
- 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.")
35
- end
36
- if tag_expression.include?(',')
37
- 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.")
38
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.")
39
63
  end
40
64
  end
41
65
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/platform'
3
4
  module Cucumber
4
5
  module Glue
@@ -10,16 +11,15 @@ module Cucumber
10
11
  return if Cucumber.use_full_backtrace
11
12
 
12
13
  instance_exec_pos = backtrace.index(instance_exec_invocation_line)
13
- if instance_exec_pos
14
- replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
15
- 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
16
17
 
17
- depth = backtrace.count { |line| line == instance_exec_invocation_line }
18
- 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
19
20
 
20
- backtrace[replacement_line+1..end_pos] = nil
21
- backtrace.compact!
22
- end
21
+ backtrace[replacement_line + 1..end_pos] = nil
22
+ backtrace.compact!
23
23
  end
24
24
 
25
25
  def self.cucumber_instance_exec_in(world, check_arity, pseudo_method, *args, &block)
@@ -27,12 +27,10 @@ module Cucumber
27
27
  if check_arity && !cucumber_compatible_arity?(args, block)
28
28
  world.instance_exec do
29
29
  ari = block.arity
30
- ari = ari < 0 ? (ari.abs-1).to_s+'+' : ari
30
+ ari = ari < 0 ? (ari.abs - 1).to_s + '+' : ari
31
31
  s1 = ari == 1 ? '' : 's'
32
32
  s2 = args.length == 1 ? '' : 's'
33
- raise ArityMismatchError.new(
34
- "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
35
- )
33
+ raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
36
34
  end
37
35
  else
38
36
  world.instance_exec(*args, &block)
@@ -49,13 +47,11 @@ module Cucumber
49
47
  end
50
48
 
51
49
  def self.cucumber_run_with_backtrace_filtering(pseudo_method)
52
- begin
53
- yield
54
- rescue Exception => e
55
- instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
56
- replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
57
- raise e
58
- 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
59
55
  end
60
56
 
61
57
  INSTANCE_EXEC_OFFSET = -3
@@ -65,6 +61,5 @@ module Cucumber
65
61
  # is different from the number of Proc arguments.
66
62
  class ArityMismatchError < StandardError
67
63
  end
68
-
69
64
  end
70
65
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/gherkin/formatter/ansi_escapes'
3
- require 'cucumber/core/ast/data_table'
4
+ require 'cucumber/core/test/data_table'
5
+ require 'cucumber/deprecate'
4
6
 
5
7
  module Cucumber
6
8
  module Glue
@@ -9,7 +11,6 @@ module Cucumber
9
11
  # You can, and probably should, extend this API with your own methods that
10
12
  # make sense in your domain. For more on that, see {Cucumber::Glue::Dsl#World}
11
13
  module ProtoWorld
12
-
13
14
  # Run a single Gherkin step
14
15
  # @example Call another step
15
16
  # step "I am logged in"
@@ -24,8 +25,8 @@ 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
- def step(name, raw_multiline_arg=nil)
28
+ # @param [String,Cucumber::Test::DocString,Cucumber::Ast::Table] multiline_argument
29
+ def step(name, raw_multiline_arg = nil)
29
30
  super
30
31
  end
31
32
 
@@ -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,31 +80,51 @@ 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
+ '5.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
87
- def ask(question, timeout_seconds=60)
96
+ def ask(question, timeout_seconds = 60)
88
97
  super
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
+ '5.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
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.
@@ -138,33 +158,22 @@ module Cucumber
138
158
  add_namespaced_modules!(namespaced_world_modules)
139
159
  end
140
160
 
141
- define_method(:step) do |name, raw_multiline_arg=nil|
142
- location = Core::Ast::Location.of_caller
161
+ define_method(:step) do |name, raw_multiline_arg = nil|
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
- define_method(:ask) do |question, timeout_seconds=60|
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
@@ -211,7 +220,6 @@ module Cucumber
211
220
  def stringify_namespaced_modules
212
221
  @__namespaced_modules.map { |k, v| "#{v.join(',')} (as #{k})" }.join('+')
213
222
  end
214
-
215
223
  end
216
224
  end
217
225
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/cucumber_expressions/parameter_type_registry'
3
4
  require 'cucumber/cucumber_expressions/cucumber_expression'
4
5
  require 'cucumber/cucumber_expressions/regular_expression'
@@ -27,7 +28,7 @@ module Cucumber
27
28
  # Raised if there are 2 or more World blocks.
28
29
  class MultipleWorld < StandardError
29
30
  def initialize(first_proc, second_proc)
30
- message = String.new
31
+ message = String.new # rubocop:disable Style/EmptyLiteral
31
32
  message << "You can only pass a proc to #World once, but it's happening\n"
32
33
  message << "in 2 places:\n\n"
33
34
  message << Glue.backtrace_line(first_proc, 'World') << "\n"
@@ -52,7 +53,8 @@ module Cucumber
52
53
  end
53
54
 
54
55
  def initialize(runtime, configuration)
55
- @runtime, @configuration = runtime, configuration
56
+ @runtime = runtime
57
+ @configuration = configuration
56
58
  @step_definitions = []
57
59
  Glue::Dsl.rb_language = self
58
60
  @world_proc = @world_modules = nil
@@ -62,27 +64,36 @@ module Cucumber
62
64
  end
63
65
 
64
66
  def step_matches(name_to_match)
65
- @step_definitions.reduce([]) { |result, step_definition|
67
+ @step_definitions.each_with_object([]) do |step_definition, result|
66
68
  if (arguments = step_definition.arguments_from(name_to_match))
67
69
  result << StepMatch.new(step_definition, name_to_match, arguments)
68
70
  end
69
- result
70
- }
71
+ end
71
72
  end
72
73
 
73
74
  def register_rb_hook(phase, tag_expressions, proc)
74
- 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
75
78
  end
76
79
 
77
80
  def define_parameter_type(parameter_type)
81
+ @configuration.notify :envelope, parameter_type_envelope(parameter_type)
82
+
78
83
  @parameter_type_registry.define_parameter_type(parameter_type)
79
84
  end
80
85
 
81
86
  def register_rb_step_definition(string_or_regexp, proc_or_sym, options)
82
- 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)
83
88
  @step_definitions << step_definition
84
89
  @configuration.notify :step_definition_registered, step_definition
90
+ @configuration.notify :envelope, step_definition.to_envelope
85
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
86
97
  end
87
98
 
88
99
  def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
@@ -95,15 +106,20 @@ module Cucumber
95
106
 
96
107
  @namespaced_world_modules ||= Hash.new { |h, k| h[k] = [] }
97
108
  namespaced_world_modules.each do |namespace, world_module|
98
- unless @namespaced_world_modules[namespace].include?(world_module)
99
- @namespaced_world_modules[namespace] << world_module
100
- end
109
+ @namespaced_world_modules[namespace] << world_module unless @namespaced_world_modules[namespace].include?(world_module)
101
110
  end
102
111
  end
103
112
 
104
113
  def load_code_file(code_file)
105
114
  return unless File.extname(code_file) == '.rb'
106
- 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
107
123
  end
108
124
 
109
125
  def begin_scenario(test_case)
@@ -134,7 +150,7 @@ module Cucumber
134
150
  end
135
151
 
136
152
  def hooks_for(phase, scenario) #:nodoc:
137
- hooks[phase.to_sym].select{|hook| scenario.accept_hook?(hook)}
153
+ hooks[phase.to_sym].select { |hook| scenario.accept_hook?(hook) }
138
154
  end
139
155
 
140
156
  def unmatched_step_definitions
@@ -152,11 +168,34 @@ module Cucumber
152
168
  def create_expression(string_or_regexp)
153
169
  return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
154
170
  return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
155
- raise ArgumentError.new('Expression must be a String or Regexp')
171
+ raise ArgumentError, 'Expression must be a String or Regexp'
172
+ end
173
+
174
+ def self.cli_snippet_type_options
175
+ registry = CucumberExpressions::ParameterTypeRegistry.new
176
+ cucumber_expression_generator = CucumberExpressions::CucumberExpressionGenerator.new(registry)
177
+ Snippet::SNIPPET_TYPES.keys.sort_by(&:to_s).map do |type|
178
+ Snippet::SNIPPET_TYPES[type].cli_option_string(type, cucumber_expression_generator)
179
+ end
156
180
  end
157
181
 
158
182
  private
159
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
+
160
199
  def available_step_definition_hash
161
200
  @available_step_definition_hash ||= {}
162
201
  end
@@ -166,20 +205,12 @@ module Cucumber
166
205
  end
167
206
 
168
207
  def hooks
169
- @hooks ||= Hash.new{|h,k| h[k] = []}
170
- end
171
-
172
- def self.cli_snippet_type_options
173
- registry = CucumberExpressions::ParameterTypeRegistry.new
174
- cucumber_expression_generator = CucumberExpressions::CucumberExpressionGenerator.new(registry)
175
- Snippet::SNIPPET_TYPES.keys.sort_by(&:to_s).map do |type|
176
- Snippet::SNIPPET_TYPES[type].cli_option_string(type, cucumber_expression_generator)
177
- end
208
+ @hooks ||= Hash.new { |h, k| h[k] = [] }
178
209
  end
179
210
  end
180
211
 
181
212
  def self.backtrace_line(proc, name)
182
- location = Cucumber::Core::Ast::Location.from_source_location(*proc.source_location)
213
+ location = Cucumber::Core::Test::Location.from_source_location(*proc.source_location)
183
214
  "#{location}:in `#{name}'"
184
215
  end
185
216
  end