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
@@ -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,21 +4,6 @@ require 'cucumber/step_definition_light'
4
4
  module Cucumber
5
5
  module LanguageSupport
6
6
  module LanguageMethods
7
- def around(scenario)
8
- execute_around(scenario) do
9
- yield
10
- end
11
- end
12
-
13
- def before(scenario)
14
- begin_scenario(scenario)
15
- execute_before(scenario)
16
- end
17
-
18
- def after(scenario)
19
- execute_after(scenario)
20
- end_scenario
21
- end
22
7
 
23
8
  def after_configuration(configuration)
24
9
  hooks[:after_configuration].each do |hook|
@@ -26,12 +11,6 @@ module Cucumber
26
11
  end
27
12
  end
28
13
 
29
- def execute_after_step(scenario)
30
- hooks_for(:after_step, scenario).each do |hook|
31
- invoke(hook, 'AfterStep', scenario)
32
- end
33
- end
34
-
35
14
  def execute_transforms(args)
36
15
  args.map do |arg|
37
16
  matching_transform = transforms.detect {|transform| transform.match(arg) }
@@ -87,39 +66,6 @@ module Cucumber
87
66
  @transforms ||= []
88
67
  end
89
68
 
90
- def execute_around(scenario, &block)
91
- hooks_for(:around, scenario).reverse.inject(block) do |blk, hook|
92
- proc do
93
- invoke(hook, 'Around', scenario, true) do
94
- blk.call(scenario)
95
- end
96
- end
97
- end.call
98
- end
99
-
100
- def execute_before(scenario)
101
- hooks_for(:before, scenario).each do |hook|
102
- invoke(hook, 'Before', scenario, true)
103
- end
104
- end
105
-
106
- def execute_after(scenario)
107
- hooks_for(:after, scenario).reverse_each do |hook|
108
- invoke(hook, 'After', scenario, true)
109
- end
110
- end
111
-
112
- def invoke(hook, location, scenario, exception_fails_scenario, &block)
113
- begin
114
- hook.invoke(location, scenario, &block)
115
- rescue Exception => exception
116
- if exception_fails_scenario
117
- scenario.fail!(exception)
118
- else
119
- raise
120
- end
121
- end
122
- end
123
69
  end
124
70
  end
125
71
  end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'gherkin/formatter/escaping'
2
3
  require 'cucumber/core/ast/describes_itself'
3
4
 
@@ -46,34 +47,50 @@ module Cucumber
46
47
 
47
48
  include Enumerable
48
49
  include Core::Ast::DescribesItself
50
+ extend Forwardable
49
51
 
50
52
  NULL_CONVERSIONS = Hash.new({ :strict => false, :proc => lambda{ |cell_value| cell_value } }).freeze
51
53
 
52
54
  attr_accessor :file
55
+ def_delegator :@ast_table, :location
53
56
 
54
57
  def self.default_arg_name #:nodoc:
55
58
  "table"
56
59
  end
57
60
 
58
- def self.parse(text, uri, offset)
59
- builder = Builder.new
60
- lexer = Gherkin::Lexer::I18nLexer.new(builder)
61
- lexer.scan(text)
62
- new(builder.rows)
61
+ class << self
62
+ def from(data, location = Core::Ast::Location.of_caller)
63
+ case data
64
+ when Array
65
+ from_array(data, location)
66
+ when String
67
+ parse(data, location)
68
+ else
69
+ raise ArgumentError, "expected data to be a String or an Array."
70
+ end
71
+ end
72
+
73
+ private
74
+ def parse(text, location = Core::Ast::Location.of_caller)
75
+ builder = Builder.new
76
+ lexer = Gherkin::Lexer::I18nLexer.new(builder)
77
+ lexer.scan(text)
78
+ from_array(builder.rows, location)
79
+ end
80
+
81
+ def from_array(data, location = Core::Ast::Location.of_caller)
82
+ new Core::Ast::DataTable.new(data, location)
83
+ end
63
84
  end
64
85
 
65
- # Creates a new instance. +raw+ should be an Array of Array of String
66
- # or an Array of Hash (similar to what #hashes returns).
67
- # You don't typically create your own Table objects - Cucumber will do
68
- # it internally and pass them to your Step Definitions.
69
- #
86
+
87
+ # @param data [Core::Ast::DataTable] the data for the table
88
+ # @param conversion_procs [Hash] see map_columns!
89
+ # @param header_mappings [Hash] see map_headers!
90
+ # @param header_conversion_proc [Proc] see map_headers!
70
91
  def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)
