cucumber 2.0.0.beta.3 → 2.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/History.md +131 -32
  4. data/Rakefile +0 -2
  5. data/cucumber.gemspec +4 -3
  6. data/examples/i18n/ht/Rakefile +6 -0
  7. data/examples/i18n/ht/features/adisyon.feature +17 -0
  8. data/examples/i18n/ht/features/divizyon.feature +10 -0
  9. data/examples/i18n/ht/features/step_definitions/kalkilatris_steps.rb +25 -0
  10. data/examples/i18n/ht/lib/kalkilatris.rb +14 -0
  11. data/examples/tcl/features/step_definitions/fib_steps.rb +1 -1
  12. data/features/docs/cli/dry_run.feature +48 -0
  13. data/features/docs/cli/exclude_files.feature +1 -2
  14. data/features/docs/cli/run_specific_scenarios.feature +28 -66
  15. data/features/docs/cli/strict_mode.feature +24 -1
  16. data/features/docs/defining_steps/nested_steps.feature +49 -0
  17. data/features/docs/defining_steps/skip_scenario.feature +31 -2
  18. data/features/docs/defining_steps/snippets.feature +15 -0
  19. data/features/docs/exception_in_after_step_hook.feature +1 -1
  20. data/features/docs/exception_in_around_hook.feature +80 -0
  21. data/features/docs/extending_cucumber/custom_filter.feature +29 -0
  22. data/features/docs/extending_cucumber/custom_formatter.feature +65 -7
  23. data/features/docs/formatters/debug_formatter.feature +24 -17
  24. data/features/docs/formatters/json_formatter.feature +65 -1
  25. data/features/docs/formatters/junit_formatter.feature +40 -0
  26. data/features/docs/formatters/pretty_formatter.feature +42 -0
  27. data/features/docs/formatters/rerun_formatter.feature +3 -2
  28. data/features/docs/getting_started.feature +1 -1
  29. data/features/docs/{wire_protocol_erb.feature → wire_protocol/erb_configuration.feature} +2 -2
  30. data/features/docs/wire_protocol/handle_unexpected_response.feature +30 -0
  31. data/features/docs/wire_protocol/invoke_message.feature +216 -0
  32. data/features/docs/wire_protocol/readme.md +26 -0
  33. data/features/docs/wire_protocol/snippets_message.feature +51 -0
  34. data/features/docs/wire_protocol/step_matches_message.feature +81 -0
  35. data/features/docs/{wire_protocol_table_diffing.feature → wire_protocol/table_diffing.feature} +1 -0
  36. data/features/docs/{wire_protocol_tags.feature → wire_protocol/tags.feature} +1 -0
  37. data/features/docs/{wire_protocol_timeouts.feature → wire_protocol/timeouts.feature} +1 -0
  38. data/features/docs/work_in_progress.feature +1 -1
  39. data/features/docs/writing_support_code/after_hooks.feature +24 -0
  40. data/features/docs/writing_support_code/around_hooks.feature +31 -0
  41. data/features/docs/writing_support_code/before_hook.feature +7 -3
  42. data/features/docs/writing_support_code/tagged_hooks.feature +44 -6
  43. data/features/lib/step_definitions/wire_steps.rb +18 -1
  44. data/features/lib/support/fake_wire_server.rb +10 -7
  45. data/lib/cucumber/cli/configuration.rb +6 -11
  46. data/lib/cucumber/cli/main.rb +2 -2
  47. data/lib/cucumber/cli/options.rb +8 -1
  48. data/lib/cucumber/cli/profile_loader.rb +1 -1
  49. data/lib/cucumber/core_ext/instance_exec.rb +1 -1
  50. data/lib/cucumber/encoding.rb +5 -0
  51. data/lib/cucumber/errors.rb +8 -0
  52. data/lib/cucumber/file_specs.rb +3 -1
  53. data/lib/cucumber/filters/activate_steps.rb +33 -0
  54. data/lib/cucumber/filters/apply_after_hooks.rb +9 -0
  55. data/lib/cucumber/filters/apply_after_step_hooks.rb +12 -0
  56. data/lib/cucumber/filters/apply_around_hooks.rb +12 -0
  57. data/lib/cucumber/filters/apply_before_hooks.rb +9 -0
  58. data/lib/cucumber/{runtime → filters}/gated_receiver.rb +5 -1
  59. data/lib/cucumber/filters/prepare_world.rb +45 -0
  60. data/lib/cucumber/filters/quit.rb +28 -0
  61. data/lib/cucumber/filters/randomizer.rb +40 -0
  62. data/lib/cucumber/{runtime → filters}/tag_limits/test_case_index.rb +4 -2
  63. data/lib/cucumber/{runtime → filters}/tag_limits/verifier.rb +4 -2
  64. data/lib/cucumber/filters/tag_limits.rb +45 -0
  65. data/lib/cucumber/filters.rb +9 -0
  66. data/lib/cucumber/formatter/ansicolor.rb +0 -8
  67. data/lib/cucumber/formatter/console.rb +37 -20
  68. data/lib/cucumber/formatter/debug.rb +1 -8
  69. data/lib/cucumber/formatter/fanout.rb +27 -0
  70. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +10 -9
  71. data/lib/cucumber/formatter/html.rb +31 -6
  72. data/lib/cucumber/formatter/ignore_missing_messages.rb +20 -0
  73. data/lib/cucumber/formatter/junit.rb +97 -2
  74. data/lib/cucumber/formatter/legacy_api/adapter.rb +1060 -0
  75. data/lib/cucumber/formatter/legacy_api/ast.rb +376 -0
  76. data/lib/cucumber/formatter/legacy_api/results.rb +51 -0
  77. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +30 -0
  78. data/lib/cucumber/formatter/pretty.rb +14 -0
  79. data/lib/cucumber/formatter/progress.rb +10 -0
  80. data/lib/cucumber/formatter/rerun.rb +14 -88
  81. data/lib/cucumber/hooks.rb +97 -0
  82. data/lib/cucumber/language_support/language_methods.rb +0 -54
  83. data/lib/cucumber/multiline_argument/data_table.rb +41 -29
  84. data/lib/cucumber/platform.rb +3 -3
  85. data/lib/cucumber/project_initializer.rb +43 -0
  86. data/lib/cucumber/rb_support/rb_hook.rb +2 -2
  87. data/lib/cucumber/rb_support/rb_step_definition.rb +11 -2
  88. data/lib/cucumber/rb_support/rb_transform.rb +3 -1
  89. data/lib/cucumber/rb_support/rb_world.rb +2 -2
  90. data/lib/cucumber/rb_support/snippet.rb +1 -1
  91. data/lib/cucumber/rspec/doubles.rb +1 -1
  92. data/lib/cucumber/running_test_case.rb +115 -0
  93. data/lib/cucumber/runtime/after_hooks.rb +24 -0
  94. data/lib/cucumber/runtime/before_hooks.rb +23 -0
  95. data/lib/cucumber/runtime/for_programming_languages.rb +4 -8
  96. data/lib/cucumber/runtime/step_hooks.rb +22 -0
  97. data/lib/cucumber/runtime/support_code.rb +70 -5
  98. data/lib/cucumber/runtime.rb +56 -112
  99. data/lib/cucumber/step_match.rb +26 -2
  100. data/lib/cucumber.rb +7 -3
  101. data/spec/cucumber/cli/configuration_spec.rb +16 -1
  102. data/spec/cucumber/cli/profile_loader_spec.rb +10 -0
  103. data/spec/cucumber/core_ext/instance_exec_spec.rb +4 -0
  104. data/spec/cucumber/file_specs_spec.rb +21 -2
  105. data/spec/cucumber/filters/activate_steps_spec.rb +57 -0
  106. data/spec/cucumber/{runtime → filters}/gated_receiver_spec.rb +3 -3
  107. data/spec/cucumber/{runtime → filters}/tag_limits/test_case_index_spec.rb +3 -3
  108. data/spec/cucumber/{runtime → filters}/tag_limits/verifier_spec.rb +4 -4
  109. data/spec/cucumber/{runtime/tag_limits/filter_spec.rb → filters/tag_limits_spec.rb} +6 -6
  110. data/spec/cucumber/formatter/debug_spec.rb +25 -530
  111. data/spec/cucumber/formatter/html_spec.rb +140 -0
  112. data/spec/cucumber/formatter/junit_spec.rb +212 -156
  113. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +2090 -0
  114. data/spec/cucumber/formatter/pretty_spec.rb +248 -2
  115. data/spec/cucumber/formatter/rerun_spec.rb +107 -0
  116. data/spec/cucumber/formatter/spec_helper.rb +17 -8
  117. data/spec/cucumber/hooks_spec.rb +30 -0
  118. data/spec/cucumber/multiline_argument/data_table_spec.rb +53 -47
  119. data/spec/cucumber/project_initializer_spec.rb +87 -0
  120. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  121. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +32 -7
  122. data/spec/cucumber/rb_support/rb_transform_spec.rb +20 -0
  123. data/spec/cucumber/rb_support/snippet_spec.rb +6 -6
  124. data/spec/cucumber/running_test_case_spec.rb +83 -0
  125. data/spec/cucumber/runtime_spec.rb +1 -5
  126. data/spec/spec_helper.rb +3 -4
  127. metadata +149 -107
  128. data/bin/cuke +0 -60
  129. data/features/docs/extending_cucumber/formatter_callbacks.feature +0 -370
  130. data/features/docs/output_from_hooks.feature +0 -128
  131. data/features/docs/report_called_undefined_steps.feature +0 -57
  132. data/features/docs/wire_protocol.feature +0 -337
  133. data/gem_tasks/yard/default/layout/html/bubble_32x32.png +0 -0
  134. data/gem_tasks/yard/default/layout/html/footer.erb +0 -5
  135. data/gem_tasks/yard/default/layout/html/index.erb +0 -1
  136. data/gem_tasks/yard/default/layout/html/layout.erb +0 -25
  137. data/gem_tasks/yard/default/layout/html/logo.erb +0 -1
  138. data/gem_tasks/yard/default/layout/html/setup.rb +0 -4
  139. data/gem_tasks/yard.rake +0 -43
  140. data/lib/cucumber/mappings.rb +0 -238
  141. data/lib/cucumber/reports/legacy_formatter.rb +0 -1349
  142. data/lib/cucumber/runtime/results.rb +0 -64
  143. data/lib/cucumber/runtime/tag_limits/filter.rb +0 -31
  144. data/lib/cucumber/runtime/tag_limits.rb +0 -15
  145. data/spec/cucumber/mappings_spec.rb +0 -180
  146. data/spec/cucumber/reports/legacy_formatter_spec.rb +0 -1860
  147. data/spec/cucumber/runtime/results_spec.rb +0 -88
