cucumber 2.0.0.beta.5 → 2.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -1
  3. data/Rakefile +0 -2
  4. data/cucumber.gemspec +1 -1
  5. data/features/docs/defining_steps/skip_scenario.feature +31 -2
  6. data/lib/cucumber.rb +6 -0
  7. data/lib/cucumber/ast/facade.rb +117 -0
  8. data/lib/cucumber/cli/configuration.rb +1 -1
  9. data/lib/cucumber/cli/profile_loader.rb +1 -1
  10. data/lib/cucumber/file_specs.rb +1 -1
  11. data/lib/cucumber/filters.rb +9 -0
  12. data/lib/cucumber/filters/activate_steps.rb +34 -0
  13. data/lib/cucumber/filters/apply_after_hooks.rb +9 -0
  14. data/lib/cucumber/filters/apply_after_step_hooks.rb +12 -0
  15. data/lib/cucumber/filters/apply_around_hooks.rb +12 -0
  16. data/lib/cucumber/filters/apply_before_hooks.rb +9 -0
  17. data/lib/cucumber/filters/prepare_world.rb +37 -0
  18. data/lib/cucumber/filters/quit.rb +5 -1
  19. data/lib/cucumber/filters/randomizer.rb +5 -1
  20. data/lib/cucumber/filters/tag_limits.rb +7 -2
  21. data/lib/cucumber/formatter/ansicolor.rb +0 -8
  22. data/lib/cucumber/formatter/html.rb +6 -1
  23. data/lib/cucumber/formatter/legacy_api/adapter.rb +17 -1
  24. data/lib/cucumber/formatter/legacy_api/ast.rb +6 -1
  25. data/lib/cucumber/hooks.rb +97 -0
  26. data/lib/cucumber/platform.rb +2 -3
  27. data/lib/cucumber/rb_support/rb_hook.rb +2 -2
  28. data/lib/cucumber/runtime.rb +18 -15
  29. data/lib/cucumber/runtime/after_hooks.rb +24 -0
  30. data/lib/cucumber/runtime/before_hooks.rb +23 -0
  31. data/lib/cucumber/runtime/step_hooks.rb +22 -0
  32. data/lib/cucumber/runtime/support_code.rb +56 -1
  33. data/lib/cucumber/step_match.rb +26 -2
  34. data/spec/cucumber/cli/configuration_spec.rb +16 -1
  35. data/spec/cucumber/cli/profile_loader_spec.rb +10 -0
  36. data/spec/cucumber/file_specs_spec.rb +10 -2
  37. data/spec/cucumber/filters/activate_steps_spec.rb +57 -0
  38. data/spec/cucumber/formatter/debug_spec.rb +0 -14
  39. data/spec/cucumber/formatter/html_spec.rb +29 -0
  40. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +210 -110
  41. data/spec/cucumber/formatter/pretty_spec.rb +0 -2
  42. data/spec/cucumber/formatter/rerun_spec.rb +17 -16
  43. data/spec/cucumber/formatter/spec_helper.rb +11 -6
  44. data/spec/cucumber/hooks_spec.rb +30 -0
  45. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +11 -4
  46. metadata +22 -16
  47. data/gem_tasks/yard.rake +0 -43
  48. data/gem_tasks/yard/default/layout/html/bubble_32x32.png +0 -0
  49. data/gem_tasks/yard/default/layout/html/footer.erb +0 -5
  50. data/gem_tasks/yard/default/layout/html/index.erb +0 -1
  51. data/gem_tasks/yard/default/layout/html/layout.erb +0 -25
  52. data/gem_tasks/yard/default/layout/html/logo.erb +0 -1
  53. data/gem_tasks/yard/default/layout/html/setup.rb +0 -4
  54. data/lib/cucumber/mappings.rb +0 -238
  55. data/spec/cucumber/mappings_spec.rb +0 -180
@@ -1,14 +1,6 @@
1
1
  require 'cucumber/platform'
2
2
  require 'cucumber/term/ansicolor'
3
3
 
4
- if Cucumber::IRONRUBY
5
- begin
6
- require 'iron-term-ansicolor'
7
- rescue LoadError
8
- STDERR.puts %{*** WARNING: You must "igem install iron-term-ansicolor" to get coloured ouput in on IronRuby}
9
- end
10
- end
11
-
12
4
  if Cucumber::WINDOWS_MRI
13
5
  unless ENV['ANSICON']