71
- ast_table = case data
72
- when Core::Ast::DataTable
73
- data
74
- when Array
75
- Core::Ast::DataTable.new(data, Core::Ast::Location.of_caller)
76
- end
92
+ raise ArgumentError, "data must be a Core::Ast::DataTable" unless data.kind_of? Core::Ast::DataTable
93
+ ast_table = data
77
94
  # Verify that it's square
78
95
  ast_table.transpose
79
96
  @cell_matrix = create_cell_matrix(ast_table)
@@ -95,7 +112,7 @@ module Cucumber
95
112
  # registered with #map_column! and #map_headers!.
96
113
  #
97
114
  def dup
98
- self.class.new(raw.dup, @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
115
+ self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
99
116
  end
100
117
 
101
118
  # Returns a new, transposed table. Example:
@@ -110,7 +127,7 @@ module Cucumber
110
127
  # | 4 | 2 |
111
128
  #
112
129
  def transpose
113
- self.class.new(raw.transpose, @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
130
+ self.class.new(Core::Ast::DataTable.new(raw.transpose, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
114
131
  end
115
132
 
116
133
  # Converts this table into an Array of Hash where the keys of each
@@ -237,7 +254,7 @@ module Cucumber
237
254
 
238
255
  # Returns a new Table where the headers are redefined. See #map_headers!
239
256
  def map_headers(mappings={}, &block)
240
- self.class.new raw.dup, @conversion_procs.dup, mappings, block
257
+ self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, mappings, block)
241
258
  end
242
259
 
243
260
  # Change how #hashes converts column values. The +column_name+ argument identifies the column
@@ -262,7 +279,7 @@ module Cucumber
262
279
  def map_column(column_name, strict=true, &conversion_proc)
263
280
  conversion_procs = @conversion_procs.dup
264
281
  conversion_procs[column_name.to_s] = { :strict => strict, :proc => conversion_proc }
265
- self.class.new(raw.dup, conversion_procs, @header_mappings.dup, @header_conversion_proc)
282
+ self.class.new(Core::Ast::DataTable.new(raw, location), conversion_procs, @header_mappings.dup, @header_conversion_proc)
266
283
  end
267
284
 
268
285
  # Compares +other_table+ to self. If +other_table+ contains columns
@@ -427,8 +444,9 @@ module Cucumber
427
444
  end
428
445
 
429
446
  def to_s(options = {}) #:nodoc:
447
+ # TODO: factor out this code so we don't depend on such a big lump of old cruft
430
448
  require 'cucumber/formatter/pretty'
431
- require 'cucumber/reports/legacy_formatter'
449
+ require 'cucumber/formatter/legacy_api/adapter'
432
450
  options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
433
451
  io = StringIO.new
434
452
 
@@ -436,19 +454,13 @@ module Cucumber
436
454
  Cucumber::Term::ANSIColor.coloring = options[:color]
437
455
  formatter = Formatter::Pretty.new(nil, io, options)
438
456
  formatter.instance_variable_set('@indent', options[:indent])
439
-
440
- Reports::Legacy::Ast::MultilineArg.for(self).accept(Reports::FormatterWrapper.new([formatter]))
441
-
457
+ Formatter::LegacyApi::Ast::MultilineArg.for(self).accept(Formatter::Fanout.new([formatter]))
442
458
  Cucumber::Term::ANSIColor.coloring = c
443
459
  io.rewind
444
460
  s = "\n" + io.read + (" " * (options[:indent] - 2))
445
461
  s
446
462
  end
447
463
 
448
- def location
449
- @ast_table.location
450
- end
451
-
452
464
  def description_for_visitors
453
465
  :legacy_table
454
466
  end
@@ -586,7 +598,7 @@ module Cucumber
586
598
 
587
599
  def ensure_table(table_or_array) #:nodoc:
588
600
  return table_or_array if DataTable === table_or_array
589
- DataTable.new(table_or_array)
601
+ DataTable.from(table_or_array)
590
602
  end
591
603
 
592
604
  def ensure_array_of_array(array)
@@ -4,16 +4,16 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.0.0.beta.3'
7
+ VERSION = '2.0.0'
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'])
16
+ RUBY_2_2 = RUBY_VERSION =~ /^2\.2/
17
17
  RUBY_2_1 = RUBY_VERSION =~ /^2\.1/
18
18
  RUBY_2_0 = RUBY_VERSION =~ /^2\.0/
19
19
  RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
@@ -0,0 +1,43 @@
1
+ module Cucumber
2
+
3
+ # Generates generic file structure for a cucumber project
4
+ class ProjectInitializer
5
+ def run
6
+ create_directory('features')
7
+ create_directory('features/step_definitions')
8
+ create_directory('features/support')
9
+ create_file('features/support/env.rb')
10
+ end
11
+
12
+ private
13
+
14
+ def create_directory(dir_name)
15
+ create_directory_or_file dir_name, true
16
+ end
17
+
18
+ def create_file(file_name)
19
+ create_directory_or_file file_name, false
20
+ end
21
+
22
+ def create_directory_or_file(file_name, directory)
23
+ file_type = if directory
24
+ :mkdir_p
25
+ else
26
+ :touch
27
+ end
28
+
29
+ report_exists(file_name) || return if File.exists?(file_name)
30
+
31
+ report_creating(file_name)
32
+ FileUtils.send file_type, file_name
33
+ end
34
+
35
+ def report_exists(file)
36
+ puts " exist #{file}"
37
+ end
38
+
39
+ def report_creating(file)
40
+ puts " create #{file}"
41
+ end
42
+ end
43
+ end
@@ -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
@@ -44,10 +44,19 @@ module Cucumber
44
44
  raise ArgumentError unless proc_or_sym.is_a?(Symbol)
45
45
  message = proc_or_sym
46
46
  target_proc = parse_target_proc_from(options)
47
- lambda do |*args|
47
+ patch_location_onto lambda { |*args|
48
48
  target = instance_exec(&target_proc)
49
49
  target.send(message, *args)
50
- end
50
+ }
51
+ end
52
+
53
+ def patch_location_onto(block)
54
+ file, line = caller[5].split(':')[0..1]
55
+ location = Core::Ast::Location.new(
56
+ Pathname.new(file).relative_path_from(Pathname.new(Dir.pwd)),
57
+ line)
58
+ block.define_singleton_method(:file_colon_line) { location.to_s }
59
+ block
51
60
  end
52
61
 
53
62
  def parse_target_proc_from(options)
@@ -39,7 +39,9 @@ module Cucumber
39
39
 
40
40
  private
41
41
  def convert_captures(regexp_source)
42
- regexp_source.gsub(/(\()(?!\?:)/,'(?:')
42
+ regexp_source
43
+ .gsub(/(\()(?!\?[<:=!])/,'(?:')
44
+ .gsub(/(\(\?<)(?![=!])/,'(?:<')
43
45
  end
44
46
 
45
47
  def strip_captures(regexp_source)
@@ -37,7 +37,7 @@ module Cucumber
37
37
  # @param [String,Cucumber::Ast::DocString,Cucumber::Ast::Table] multiline_argument
38
38
  def step(name, raw_multiline_arg=nil)
39
39
  location = Core::Ast::Location.of_caller
40
- @__cucumber_runtime.invoke(name, MultilineArgument.from(raw_multiline_arg, location))
40
+ @__cucumber_runtime.invoke_dynamic_step(name, MultilineArgument.from(raw_multiline_arg, location))
41
41
  end
42
42
 
43
43
  # Run a snippet of Gherkin
@@ -48,7 +48,7 @@ module Cucumber
48
48
  # }
49
49
  # @param [String] steps_text The Gherkin snippet to run
50
50
  def steps(steps_text)
51
- @__cucumber_runtime.invoke_steps(steps_text, @__natural_language, caller[0])
51
+ @__cucumber_runtime.invoke_dynamic_steps(steps_text, @__natural_language, caller[0])
52
52
  end
53
53
 
54
54
  # Parse Gherkin into a {Cucumber::Ast::Table} object.
@@ -2,7 +2,7 @@ module Cucumber
2
2
  module RbSupport
3
3
  module Snippet
4
4
 
5
- ARGUMENT_PATTERNS = ['"(.*?)"', '(\d+)']
5
+ ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
6
6
 
7
7
  class BaseSnippet
8
8
 
@@ -3,7 +3,7 @@ require 'rspec/mocks'
3
3
  World(RSpec::Mocks::ExampleMethods)
4
4
 
5
5
  Before do
6
- if RSpec::Mocks::Version::STRING.split('.').first.to_i > 2
6
+ if RSpec::Mocks::Version::STRING >= "2.9.9"
7
7
  RSpec::Mocks.setup
8
8
  else
9
9
  RSpec::Mocks.setup(self)
@@ -0,0 +1,115 @@
1
+ require 'delegate'
2
+
3
+ module Cucumber
4
+ # Represents the current status of a running test case.
5
+ #
6
+ # This wraps a `Cucumber::Core::Test::Case` and delegates
7
+ # many methods to that object.
8
+ #
9
+ # We decorete the core object with the current result.
10
+ # In the first Before hook of a scenario, this will be an
11
+ # instance of `Cucumber::Core::Test::Result::Unknown`
12
+ # but as the scenario runs, it will be updated to reflect
13
+ # the passed / failed / undefined / skipped status of
14
+ # the test case.
15
+ #
16
+ # The test case might come from a regular Scenario or
17
+ # a Scenario outline. You can call the `#outline?`
18
+ # predicate to find out. If it's from an outline,
19
+ # you get a couple of extra methods.
20
+ module RunningTestCase
21
+ def self.new(test_case)
22
+ Builder.new(test_case).result
23
+ end
24
+
25
+ class Builder
26
+ def initialize(test_case)
27
+ @test_case = test_case
28
+ test_case.describe_source_to(self)
29
+ end
30
+
31
+ def feature(feature)
32
+ end
33
+
34
+ def scenario(scenario)
35
+ @factory = Scenario
36
+ end
37
+
38
+ def scenario_outline(scenario)
39
+ @factory = ScenarioOutlineExample
40
+ end
41
+
42
+ def examples_table(examples_table)
43
+ end
44
+
45
+ def examples_table_row(row)
46
+ end
47
+
48
+ def result
49
+ @factory.new(@test_case)
50
+ end
51
+ end
52
+ private_constant :Builder
53
+
54
+ class Scenario < SimpleDelegator
55
+ def initialize(test_case, result = Core::Test::Result::Unknown.new)
56
+ @test_case = test_case
57
+ @result = result
58
+ super test_case
59
+ end
60
+
61
+ def accept_hook?(hook)
62
+ hook.tag_expressions.all? { |expression| @test_case.match_tags?(expression) }
63
+ end
64
+
65
+ def failed?
66
+ @result.failed?
67
+ end
68
+
69
+ def passed?
70
+ !failed?
71
+ end
72
+
73
+ def title
74
+ warn("deprecated: call #name instead")
75
+ name
76
+ end
77
+
78
+ def source_tags
79
+ #warn('deprecated: call #tags instead')
80
+ tags
81
+ end
82
+
83
+ def source_tag_names
84
+ tags.map &:name
85
+ end
86
+
87
+ def skip_invoke!
88
+ Cucumber.deprecate(self.class.name, __method__, "Call #skip_this_scenario on the World directly")
89
+ raise Cucumber::Core::Test::Result::Skipped
90
+ end
91
+
92
+ def outline?
93
+ false
94
+ end
95
+
96
+ def with_result(result)
97
+ self.class.new(@test_case, result)
98
+ end
99
+ end
100
+
101
+ class ScenarioOutlineExample < Scenario
102
+ def outline?
103
+ true
104
+ end
105
+
106
+ def scenario_outline
107
+ self
108
+ end
109
+
110
+ def cell_values
111
+ source.last.values
112
+ end
113
+ end
114
+ end
115
+ end
@@ -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).reverse
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
@@ -23,8 +23,8 @@ module Cucumber
23
23
  :step_match
24
24
 
25
25
  def_delegators :@support_code,
26
- :invoke_steps,
27
- :invoke,
26
+ :invoke_dynamic_steps,
27
+ :invoke_dynamic_step,
28
28
  :load_programming_language
29
29
 
30
30
  # Returns a Cucumber::MultilineArgument::DataTable for +text_or_table+, which can either
@@ -44,17 +44,13 @@ module Cucumber
44
44
  # %w{ CUC-101 Peeler 22 }
45
45
  # ])
46
46
  #
47
- def table(text_or_table, file=nil, line_offset=0)
47
+ def table(text_or_table, file=nil, line=0)
48
48
  if !file
49
49
  location = Core::Ast::Location.of_caller
50
50
  else
51
51
  location = Core::Ast::Location.new(file, line)
52
52
  end
53
- if Array === text_or_table
54
- MultilineArgument::DataTable.new(text_or_table, location)
55
- else
56
- MultilineArgument::DataTable.parse(text_or_table, file, location)
57
- end
53
+ MultilineArgument::DataTable.from(text_or_table, location)
58
54
  end
59
55
 
60
56
  # Returns a Cucumber::MultilineArgument::DocString for +body+.
@@ -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