@@ -1,5 +1,8 @@
1
1
  require 'cucumber/constantize'
2
2
  require 'cucumber/runtime/for_programming_languages'
3
+ require 'cucumber/runtime/step_hooks'
4
+ require 'cucumber/runtime/before_hooks'
5
+ require 'cucumber/runtime/after_hooks'
3
6
 
4
7
  module Cucumber
5
8
 
@@ -7,7 +10,6 @@ module Cucumber
7
10
 
8
11
  class SupportCode
9
12
 
10
- # TODO: figure out a way to move this to the core. We'd need to have access to the mappings to pass those in.
11
13
  require 'forwardable'
12
14
  class StepInvoker
13
15
  include Gherkin::Rubify
@@ -21,7 +23,7 @@ module Cucumber
21
23
 
22
24
  def step(step)
23
25
  location = Core::Ast::Location.of_caller
24
- @support_code.invoke(step.name, multiline_arg(step, location))
26
+ @support_code.invoke_dynamic_step(step.name, multiline_arg(step, location))
25
27
  end
26
28
 
27
29
  def eof
@@ -56,14 +58,24 @@ module Cucumber
56
58
  # Given I have 8 cukes in my belly
57
59
  # Then I should not be thirsty
58
60
  # })
59
- def invoke_steps(steps_text, i18n, file_colon_line)
61
+ def invoke_dynamic_steps(steps_text, i18n, file_colon_line)
60
62
  file, line = file_colon_line.split(':')