14
6
  STDERR.puts %{*** WARNING: You must use ANSICON 1.31 or higher (https://github.com/adoxa/ansicon/) to get coloured output on Windows}
@@ -2,6 +2,7 @@ require 'erb'
2
2
  require 'builder'
3
3
  require 'cucumber/formatter/duration'
4
4
  require 'cucumber/formatter/io'
5
+ require 'pathname'
5
6
 
6
7
  module Cucumber
7
8
  module Formatter
@@ -50,6 +51,10 @@ module Cucumber
50
51
  def embed_image(src, label)
51
52
  id = "img_#{@img_id}"
52
53
  @img_id += 1
54
+ if @io.respond_to?(:path) and File.file?(src)
55
+ out_dir = Pathname.new(File.dirname(File.absolute_path(@io.path)))
56
+ src = Pathname.new(File.absolute_path(src)).relative_path_from(out_dir)
57
+ end
53
58
  @builder.span(:class => 'embed') do |pre|
54
59
  pre << %{<a href="" onclick="img=document.getElementById('#{id}'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false">#{label}</a><br>&nbsp;
55
60
  <img id="#{id}" style="display: none" src="#{src}"/>}
@@ -562,7 +567,7 @@ module Cucumber
562
567
  end
563
568
 
564
569
  def backtrace_line(line)
565
- line.gsub(/\A([^:]*\.(?:rb|feature|haml)):(\d*).*\z/) do
570
+ line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*).*$/) do
566
571
  if ENV['TM_PROJECT_DIRECTORY']
567
572
  "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
568
573
  else
@@ -238,6 +238,7 @@ module Cucumber
238
238
  end
239
239
 
240
240
  def after_step_hook(hook, result)
241
+ p current_test_step_source if current_test_step_source.step.nil?
241
242
  line = StepBacktraceLine.new(current_test_step_source.step)
242
243
  @child.after_step_hook Ast::HookResult.new(LegacyResultBuilder.new(result).
243
244
  append_to_exception_backtrace(line), @delayed_messages, @delayed_embeddings)
@@ -398,7 +399,11 @@ module Cucumber
398
399
 
399
400
  # Basic printer used by default
400
401
  class AfterHookPrinter
401
- include Cucumber.initializer(:formatter)
402
+ attr_reader :formatter
403
+
404
+ def initialize(formatter)
405
+ @formatter = formatter
406
+ end
402
407
 
403
408
  include PrintsAfterHooks
404
409
 
@@ -409,6 +414,12 @@ module Cucumber
409
414
 
410
415
  BackgroundPrinter = Struct.new(:formatter, :node, :before_hook_results) do
411
416
 
417
+ def after_test_case(*)
418
+ end
419
+
420
+ def after_hook(*)
421
+ end
422
+
412
423
  def before
413
424
  formatter.before_background node
414
425
  formatter.background_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
@@ -965,6 +976,11 @@ module Cucumber
965
976
  end
966
977
 
967
978
  class StepBacktraceLine < Struct.new(:step)
979
+ def initialize(step)
980
+ raise ArgumentError if step.nil?
981
+ super
982
+ end
983
+
968
984
  def to_s
969
985
  step.backtrace_line
970
986
  end
@@ -118,7 +118,12 @@ module Cucumber
118
118
  end
119
119
 
120
120
  def step_result_attributes
121
- [keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line]
121
+ legacy_multiline_arg = if multiline_arg.kind_of?(Core::Ast::EmptyMultilineArgument)
122
+ nil
123
+ else
124
+ step.multiline_arg
125
+ end
126
+ [keyword, step_match, legacy_multiline_arg, status, exception, source_indent, background, file_colon_line]
122
127
  end
123
128
 
124
129
  def failed?