61
63
  parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps', false, i18n.iso_code)
62
64
  parser.parse(steps_text, file, line.to_i)
63
65
  end
64
66
 
65
- def invoke(step_name, multiline_argument, location=nil)
66
- step_match(step_name).invoke(multiline_argument)
67
+ # @api private
68
+ # This allows users to attempt to find, match and execute steps
69
+ # from code as the features are running, as opposed to regular
70
+ # steps which are compiled into test steps before execution.
71
+ #
72
+ # These are commonly called nested steps.
73
+ def invoke_dynamic_step(step_name, multiline_argument, location=nil)
74
+ begin
75
+ step_match(step_name).invoke(multiline_argument)
76
+ rescue Undefined => exception
77
+ raise UndefinedDynamicStep, step_name
78
+ end
67
79
  end
68
80
 
69
81
  # Loads and registers programming language implementation.
@@ -121,6 +133,59 @@ module Cucumber
121
133
  end.flatten
122
134
  end
123
135
 
136
+ def find_match(test_step)
137
+ begin
138
+ match = step_match(test_step.name)
139
+ rescue Cucumber::Undefined
140
+ return NoStepMatch.new(test_step.source.last, test_step.name)
141
+ end
142
+ if @configuration.dry_run?
143
+ return SkippingStepMatch.new
144
+ end
145
+ match
146
+ end
147
+
148
+ def find_after_step_hooks(test_case)
149
+ ruby = load_programming_language('rb')
150
+ scenario = RunningTestCase.new(test_case)
151
+
152
+ action_blocks = ruby.hooks_for(:after_step, scenario).map do |hook|
153
+ ->(*args) { hook.invoke('AfterStep', args) }
154
+ end
155
+ StepHooks.new action_blocks
156
+ end
157
+
158
+ def apply_before_hooks(test_case)
159
+ ruby = load_programming_language('rb')
160
+ scenario = RunningTestCase.new(test_case)
161
+
162
+ action_blocks = ruby.hooks_for(:before, scenario).map do |hook|
163
+ ->(result) { hook.invoke('Before', scenario.with_result(result)) }
164
+ end
165
+ BeforeHooks.new(action_blocks).apply_to(test_case)
166
+ end
167
+
168
+ def apply_after_hooks(test_case)
169
+ ruby = load_programming_language('rb')
170
+ scenario = RunningTestCase.new(test_case)
171
+
172
+ action_blocks = ruby.hooks_for(:after, scenario).map do |hook|
173
+ ->(result) { hook.invoke('After', scenario.with_result(result)) }
174
+ end
175
+ AfterHooks.new(action_blocks).apply_to(test_case)
176
+ end
177
+
178
+ def find_around_hooks(test_case)
179
+ ruby = load_programming_language('rb')
180
+ scenario = RunningTestCase.new(test_case)
181
+
182
+ ruby.hooks_for(:around, scenario).map do |hook|
183
+ Hooks.around_hook(test_case.source) do |run_scenario|
184
+ hook.invoke('Around', scenario, &run_scenario)
185
+ end
186
+ end
187
+ end
188
+
124
189
  def step_match(step_name, name_to_report=nil) #:nodoc:
125
190
  @match_cache ||= {}
126
191
 
@@ -9,6 +9,8 @@ require 'cucumber/load_path'
9
9
  require 'cucumber/language_support/language_methods'
10
10
  require 'cucumber/formatter/duration'
11
11
  require 'cucumber/file_specs'
12
+ require 'cucumber/filters'
13
+ require 'cucumber/formatter/fanout'
12
14
 
13
15
  module Cucumber
14
16
  module FixRuby21Bug9285
@@ -30,14 +32,12 @@ module Cucumber
30
32
  end
31
33
 
32
34
  class FeatureFolderNotFoundException < FileException
33
- include FixRuby21Bug9285 if Cucumber::RUBY_2_1
35
+ include FixRuby21Bug9285 if Cucumber::RUBY_2_1 || Cucumber::RUBY_2_2
34
36
  end
35
37
 
36
38
  require 'cucumber/core'
37
39
  require 'cucumber/runtime/user_interface'
38
- require 'cucumber/runtime/results'
39
40
  require 'cucumber/runtime/support_code'
40
- require 'cucumber/runtime/tag_limits'
41
41
  class Runtime
42
42
  attr_reader :results, :support_code, :configuration
43
43
 
@@ -46,17 +46,15 @@ module Cucumber
46
46
  include Runtime::UserInterface
47
47
 
48
48
  def initialize(configuration = Configuration.default)
49
- @current_scenario = nil
50
49
  @configuration = Configuration.parse(configuration)
51
50
  @support_code = SupportCode.new(self, @configuration)
52
- @results = Results.new(@configuration)
51
+ @results = Formatter::LegacyApi::Results.new
53
52
  end
54
53
 
55
- # Allows you to take an existing runtime and change it's configuration
54
+ # Allows you to take an existing runtime and change its configuration
56
55
  def configure(new_configuration)
57
56
  @configuration = Configuration.parse(new_configuration)
58
57
  @support_code.configure(@configuration)
59
- @results.configure(@configuration)
60
58
  end
61
59
 
62
60
  def load_programming_language(language)
@@ -68,7 +66,8 @@ module Cucumber
68
66
  fire_after_configuration_hook
69
67
  self.visitor = report
70
68
 
71
- execute features, mappings, report, filters
69
+ receiver = Test::Runner.new(report)
70
+ compile features, receiver, filters
72
71
  end
73
72
 
74
73
  def features_paths
@@ -79,10 +78,6 @@ module Cucumber
79
78
  @configuration.dry_run?
80
79
  end
81
80
 
82
- def step_visited(step) #:nodoc:
83
- @results.step_visited(step)
84
- end
85
-
86
81
  def scenarios(status = nil)
87
82
  @results.scenarios(status)
88
83
  end
@@ -103,53 +98,12 @@ module Cucumber
103
98
  @support_code.snippet_text(::Gherkin::I18n.code_keyword_for(step_keyword), step_name, multiline_arg)
104
99
  end
105
100
 
106
- def with_hooks(scenario, skip_hooks=false)
107
- around(scenario, skip_hooks) do
108
- before_and_after(scenario, skip_hooks) do
109
- yield scenario
110
- end
111
- end
112
- end
113
-
114
- def around(scenario, skip_hooks=false, &block) #:nodoc:
115
- if skip_hooks
116
- yield
117
- return
118
- end
119
-
120
- @support_code.around(scenario, block)
121
- end
122
-
123
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
124
- before(scenario) unless skip_hooks
125
- yield scenario
126
- after(scenario) unless skip_hooks
127
- record_result scenario
128
- end
129
-
130
- def record_result(scenario)
131
- @results.scenario_visited(scenario)
132
- end
133
-
134
101
  def begin_scenario(scenario)