@@ -0,0 +1,97 @@
1
+ require 'cucumber/core/test/around_hook'
2
+
3
+ module Cucumber
4
+
5
+ # Hooks quack enough like `Cucumber::Core::Ast` source nodes that we can use them as
6
+ # source for test steps
7
+ module Hooks
8
+
9
+ class << self
10
+ def before_hook(source, &block)
11
+ build_hook_step(source, block, BeforeHook, Core::Test::UnskippableAction)
12
+ end
13
+
14
+ def after_hook(source, &block)
15
+ build_hook_step(source, block, AfterHook, Core::Test::UnskippableAction)
16
+ end
17
+
18
+ def after_step_hook(source, &block)
19
+ raise ArgumentError unless source.last.kind_of?(Core::Ast::Step)
20
+ build_hook_step(source, block, AfterStepHook, Core::Test::Action)
21
+ end
22
+
23
+ def around_hook(source, &block)
24
+ Core::Test::AroundHook.new(&block)
25
+ end
26
+
27
+ private
28
+
29
+ def build_hook_step(source, block, hook_type, action_type)
30
+ action = action_type.new(&block)
31
+ hook = hook_type.new(action.location)
32
+ Core::Test::Step.new(source + [hook], action)
33
+ end
34
+ end
35
+
36
+ class AfterHook
37
+ attr_reader :location
38
+
39
+ def initialize(location)
40
+ @location = location
41
+ end
42
+
43
+ def name
44
+ "After hook"
45
+ end
46
+
47
+ def match_locations?(queried_locations)
48
+ queried_locations.any? { |other_location| other_location.match?(location) }
49
+ end
50
+
51
+ def describe_to(visitor, *args)
52
+ visitor.after_hook(self, *args)
53
+ end
54
+ end
55
+
56
+ class BeforeHook
57
+ attr_reader :location
58
+
59
+ def initialize(location)
60
+ @location = location
61
+ end
62
+
63
+ def name
64
+ "Before hook"
65
+ end
66
+
67
+ def match_locations?(queried_locations)
68
+ queried_locations.any? { |other_location| other_location.match?(location) }
69
+ end
70
+
71
+ def describe_to(visitor, *args)
72
+ visitor.before_hook(self, *args)
73
+ end
74
+ end
75
+
76
+ class AfterStepHook
77
+ attr_reader :location
78
+
79
+ def initialize(location)
80
+ @location = location
81
+ end
82
+
83
+ def name
84
+ "AfterStep hook"
85
+ end
86
+
87
+ def match_locations?(queried_locations)
88
+ queried_locations.any? { |other_location| other_location.match?(location) }
89
+ end
90
+
91
+ def describe_to(visitor, *args)
92
+ visitor.after_step_hook(self, *args)
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -4,14 +4,13 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.0.0.beta.5'
7
+ VERSION = '2.0.0.rc.1'
8
8
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
9
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
10
  JRUBY = defined?(JRUBY_VERSION)
11
- IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
12
11
  WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
13
12
  OS_X = RbConfig::CONFIG['host_os'] =~ /darwin/
14
- WINDOWS_MRI = WINDOWS && !JRUBY && !IRONRUBY
13
+ WINDOWS_MRI = WINDOWS && !JRUBY
15
14
  RAILS = defined?(Rails)
16
15
  RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
17
16
  RUBY_2_1 = RUBY_VERSION =~ /^2\.1/
@@ -10,8 +10,8 @@ module Cucumber
10
10
  @proc = proc
11
11
  end
12
12
 
13
- def invoke(location, argument, &block)
14
- @rb_language.current_world.cucumber_instance_exec(false, location, *[argument, block].compact, &@proc)
13
+ def invoke(pseudo_method, arguments, &block)
14
+ @rb_language.current_world.cucumber_instance_exec(false, pseudo_method, *[arguments, block].compact, &@proc)
15
15
  end
16
16
  end
17
17
  end
@@ -9,9 +9,7 @@ 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/quit'
13
- require 'cucumber/filters/randomizer'
14
- require 'cucumber/filters/tag_limits'
12
+ require 'cucumber/filters'
15
13
  require 'cucumber/formatter/fanout'
16
14
 
17
15
  module Cucumber
@@ -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
@@ -173,11 +172,6 @@ module Cucumber
173
172
  end
174
173
  end
175
174
 
176
- require 'cucumber/mappings'
177
- def mappings
178
- @mappings = Mappings.for(self)
179
- end
180
-
181
175
  require 'cucumber/formatter/legacy_api/adapter'
182
176
  require 'cucumber/formatter/legacy_api/runtime_facade'
183
177
  require 'cucumber/formatter/legacy_api/results'
@@ -218,12 +212,21 @@ module Cucumber
218
212
  name_regexps = @configuration.name_regexps
219
213
  tag_limits = @configuration.tag_limits
220
214
  [].tap do |filters|
221
- filters << [Filters::Randomizer, [@configuration.seed]] if @configuration.randomize?
222
- filters << [Filters::TagLimits, [tag_limits]] if tag_limits.any?
223
- filters << [Cucumber::Core::Test::TagFilter, [tag_expressions]]
224
- filters << [Cucumber::Core::Test::NameFilter, [name_regexps]]
225
- filters << [Cucumber::Core::Test::LocationsFilter, [filespecs.locations]]
226
- filters << [Filters::Quit, []]
215
+ filters << Filters::Randomizer.new(@configuration.seed) if @configuration.randomize?
216
+ filters << Filters::TagLimits.new(tag_limits) if tag_limits.any?
217
+ filters << Cucumber::Core::Test::TagFilter.new(tag_expressions)
218
+ filters << Cucumber::Core::Test::NameFilter.new(name_regexps)
219
+ filters << Cucumber::Core::Test::LocationsFilter.new(filespecs.locations)
220
+ filters << Filters::Quit.new
221
+ filters << Filters::ActivateSteps.new(@support_code)
222
+ unless configuration.dry_run?
223
+ filters << Filters::ApplyAfterStepHooks.new(@support_code)
224
+ filters << Filters::ApplyBeforeHooks.new(@support_code)
225
+ filters << Filters::ApplyAfterHooks.new(@support_code)
226
+ filters << Filters::ApplyAroundHooks.new(@support_code)
227
+ # need to do this last so it becomes the first test step
228
+ filters << Filters::PrepareWorld.new(self)
229
+ end
227
230
  end