135
102
  @support_code.fire_hook(:begin_scenario, scenario)
136
103
  end
137
104
 
138
- def before(scenario) #:nodoc:
139
- return if dry_run? || @current_scenario
140
- @current_scenario = scenario
141
- @support_code.fire_hook(:before, scenario)
142
- end
143
-
144
- def after(scenario) #:nodoc:
145
- @current_scenario = nil
146
- return if dry_run?
147
- @support_code.fire_hook(:after, scenario)
148
- end
149
-
150
- def after_step #:nodoc:
151
- return if dry_run?
152
- @support_code.fire_hook(:execute_after_step, @current_scenario)
105
+ def end_scenario(scenario)
106
+ @support_code.fire_hook(:end_scenario)
153
107
  end
154
108
 
155
109
  def unknown_programming_language?
@@ -159,8 +113,7 @@ module Cucumber
159
113
  # Returns Ast::DocString for +string_without_triple_quotes+.
160
114
  #
161
115
  def doc_string(string_without_triple_quotes, content_type='', line_offset=0)
162
- file, line = *caller[0].split(':')[0..1]
163
- location = Core::Ast::Location.new(file, line)
116
+ location = Core::Ast::Location.of_caller
164
117
  Core::Ast::DocString.new(string_without_triple_quotes, content_type, location)
165
118
  end
166
119
 
@@ -223,74 +176,65 @@ module Cucumber
223
176
  end
224
177
  end
225
178
 
226
- require 'cucumber/mappings'
227
- def mappings
228
- @mappings = Mappings.for(self)
179
+ require 'cucumber/formatter/legacy_api/adapter'
180
+ require 'cucumber/formatter/legacy_api/runtime_facade'
181
+ require 'cucumber/formatter/legacy_api/results'
182
+ require 'cucumber/formatter/ignore_missing_messages'
183
+ require 'cucumber/core/report/summary'
184
+ def report
185
+ @report ||= Formatter::Fanout.new([summary_report] + formatters)
229
186
  end
230
187
 
231
- require 'cucumber/reports/legacy_formatter'
232
- def report
233
- @report ||= Cucumber::Reports::LegacyFormatter.new(self, @configuration.formatters(self))
188
+ def summary_report
189
+ @summary_report ||= Core::Report::Summary.new
190
+ end
191
+
192
+ def formatters
193
+ @formatters ||= @configuration.formatter_factories { |factory, path_or_io, options|
194
+ results = Formatter::LegacyApi::Results.new
195
+ runtime_facade = Formatter::LegacyApi::RuntimeFacade.new(results, @support_code, @configuration)
196
+ formatter = factory.new(runtime_facade, path_or_io, options)
197
+ Formatter::LegacyApi::Adapter.new(
198
+ Formatter::IgnoreMissingMessages.new(formatter),
199
+ results, @support_code, @configuration)
200
+ }
234
201
  end
235
202
 
203
+ def failure?
204
+ if @configuration.wip?
205
+ summary_report.test_cases.total_passed > 0
206
+ else
207
+ summary_report.test_cases.total_failed > 0 || summary_report.test_steps.total_failed > 0 ||
208
+ (@configuration.strict? && (summary_report.test_steps.total_undefined > 0 || summary_report.test_steps.total_pending > 0))
209
+ end
210
+ end
211
+ public :failure?
212
+
236
213
  require 'cucumber/core/test/filters'
237
214
  def filters
238
215
  tag_expressions = @configuration.tag_expressions
239
216
  name_regexps = @configuration.name_regexps
240
217
  tag_limits = @configuration.tag_limits
241
218
  [].tap do |filters|
242
- filters << [Cucumber::Runtime::Randomizer, [@configuration.seed]] if @configuration.randomize?
243
- filters << [Cucumber::Runtime::TagLimits::Filter, [tag_limits]] if tag_limits.any?
244
- filters << [Cucumber::Core::Test::TagFilter, [tag_expressions]]
245
- filters << [Cucumber::Core::Test::NameFilter, [name_regexps]]
246
- filters << [Cucumber::Core::Test::LocationsFilter, [filespecs.locations]]
247
- filters << [Quit, []]
248
- end
249
- end
250
-
251
- class Randomizer
252
- def initialize(seed, receiver)
253
- @receiver = receiver
254
- @test_cases = []
255
- @seed = seed
256
- end
257
-
258
- def test_case(test_case)
259
- @test_cases << test_case
260
- end
261
-
262
- def done
263
- shuffled_test_cases.each do |test_case|
264
- test_case.describe_to(@receiver)
219
+ filters << Filters::Randomizer.new(@configuration.seed) if @configuration.randomize?
220
+ filters << Filters::TagLimits.new(tag_limits) if tag_limits.any?
221
+ filters << Cucumber::Core::Test::TagFilter.new(tag_expressions)
222
+ filters << Cucumber::Core::Test::NameFilter.new(name_regexps)
223
+ filters << Cucumber::Core::Test::LocationsFilter.new(filespecs.locations)
224
+ filters << Filters::Quit.new
225
+ filters << Filters::ActivateSteps.new(@support_code)
226
+ @configuration.filters.each do |filter|
227
+ filters << filter
265
228
  end
266
- @receiver.done
267
- end
268
-
269
- private
270
-
271
- def shuffled_test_cases
272
- @test_cases.shuffle(random: Random.new(seed))
273
- end
274
-
275
- attr_reader :seed
276
- private :seed
277
- end
278
-
279
- class Quit
280
- def initialize(receiver)
281
- @receiver = receiver
282
- end
283
-
284
- def test_case(test_case)
285
- unless Cucumber.wants_to_quit
286
- test_case.describe_to @receiver
229
+ unless configuration.dry_run?
230
+ filters << Filters::ApplyAfterStepHooks.new(@support_code)
231
+ filters << Filters::ApplyBeforeHooks.new(@support_code)
232
+ filters << Filters::ApplyAfterHooks.new(@support_code)
233
+ filters << Filters::ApplyAroundHooks.new(@support_code)
234
+ # need to do this last so it becomes the first test step
235
+ filters << Filters::PrepareWorld.new(self)
287
236
  end
288
237
  end
289
-
290
- def done
291
- @receiver.done
292
- self
293
- end
294
238
  end
295
239
 
296
240
  def load_step_definitions
@@ -1,3 +1,5 @@
1
+ require 'cucumber/multiline_argument'
2
+
1
3
  module Cucumber
2
4
  class StepMatch #:nodoc:
3
5
  attr_reader :step_definition, :step_arguments
@@ -12,15 +14,21 @@ module Cucumber
12
14
  end
13
15
 
14
16
  def args
15
- @step_arguments.map{|g| g.val.freeze }
17
+ @step_arguments.map{|g| g.val }
16
18
  end
17
19
 
18
20
  def name
19
21
  @name_to_report
20
22
  end
21
23
 
24
+ def activate(test_step)
25
+ test_step.with_action do
26
+ invoke(MultilineArgument.from_core(test_step.source.last.multiline_arg))
27
+ end
28
+ end
29
+
22
30
  def invoke(multiline_arg)
23
- all_args = args.dup
31
+ all_args = deep_clone_args
24
32
  multiline_arg.append_to(all_args)
25
33
  @step_definition.invoke(all_args)
26
34
  end
@@ -80,6 +88,17 @@ module Cucumber
80
88
  def inspect #:nodoc:
81
89
  sprintf("#<%s:0x%x>", self.class, self.object_id)
82
90
  end
91
+
92
+ private
93
+ def deep_clone_args
94
+ Marshal.load( Marshal.dump( args ) )
95
+ end
96
+ end
97
+
98
+ class SkippingStepMatch
99
+ def activate(test_step)
100
+ return test_step.with_action { raise Core::Test::Result::Skipped.new }
101
+ end
83
102
  end
84
103
 
85
104
  class NoStepMatch #:nodoc:
@@ -110,5 +129,10 @@ module Cucumber
110
129
  def step_arguments
111
130
  []
112
131
  end
132
+
133
+ def activate(test_step)
134
+ # noop
135
+ return test_step
136
+ end
113
137
  end
114
138
  end
data/lib/cucumber.rb CHANGED
@@ -1,7 +1,5 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
-
4
1
  require 'yaml'
2
+ require 'cucumber/encoding'
5
3
  require 'cucumber/platform'
6
4
  require 'cucumber/runtime'
7
5
  require 'cucumber/cli/main'
@@ -22,5 +20,11 @@ module Cucumber
22
20
  def logger=(logger)
23
21
  @log = logger
24
22
  end
23
+
24
+ def deprecate(class_name, method, message)
25
+ return self # deprecation warnings will come in v2.1
26
+ called_by = caller[1]
27
+ warn("Deprecated: #{class_name}##{method} #{message}. Caller: #{called_by}")
28
+ end
25
29
  end
26
30
  end
@@ -398,7 +398,22 @@ END_OF_MESSAGE
398
398
  expect(config.feature_files).to eq ["cucumber.feature"]
399
399
  end
400
400
 