228
231
  end
229
232
 
@@ -0,0 +1,24 @@
1
+ module Cucumber
2
+ class Runtime
3
+ class AfterHooks
4
+ def initialize(action_blocks)
5
+ @action_blocks = action_blocks
6
+ end
7
+
8
+ def apply_to(test_case)
9
+ test_case.with_steps(
10
+ test_case.test_steps + after_hooks(test_case.source)
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ def after_hooks(source)
17
+ @action_blocks.map do |action_block|
18
+ Hooks.after_hook(source, &action_block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,23 @@
1
+ module Cucumber
2
+ class Runtime
3
+ class BeforeHooks
4
+ def initialize(action_blocks)
5
+ @action_blocks = action_blocks
6
+ end
7
+
8
+ def apply_to(test_case)
9
+ test_case.with_steps(
10
+ before_hooks(test_case.source) + test_case.test_steps
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ def before_hooks(source)
17
+ @action_blocks.map do |action_block|
18
+ Hooks.before_hook(source, &action_block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Cucumber
2
+ class Runtime
3
+ class StepHooks
4
+ def initialize(after)
5
+ @after = after
6
+ end
7
+
8
+ def apply(test_steps)
9
+ test_steps.flat_map do |test_step|
10
+ [test_step] + after_step_hooks(test_step)
11
+ end
12
+ end
13
+
14
+ private
15
+ def after_step_hooks(test_step)
16
+ @after.map do |action_block|
17
+ Hooks.after_step_hook(test_step.source, &action_block)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -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
@@ -121,6 +123,59 @@ module Cucumber
121
123
  end.flatten
122
124
  end
123
125
 
126
+ def find_match(test_step)
127
+ begin
128
+ match = step_match(test_step.name)
129
+ rescue Cucumber::Undefined
130
+ return NoStepMatch.new(test_step.source.last, test_step.name)
131
+ end
132
+ if @configuration.dry_run?
133
+ return SkippingStepMatch.new
134
+ end
135
+ match
136
+ end
137
+
138
+ def find_after_step_hooks(test_case)
139
+ ruby = load_programming_language('rb')
140
+ scenario = Ast::Facade.new(test_case).build_scenario
141
+
142
+ action_blocks = ruby.hooks_for(:after_step, scenario).map do |hook|
143
+ ->(*args) { hook.invoke('AfterStep', args) }
144
+ end
145
+ StepHooks.new action_blocks
146
+ end
147
+
148
+ def apply_before_hooks(test_case)
149
+ ruby = load_programming_language('rb')
150
+ scenario = Ast::Facade.new(test_case).build_scenario
151
+
152
+ action_blocks = ruby.hooks_for(:before, scenario).map do |hook|
153
+ ->(result) { hook.invoke('Before', scenario.with_result(result)) }
154
+ end
155
+ BeforeHooks.new(action_blocks).apply_to(test_case)
156
+ end
157
+
158
+ def apply_after_hooks(test_case)
159
+ ruby = load_programming_language('rb')
160
+ scenario = Ast::Facade.new(test_case).build_scenario
161
+
162
+ action_blocks = ruby.hooks_for(:after, scenario).map do |hook|
163
+ ->(result) { hook.invoke('After', scenario.with_result(result)) }
164
+ end
165
+ AfterHooks.new(action_blocks).apply_to(test_case)
166
+ end
167
+
168
+ def find_around_hooks(test_case)
169
+ ruby = load_programming_language('rb')
170
+ scenario = Ast::Facade.new(test_case).build_scenario
171
+
172
+ ruby.hooks_for(:around, scenario).map do |hook|
173
+ Hooks.around_hook(test_case.source) do |run_scenario|
174
+ hook.invoke('Around', scenario, &run_scenario)
175
+ end
176
+ end
177
+ end
178
+
124
179
  def step_match(step_name, name_to_report=nil) #:nodoc:
125
180
  @match_cache ||= {}
126
181
 
@@ -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