401
- it "allows specifying environment variables on the command line" do
401
+ it "gets the feature files from the rerun file" do
402
+ allow(File).to receive(:directory?).and_return(false)
403
+ allow(File).to receive(:file?).and_return(true)
404
+ allow(IO).to receive(:read).and_return(
405
+ "cucumber.feature:1:3 cucumber space.feature:134 domain folder/cuke.feature:1 domain folder/different cuke:4:5" )
406
+
407
+ config.parse!(%w{@rerun.txt})
408
+
409
+ expect(config.feature_files).to eq [
410
+ "cucumber.feature:1:3",
411
+ "cucumber space.feature:134",
412
+ "domain folder/cuke.feature:1",
413
+ "domain folder/different cuke:4:5"]
414
+ end
415
+
416
+ it "should allow specifying environment variables on the command line" do
402
417
  config.parse!(["foo=bar"])
403
418
 
404
419
  expect(ENV["foo"]).to eq "bar"
@@ -31,6 +31,16 @@ module Cucumber
31
31
 
32
32
  expect(loader.args_from('default')).to eq ['--format','ugly','features/sync_imap_mailbox.feature:16:22']
33
33
  end
34
+
35
+ it "treats percent sign as ERB code block after YAML directive" do
36
+ yml = <<-HERE
37
+ ---
38
+ % x = '--format "pretty" features/sync_imap_mailbox.feature:16:22'
39
+ default: <%= x %>
40
+ HERE
41
+ given_cucumber_yml_defined_as yml
42
+ expect(loader.args_from('default')).to eq ['--format','pretty','features/sync_imap_mailbox.feature:16:22']
43
+ end
34
44
  end
35
45
  end
36
46
  end
@@ -0,0 +1,4 @@
1
+ require 'cucumber/core_ext/instance_exec'
2
+
3
+ describe 'cucumber_instance_exec' do
4
+ end
@@ -4,6 +4,7 @@ module Cucumber
4
4
  describe FileSpecs do
5
5
  let(:file_specs) { FileSpecs.new(["features/foo.feature:1:2:3", "features/bar.feature:4:5:6"]) }
6
6
  let(:locations) { file_specs.locations }
7
+ let(:files) { file_specs.files }
7
8
 
8
9
  it "parses locations from multiple files" do
9
10
  expect(locations.length).to eq 6
@@ -18,8 +19,6 @@ module Cucumber
18
19
  end
19
20
 
20
21
  it "parses file names from multiple file specs" do
21
- files = file_specs.files
22
-
23
22
  expect(files.length).to eq 2
24
23
  expect(files).to eq [
25
24
  "features/foo.feature",
@@ -27,6 +26,15 @@ module Cucumber
27
26
  ]
28
27
  end
29
28
 
29
+ context "when files are not unique" do
30
+ let(:file_specs) { FileSpecs.new(["features/foo.feature:4", "features/foo.feature:34"]) }
31
+
32
+ it "parses unique file names" do
33
+ expect(files.length).to eq 1
34
+ expect(files).to eq ["features/foo.feature"]
35
+ end
36
+ end
37
+
30
38
  context "when no line number is specified" do
31
39
  let(:file_specs) { FileSpecs.new(["features/foo.feature", "features/bar.feature:34"]) }
32
40
 
@@ -37,5 +45,16 @@ module Cucumber
37
45
  ]
38
46
  end
39
47
  end
48
+
49
+ context "when the same file is referenced more than once" do
50
+ let(:file_specs) { FileSpecs.new(["features/foo.feature:10", "features/foo.feature:1"]) }
51
+
52
+ it "returns locations in the order specified" do
53
+ expect(locations).to eq [
54
+ Cucumber::Core::Ast::Location.new("features/foo.feature", 10),
55
+ Cucumber::Core::Ast::Location.new("features/foo.feature", 1),
56
+ ]
57
+ end
58
+ end
40
59
  end
41
60
  end
@@ -0,0 +1,57 @@
1
+ require 'cucumber/filters/activate_steps'
2
+ require 'cucumber/core/gherkin/writer'
3
+ require 'cucumber/core'
4
+
5
+ describe Cucumber::Filters::ActivateSteps do
6
+ include Cucumber::Core::Gherkin::Writer
7
+ include Cucumber::Core
8
+
9
+ let(:step_definitions) { double(find_match: step_match) }
10
+ let(:step_match) { double(activate: activated_test_step) }
11
+ let(:activated_test_step) { double }
12
+ let(:receiver) { double.as_null_object }
13
+
14
+ context "a scenario with a single step" do
15
+ let(:doc) do
16
+ gherkin do
17
+ feature do
18
+ scenario do
19
+ step 'a passing step'
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ it "activates each step" do
26
+ expect(step_match).to receive(:activate) do |test_step|
27
+ expect(test_step.name).to eq 'a passing step'
28
+ end
29
+ compile [doc], receiver, [Cucumber::Filters::ActivateSteps.new(step_definitions)]
30
+ end
31
+ end
32
+
33
+ context "a scenario outline" do
34
+ let(:doc) do
35
+ gherkin do
36
+ feature do
37
+ scenario_outline do
38
+ step 'a <status> step'
39
+
40
+ examples do
41
+ row 'status'
42
+ row 'passing'
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ it "activates each step" do
50
+ expect(step_match).to receive(:activate) do |test_step|
51
+ expect(test_step.name).to eq 'a passing step'
52
+ end
53
+ compile [doc], receiver, [Cucumber::Filters::ActivateSteps.new(step_definitions)]
54
+ end
55
+ end
56
+
57
+ end
@@ -1,7 +1,7 @@
1
- require "cucumber/runtime/gated_receiver"
1
+ require "cucumber/filters/gated_receiver"
2
2
 
3
- describe Cucumber::Runtime::GatedReceiver do
4
- subject(:gated_receiver) { Cucumber::Runtime::GatedReceiver.new(receiver) }
3
+ describe Cucumber::Filters::GatedReceiver do
4
+ subject(:gated_receiver) { Cucumber::Filters::GatedReceiver.new(receiver) }
5
5
 
6
6
  let(:receiver) { double(:receiver) }
7
7
  let(:test_cases){ [double(:test_case), double(:test_case)] }
@@ -1,7 +1,7 @@
1
- require "cucumber/runtime/tag_limits"
1
+ require "cucumber/filters/tag_limits"
2
2
 
3
- describe Cucumber::Runtime::TagLimits::TestCaseIndex do
4
- subject(:index) { Cucumber::Runtime::TagLimits::TestCaseIndex.new }
3
+ describe Cucumber::Filters::TagLimits::TestCaseIndex do
4
+ subject(:index) { Cucumber::Filters::TagLimits::TestCaseIndex.new }
5
5
 
6
6
  let(:test_cases) do
7
7
  [
@@ -1,8 +1,8 @@
1
- require "cucumber/runtime/tag_limits"
1
+ require "cucumber/filters/tag_limits"
2
2
 
3
- describe Cucumber::Runtime::TagLimits::Verifier do
3
+ describe Cucumber::Filters::TagLimits::Verifier do
4
4
  describe "#verify!" do
5
- subject(:verifier) { Cucumber::Runtime::TagLimits::Verifier.new(tag_limits) }
5
+ subject(:verifier) { Cucumber::Filters::TagLimits::Verifier.new(tag_limits) }
6
6
  let(:test_case_index) { double(:test_case_index) }
7
7
 
8
8
  context "the tag counts exceed the tag limits" do
@@ -28,7 +28,7 @@ describe Cucumber::Runtime::TagLimits::Verifier do
28
28
  expect {
29
29
  verifier.verify!(test_case_index)
30
30
  }.to raise_error(
31
- Cucumber::Runtime::TagLimits::TagLimitExceededError,
31
+ Cucumber::Filters::TagLimitExceededError,
32
32
  "@exceed_me occurred 2 times, but the limit was set to 1\n" +
33
33
  " path/to/some.feature:3\n" +
34
34
  " path/to/some/other.feature:8"
@@ -1,7 +1,7 @@
1
- require "cucumber/runtime/tag_limits"
1
+ require "cucumber/filters/tag_limits"
2
2
 
3
- describe Cucumber::Runtime::TagLimits::Filter do
4
- subject(:filter) { Cucumber::Runtime::TagLimits::Filter.new(tag_limits, receiver) }
3
+ describe Cucumber::Filters::TagLimits do
4
+ subject(:filter) { Cucumber::Filters::TagLimits.new(tag_limits, receiver) }
5
5
 
6
6
  let(:tag_limits) { double(:tag_limits) }
7
7
  let(:receiver) { double(:receiver) }
@@ -11,8 +11,8 @@ describe Cucumber::Runtime::TagLimits::Filter do
11
11
  let(:test_case) { double(:test_case) }
12
12
 
13
13
  before do
14
- allow(Cucumber::Runtime::GatedReceiver).to receive(:new).with(receiver) { gated_receiver }
15
- allow(Cucumber::Runtime::TagLimits::TestCaseIndex).to receive(:new) { test_case_index }
14
+ allow(Cucumber::Filters::GatedReceiver).to receive(:new).with(receiver) { gated_receiver }
15
+ allow(Cucumber::Filters::TagLimits::TestCaseIndex).to receive(:new) { test_case_index }
16
16
  end
17
17
 
18
18
  describe "#test_case" do
@@ -36,7 +36,7 @@ describe Cucumber::Runtime::TagLimits::Filter do
36
36
  let(:verifier) { double(:verifier) }
37
37
 
38
38
  before do
39
- allow(Cucumber::Runtime::TagLimits::Verifier).to receive(:new).with(tag_limits) { verifier }
39
+ allow(Cucumber::Filters::TagLimits::Verifier).to receive(:new).with(tag_limits) { verifier }
40
40
  allow(gated_receiver).to receive(:done)
41
41
  end
42
42