cucumber 0.8.5 → 0.8.6

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 (181) hide show
  1. data/.rspec +1 -1
  2. data/LICENSE +1 -1
  3. data/Rakefile +5 -51
  4. data/bin/cucumber +7 -1
  5. data/cucumber.gemspec +463 -679
  6. data/examples/i18n/ar/features/step_definitons/calculator_steps.rb +1 -1
  7. data/examples/i18n/he/features/step_definitons/calculator_steps.rb +1 -1
  8. data/examples/i18n/ro/features/step_definitons/calculator_steps.rb +4 -7
  9. data/examples/i18n/ru/features/division.feature +2 -2
  10. data/examples/i18n/tr/features/step_definitons/hesap_makinesi_adimlari.rb +3 -3
  11. data/examples/sinatra/features/support/env.rb +2 -5
  12. data/examples/v8/features/fibonacci.feature +1 -1
  13. data/examples/watir/features/step_definitions/search_steps.rb +1 -1
  14. data/features/background.feature +284 -95
  15. data/features/custom_formatter.feature +3 -73
  16. data/features/json_formatter.feature +160 -245
  17. data/features/step_definitions/cucumber_steps.rb +7 -153
  18. data/features/support/env.rb +18 -140
  19. data/fixtures/junit/features/pending.feature +3 -1
  20. data/fixtures/self_test/features/support/env.rb +8 -0
  21. data/fixtures/tickets/features.html +1 -1
  22. data/gem_tasks/examples.rake +1 -1
  23. data/lib/cucumber.rb +12 -0
  24. data/lib/cucumber/ast.rb +1 -1
  25. data/lib/cucumber/ast/background.rb +21 -5
  26. data/lib/cucumber/ast/examples.rb +12 -4
  27. data/lib/cucumber/ast/feature.rb +13 -5
  28. data/lib/cucumber/ast/feature_element.rb +9 -4
  29. data/lib/cucumber/ast/outline_table.rb +4 -4
  30. data/lib/cucumber/ast/scenario.rb +7 -5
  31. data/lib/cucumber/ast/scenario_outline.rb +23 -15
  32. data/lib/cucumber/ast/step.rb +5 -0
  33. data/lib/cucumber/ast/step_invocation.rb +21 -15
  34. data/lib/cucumber/ast/table.rb +14 -8
  35. data/lib/cucumber/ast/tree_walker.rb +10 -48
  36. data/lib/cucumber/cli/configuration.rb +33 -8
  37. data/lib/cucumber/cli/main.rb +20 -35
  38. data/lib/cucumber/cli/options.rb +8 -7
  39. data/lib/cucumber/cli/profile_loader.rb +2 -0
  40. data/lib/cucumber/core_ext/proc.rb +2 -1
  41. data/lib/cucumber/feature_file.rb +47 -15
  42. data/lib/cucumber/formatter/ansicolor.rb +3 -5
  43. data/lib/cucumber/formatter/console.rb +27 -23
  44. data/lib/cucumber/formatter/cucumber.css +34 -17
  45. data/lib/cucumber/formatter/cucumber.sass +173 -182
  46. data/lib/cucumber/formatter/html.rb +46 -11
  47. data/lib/cucumber/formatter/io.rb +2 -4
  48. data/lib/cucumber/formatter/json.rb +15 -152
  49. data/lib/cucumber/formatter/json_pretty.rb +5 -6
  50. data/lib/cucumber/formatter/junit.rb +28 -22
  51. data/lib/cucumber/formatter/pdf.rb +6 -6
  52. data/lib/cucumber/formatter/pretty.rb +5 -5
  53. data/lib/cucumber/formatter/rerun.rb +22 -11
  54. data/lib/cucumber/formatter/unicode.rb +41 -20
  55. data/lib/cucumber/js_support/js_dsl.js +4 -4
  56. data/lib/cucumber/js_support/js_language.rb +9 -5
  57. data/lib/cucumber/js_support/js_snippets.rb +2 -2
  58. data/lib/cucumber/language_support.rb +2 -2
  59. data/lib/cucumber/parser/gherkin_builder.rb +35 -30
  60. data/lib/cucumber/platform.rb +8 -8
  61. data/lib/cucumber/py_support/py_language.rb +2 -2
  62. data/lib/cucumber/rake/task.rb +80 -31
  63. data/lib/cucumber/rb_support/rb_dsl.rb +1 -0
  64. data/lib/cucumber/rb_support/rb_language.rb +10 -8
  65. data/lib/cucumber/rb_support/rb_step_definition.rb +8 -0
  66. data/lib/cucumber/rb_support/rb_transform.rb +17 -0
  67. data/lib/cucumber/rb_support/rb_world.rb +26 -18
  68. data/lib/cucumber/rspec/doubles.rb +3 -3
  69. data/lib/cucumber/step_match.rb +6 -2
  70. data/lib/cucumber/step_mother.rb +6 -427
  71. data/lib/cucumber/wire_support/configuration.rb +4 -1
  72. data/lib/cucumber/wire_support/wire_language.rb +3 -10
  73. data/spec/cucumber/ast/background_spec.rb +68 -6
  74. data/spec/cucumber/ast/feature_factory.rb +5 -4
  75. data/spec/cucumber/ast/feature_spec.rb +4 -4
  76. data/spec/cucumber/ast/outline_table_spec.rb +1 -1
  77. data/spec/cucumber/ast/scenario_outline_spec.rb +15 -11
  78. data/spec/cucumber/ast/scenario_spec.rb +4 -4
  79. data/spec/cucumber/ast/step_spec.rb +3 -3
  80. data/spec/cucumber/ast/table_spec.rb +38 -2
  81. data/spec/cucumber/ast/tree_walker_spec.rb +2 -2
  82. data/spec/cucumber/broadcaster_spec.rb +1 -1
  83. data/spec/cucumber/cli/configuration_spec.rb +32 -6
  84. data/spec/cucumber/cli/drb_client_spec.rb +2 -3
  85. data/spec/cucumber/cli/main_spec.rb +43 -43
  86. data/spec/cucumber/cli/options_spec.rb +28 -1
  87. data/spec/cucumber/cli/profile_loader_spec.rb +1 -1
  88. data/spec/cucumber/core_ext/proc_spec.rb +1 -1
  89. data/spec/cucumber/formatter/ansicolor_spec.rb +1 -1
  90. data/spec/cucumber/formatter/duration_spec.rb +1 -1
  91. data/spec/cucumber/formatter/html_spec.rb +3 -5
  92. data/spec/cucumber/formatter/junit_spec.rb +16 -2
  93. data/spec/cucumber/formatter/progress_spec.rb +1 -1
  94. data/spec/cucumber/formatter/spec_helper.rb +11 -12
  95. data/spec/cucumber/rb_support/rb_language_spec.rb +241 -28
  96. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +33 -28
  97. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +1 -1
  98. data/spec/cucumber/step_match_spec.rb +11 -9
  99. data/spec/cucumber/wire_support/configuration_spec.rb +1 -1
  100. data/spec/cucumber/wire_support/connection_spec.rb +1 -1
  101. data/spec/cucumber/wire_support/wire_exception_spec.rb +1 -1
  102. data/spec/cucumber/wire_support/wire_language_spec.rb +1 -1
  103. data/spec/cucumber/wire_support/wire_packet_spec.rb +1 -1
  104. data/spec/cucumber/wire_support/wire_step_definition_spec.rb +1 -1
  105. data/spec/cucumber/world/pending_spec.rb +2 -2
  106. data/spec/spec_helper.rb +13 -20
  107. metadata +11 -222
  108. data/.gitignore +0 -20
  109. data/Caliper.yml +0 -4
  110. data/History.txt +0 -1552
  111. data/README.rdoc +0 -26
  112. data/VERSION.yml +0 -5
  113. data/examples/i18n/ro/features/suma.feature +0 -11
  114. data/features/announce.feature +0 -164
  115. data/features/around_hooks.feature +0 -232
  116. data/features/bug_371.feature +0 -32
  117. data/features/bug_464.feature +0 -16
  118. data/features/bug_475.feature +0 -42
  119. data/features/bug_585_tab_indentation.feature +0 -22
  120. data/features/bug_600.feature +0 -67
  121. data/features/call_steps_from_stepdefs.feature +0 -154
  122. data/features/cucumber_cli.feature +0 -591
  123. data/features/cucumber_cli_outlines.feature +0 -117
  124. data/features/default_snippets.feature +0 -42
  125. data/features/diffing.feature +0 -25
  126. data/features/drb_server_integration.feature +0 -174
  127. data/features/exception_in_after_block.feature +0 -127
  128. data/features/exception_in_after_step_block.feature +0 -104
  129. data/features/exception_in_before_block.feature +0 -98
  130. data/features/exclude_files.feature +0 -20
  131. data/features/expand.feature +0 -60
  132. data/features/html_formatter.feature +0 -8
  133. data/features/html_formatter/a.html +0 -582
  134. data/features/junit_formatter.feature +0 -88
  135. data/features/language_from_header.feature +0 -30
  136. data/features/language_help.feature +0 -78
  137. data/features/listener_debugger_formatter.feature +0 -42
  138. data/features/multiline_names.feature +0 -44
  139. data/features/negative_tagged_hooks.feature +0 -60
  140. data/features/post_configuration_hook.feature +0 -37
  141. data/features/profiles.feature +0 -126
  142. data/features/rake_task.feature +0 -152
  143. data/features/report_called_undefined_steps.feature +0 -34
  144. data/features/rerun_formatter.feature +0 -45
  145. data/features/simplest.feature +0 -11
  146. data/features/snippet.feature +0 -23
  147. data/features/snippets_when_using_star_keyword.feature +0 -36
  148. data/features/step_definitions/extra_steps.rb +0 -2
  149. data/features/step_definitions/simplest_steps.rb +0 -3
  150. data/features/step_definitions/wire_steps.rb +0 -32
  151. data/features/support/env.rb.simplest +0 -7
  152. data/features/support/fake_wire_server.rb +0 -77
  153. data/features/table_diffing.feature +0 -45
  154. data/features/table_mapping.feature +0 -34
  155. data/features/tag_logic.feature +0 -258
  156. data/features/transform.feature +0 -245
  157. data/features/unicode_table.feature +0 -35
  158. data/features/usage_and_stepdefs_formatter.feature +0 -169
  159. data/features/wire_protocol.feature +0 -332
  160. data/features/wire_protocol_table_diffing.feature +0 -119
  161. data/features/wire_protocol_tags.feature +0 -87
  162. data/features/wire_protocol_timeouts.feature +0 -63
  163. data/features/work_in_progress.feature +0 -156
  164. data/fixtures/json/features/pystring.feature +0 -8
  165. data/fixtures/self_test/features/background/background_tagged_before_on_outline.feature +0 -12
  166. data/fixtures/self_test/features/background/background_with_name.feature +0 -7
  167. data/fixtures/self_test/features/background/failing_background.feature +0 -12
  168. data/fixtures/self_test/features/background/failing_background_after_success.feature +0 -11
  169. data/fixtures/self_test/features/background/multiline_args_background.feature +0 -32
  170. data/fixtures/self_test/features/background/passing_background.feature +0 -10
  171. data/fixtures/self_test/features/background/pending_background.feature +0 -10
  172. data/fixtures/self_test/features/background/scenario_outline_failing_background.feature +0 -16
  173. data/fixtures/self_test/features/background/scenario_outline_passing_background.feature +0 -16
  174. data/gem_tasks/features.rake +0 -14
  175. data/gem_tasks/sdoc.rake +0 -12
  176. data/lib/cucumber/ast/py_string.rb +0 -80
  177. data/lib/cucumber/formatter/color_io.rb +0 -23
  178. data/lib/cucumber/formatter/tag_cloud.rb +0 -35
  179. data/spec/cucumber/ast/py_string_spec.rb +0 -40
  180. data/spec/cucumber/formatter/color_io_spec.rb +0 -29
  181. data/spec/cucumber/step_mother_spec.rb +0 -302
@@ -1,4 +1,12 @@
1
1
  require 'cucumber/platform'
2
+ require 'gherkin/formatter/ansi_escapes'
3
+
4
+ begin
5
+ # Support Rake > 0.8.7
6
+ require 'rake/dsl_definition'
7
+ include Rake::DSL
8
+ rescue LoadError
9
+ end
2
10
 
3
11
  module Cucumber
4
12
  module Rake
@@ -8,7 +16,7 @@ module Cucumber
8
16
  #
9
17
  # Cucumber::Rake::Task.new
10
18
  #
11
- # This will define a task named <tt>cucumber</tt> described as 'Run Cucumber features'.
19
+ # This will define a task named <tt>cucumber</tt> described as 'Run Cucumber features'.
12
20
  # It will use steps from 'features/**/*.rb' and features in 'features/**/*.feature'.
13
21
  #
14
22
  # To further configure the task, you can pass a block:
@@ -22,68 +30,96 @@ module Cucumber
22
30
  # Cucumber::Rake::Task.new do |t|
23
31
  # t.rcov = true
24
32
  # end
25
- #
33
+ #
26
34
  # See the attributes for additional configuration possibilities.
27
35
  class Task
36
+ include Gherkin::Formatter::AnsiEscapes
37
+
28
38
  class InProcessCucumberRunner #:nodoc:
29
39
  attr_reader :args
30
-
40
+
31
41
  def initialize(libs, cucumber_opts, feature_files)
32
42
  raise "libs must be an Array when running in-process" unless Array === libs
33
43
  libs.reverse.each{|lib| $LOAD_PATH.unshift(lib)}
34
44
  @args = (
35
- cucumber_opts +
45
+ cucumber_opts +
36
46
  feature_files
37
47
  ).flatten.compact
38
48
  end
39
-
49
+
40
50
  def run
41
51
  require 'cucumber/cli/main'
42
52
  failure = Cucumber::Cli::Main.execute(args)
43
53
  raise "Cucumber failed" if failure
44
54
  end
45
55
  end
46
-
56
+
47
57
  class ForkedCucumberRunner #:nodoc:
48
- attr_reader :args
49
-
50
- def initialize(libs, cucumber_bin, cucumber_opts, feature_files)
51
- @args = (
52
- ['-I'] + load_path(libs) +
53
- quoted_binary(cucumber_bin) +
54
- cucumber_opts +
55
- feature_files
56
- ).flatten
58
+
59
+ def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files)
60
+ @libs = libs
61
+ @cucumber_bin = cucumber_bin
62
+ @cucumber_opts = cucumber_opts
63
+ @bundler = bundler
64
+ @feature_files = feature_files
57
65
  end
58
66
 
59
67
  def load_path(libs)
60
- ['"%s"' % libs.join(File::PATH_SEPARATOR)]
68
+ ['"%s"' % @libs.join(File::PATH_SEPARATOR)]
61
69
  end
62
70
 
63
71
  def quoted_binary(cucumber_bin)
64
72
  ['"%s"' % cucumber_bin]
65
73
  end
66
74
 
67
- def runner
68
- File.exist?("./Gemfile") ? ["bundle", "exec", RUBY] : [RUBY]
75
+ def use_bundler
76
+ @bundler.nil? ? File.exist?("./Gemfile") && gem_available?("bundler") : @bundler
77
+ end
78
+
79
+ def gem_available?(gemname)
80
+ gem_available_new_rubygems?(gemname) || gem_available_old_rubygems?(gemname)
81
+ end
82
+
83
+ def gem_available_old_rubygems?(gemname)
84
+ Gem.available?(gemname)
85
+ end
86
+
87
+ def gem_available_new_rubygems?(gemname)
88
+ Gem::Specification.respond_to?(:find_all_by_name) && Gem::Specification.find_all_by_name(gemname).any?
89
+ end
90
+
91
+ def cmd
92
+ if use_bundler
93
+ [ Cucumber::RUBY_BINARY, '-S', 'bundle', 'exec', 'cucumber', @cucumber_opts,
94
+ @feature_files ].flatten
95
+ else
96
+ [ Cucumber::RUBY_BINARY, '-I', load_path(@libs), quoted_binary(@cucumber_bin),
97
+ @cucumber_opts, @feature_files ].flatten
98
+ end
69
99
  end
70
100
 
71
101
  def run
72
- sh((runner + args).join(" "))
102
+ sh(cmd.join(" "))
73
103
  end
74
104
  end
75
105
 
76
106
  class RCovCucumberRunner < ForkedCucumberRunner #:nodoc:
77
- def initialize(libs, cucumber_bin, cucumber_opts, feature_files, rcov_opts)
78
- @args = (
79
- ['-I'] + load_path(libs) +
80
- ['-S', 'rcov'] + rcov_opts +
81
- quoted_binary(cucumber_bin) +
82
- ['--'] +
83
- cucumber_opts +
84
- feature_files
85
- ).flatten
107
+
108
+ def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files, rcov_opts)
109
+ super( libs, cucumber_bin, cucumber_opts, bundler, feature_files )
110
+ @rcov_opts = rcov_opts
111
+ end
112
+
113
+ def cmd
114
+ if use_bundler
115
+ [Cucumber::RUBY_BINARY, '-S', 'bundle', 'exec', 'rcov', @rcov_opts,
116
+ quoted_binary(@cucumber_bin), '--', @cucumber_opts, @feature_files].flatten
117
+ else
118
+ [Cucumber::RUBY_BINARY, '-I', load_path(@libs), '-S', 'rcov', @rcov_opts,
119
+ quoted_binary(@cucumber_bin), '--', @cucumber_opts, @feature_files].flatten
120
+ end
86
121
  end
122
+
87
123
  end
88
124
 
89
125
  LIB = File.expand_path(File.dirname(__FILE__) + '/../..') #:nodoc:
@@ -104,6 +140,12 @@ module Cucumber
104
140
  # Run cucumber with RCov? Defaults to false. If you set this to
105
141
  # true, +fork+ is implicit.
106
142
  attr_accessor :rcov
143
+ def rcov=(flag)
144
+ if(flag && Cucumber::RUBY_1_9)
145
+ raise failed + "RCov only works on Ruby 1.8.x. You may want to use SimpleCov: https://github.com/colszowka/simplecov" + reset
146
+ end
147
+ @rcov = flag
148
+ end
107
149
 
108
150
  # Extra options to pass to rcov.
109
151
  # It's recommended to pass an Array, but if it's a String it will be #split by ' '.
@@ -117,10 +159,17 @@ module Cucumber
117
159
  # your load path and gems.
118
160
  attr_accessor :fork
119
161
 
120
- # Define what profile to be used. When used with cucumber_opts it is simply appended
162
+ # Define what profile to be used. When used with cucumber_opts it is simply appended
121
163
  # to it. Will be ignored when CUCUMBER_OPTS is used.
122
164
  attr_accessor :profile
123
165
 
166
+ # Whether or not to run with bundler (bundle exec). Setting this to false may speed
167
+ # up the execution. The default value is true if Bundler is installed and you have
168
+ # a Gemfile, false otherwise.
169
+ #
170
+ # Note that this attribute has no effect if you don't run in forked mode.
171
+ attr_accessor :bundler
172
+
124
173
  # Define Cucumber Rake task
125
174
  def initialize(task_name = "cucumber", desc = "Run Cucumber features")
126
175
  @task_name, @desc = task_name, desc
@@ -146,9 +195,9 @@ module Cucumber
146
195
  def runner(task_args = nil) #:nodoc:
147
196
  cucumber_opts = [(ENV['CUCUMBER_OPTS'] ? ENV['CUCUMBER_OPTS'].split(/\s+/) : nil) || cucumber_opts_with_profile]
148
197
  if(@rcov)
149
- RCovCucumberRunner.new(libs, binary, cucumber_opts, feature_files, rcov_opts)
198
+ RCovCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files, rcov_opts)
150
199
  elsif(@fork)
151
- ForkedCucumberRunner.new(libs, binary, cucumber_opts, feature_files)
200
+ ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files)
152
201
  else
153
202
  InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
154
203
  end
@@ -88,6 +88,7 @@ module Cucumber
88
88
 
89
89
  # Registers a proc that will run after Cucumber is configured. You can register as
90
90
  # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
91
+ # TODO: Deprecate this
91
92
  def AfterConfiguration(&proc)
92
93
  RbDsl.register_rb_hook('after_configuration', [], proc)
93
94
  end
@@ -1,3 +1,4 @@
1
+ require 'cucumber/core_ext/instance_exec'
1
2
  require 'cucumber/rb_support/rb_dsl'
2
3
  require 'cucumber/rb_support/rb_world'
3
4
  require 'cucumber/rb_support/rb_step_definition'
@@ -21,7 +22,7 @@ module Cucumber
21
22
  message << first_proc.backtrace_line('World') << "\n"
22
23
  message << second_proc.backtrace_line('World') << "\n\n"
23
24
  message << "Use Ruby modules instead to extend your worlds. See the Cucumber::RbSupport::RbDsl#World RDoc\n"
24
- message << "or http://wiki.github.com/aslakhellesoy/cucumber/a-whole-new-world.\n\n"
25
+ message << "or http://wiki.github.com/cucumber/cucumber/a-whole-new-world.\n\n"
25
26
  super(message)
26
27
  end
27
28
  end
@@ -29,7 +30,8 @@ module Cucumber
29
30
  # The Ruby implementation of the programming language API.
30
31
  class RbLanguage
31
32
  include LanguageSupport::LanguageMethods
32
- attr_reader :current_world
33
+ attr_reader :current_world,
34
+ :step_definitions
33
35
 
34
36
  Gherkin::I18n.code_keywords.each do |adverb|
35
37
  RbDsl.alias_adverb(adverb)
@@ -65,7 +67,7 @@ module Cucumber
65
67
 
66
68
  # Gets called for each file under features (or whatever is overridden
67
69
  # with --require).
68
- def step_definitions_for(rb_file)
70
+ def step_definitions_for(rb_file) # Looks Unused - Delete?
69
71
  begin
70
72
  require rb_file # This will cause self.add_step_definition and self.add_hook to be called from RbDsl
71
73
  step_definitions
@@ -76,7 +78,7 @@ module Cucumber
76
78
  @step_definitions = nil
77
79
  end
78
80
  end
79
-
81
+
80
82
  def step_matches(name_to_match, name_to_format)
81
83
  @step_definitions.map do |step_definition|
82
84
  if(arguments = step_definition.arguments_from(name_to_match))
@@ -89,7 +91,7 @@ module Cucumber
89
91
 
90
92
  ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
91
93
 
92
- def snippet_text(step_keyword, step_name, multiline_arg_class)
94
+ def snippet_text(code_keyword, step_name, multiline_arg_class)
93
95
  snippet_pattern = Regexp.escape(step_name).gsub('\ ', ' ').gsub('/', '\/')
94
96
  arg_count = 0
95
97
  ARGUMENT_PATTERNS.each do |pattern|
@@ -105,7 +107,7 @@ module Cucumber
105
107
  multiline_class_comment = "# #{multiline_arg_class.default_arg_name} is a #{multiline_arg_class.to_s}\n "
106
108
  end
107
109
 
108
- "#{Gherkin::I18n.code_keyword_for(step_keyword)} /^#{snippet_pattern}$/ do#{block_arg_string}\n #{multiline_class_comment}pending # express the regexp above with the code you wish you had\nend"
110
+ "#{code_keyword} /^#{snippet_pattern}$/ do#{block_arg_string}\n #{multiline_class_comment}pending # express the regexp above with the code you wish you had\nend"
109
111
  end
110
112
 
111
113
  def begin_rb_scenario(scenario)
@@ -138,9 +140,9 @@ module Cucumber
138
140
  end
139
141
 
140
142
  def load_code_file(code_file)
141
- require File.expand_path(code_file) # This will cause self.add_step_definition, self.add_hook, and self.add_transform to be called from RbDsl
143
+ load File.expand_path(code_file) # This will cause self.add_step_definition, self.add_hook, and self.add_transform to be called from RbDsl
142
144
  end
143
-
145
+
144
146
  protected
145
147
 
146
148
  def begin_scenario(scenario)
@@ -38,6 +38,14 @@ module Cucumber
38
38
  @regexp.inspect
39
39
  end
40
40
 
41
+ def to_hash
42
+ flags = ''
43
+ flags += 'm' if (@regexp.options & Regexp::MULTILINE) != 0
44
+ flags += 'i' if (@regexp.options & Regexp::IGNORECASE) != 0
45
+ flags += 'x' if (@regexp.options & Regexp::EXTENDED) != 0
46
+ {'source' => @regexp.source, 'flags' => flags}
47
+ end
48
+
41
49
  def ==(step_definition)
42
50
  regexp_source == step_definition.regexp_source
43
51
  end
@@ -32,6 +32,23 @@ module Cucumber
32
32
  @rb_language.current_world.cucumber_instance_exec(true, @regexp.inspect, *args, &@proc)
33
33
  end
34
34
  end
35
+
36
+ def to_s
37
+ strip_captures(strip_anchors(@regexp.source))
38
+ end
39
+
40
+ private
41
+
42
+ def strip_captures(regexp_source)
43
+ regexp_source.
44
+ gsub(/(\()/, '').
45
+ gsub(/(\))/, '')
46
+ end
47
+
48
+ def strip_anchors(regexp_source)
49
+ regexp_source.
50
+ gsub(/(^\^|\$$)/, '')
51
+ end
35
52
  end
36
53
  end
37
54
  end
@@ -1,7 +1,11 @@
1
+ require 'gherkin/formatter/ansi_escapes'
2
+
1
3
  module Cucumber
2
4
  module RbSupport
3
5
  # All steps are run in the context of an object that extends this module.
4
6
  module RbWorld
7
+ include Gherkin::Formatter::AnsiEscapes
8
+
5
9
  class << self
6
10
  def alias_adverb(adverb)
7
11
  alias_method adverb, :__cucumber_invoke
@@ -32,14 +36,19 @@ module Cucumber
32
36
  @__cucumber_step_mother.table(text_or_table, file, line_offset)
33
37
  end
34
38
 
35
- # See StepMother#py_string
36
- def py_string(string_with_triple_quotes, file=nil, line_offset=0)
37
- @__cucumber_step_mother.py_string(string_with_triple_quotes, file, line_offset)
39
+ # See StepMother#doc_string
40
+ def doc_string(string_with_triple_quotes, file=nil, line_offset=0)
41
+ @__cucumber_step_mother.doc_string(string_with_triple_quotes, file, line_offset)
42
+ end
43
+
44
+ def announce(*messages)
45
+ STDERR.puts failed + "WARNING: #announce is deprecated. Use #puts instead:" + caller[0] + reset
46
+ puts(*messages)
38
47
  end
39
48
 
40
- # See StepMother#announce
41
- def announce(announcement)
42
- @__cucumber_step_mother.announce(announcement)
49
+ # See StepMother#puts
50
+ def puts(*messages)
51
+ @__cucumber_step_mother.puts(*messages)
43
52
  end
44
53
 
45
54
  # See StepMother#ask
@@ -48,17 +57,8 @@ module Cucumber
48
57
  end
49
58
 
50
59
  # See StepMother#embed
51
- def embed(file, mime_type)
52
- @__cucumber_step_mother.embed(file, mime_type)
53
- end
54
-
55
- # Prints out the world class, followed by all included modules.
56
- def announce_world
57
- announce "WORLD:\n #{self.class}"
58
- world = self
59
- (class << self; self; end).instance_eval do
60
- world.announce " #{included_modules.join("\n ")}"
61
- end
60
+ def embed(file, mime_type, label='Screenshot')
61
+ @__cucumber_step_mother.embed(file, mime_type, label)
62
62
  end
63
63
 
64
64
  # Mark the matched step as pending.
@@ -88,7 +88,15 @@ module Cucumber
88
88
  # such errors in World we define it to just return a simple String.
89
89
  #
90
90
  def inspect #:nodoc:
91
- sprintf("#<%s:0x%x>", self.class, self.object_id)
91
+ modules = [self.class]
92
+ (class << self; self; end).instance_eval do
93
+ modules += included_modules
94
+ end
95
+ sprintf("#<%s:0x%x>", modules.join('+'), self.object_id)
96
+ end
97
+
98
+ def to_s
99
+ inspect
92
100
  end
93
101
  end
94
102
  end
@@ -4,13 +4,13 @@ RSpec.configuration.configure_mock_framework
4
4
  World(RSpec::Core::MockFrameworkAdapter)
5
5
 
6
6
  Before do
7
- _setup_mocks
7
+ RSpec::Mocks::setup(self)
8
8
  end
9
9
 
10
10
  After do
11
11
  begin
12
- _verify_mocks
12
+ RSpec::Mocks::verify
13
13
  ensure
14
- _teardown_mocks
14
+ RSpec::Mocks::teardown
15
15
  end
16
16
  end
@@ -1,6 +1,6 @@
1
1
  module Cucumber
2
2
  class StepMatch #:nodoc:
3
- attr_reader :step_definition
3
+ attr_reader :step_definition, :step_arguments
4
4
 
5
5
  # Creates a new StepMatch. The +name_to_report+ argument is what's reported, unless it's is,
6
6
  # in which case +name_to_report+ is used instead.
@@ -20,7 +20,7 @@ module Cucumber
20
20
  end
21
21
 
22
22
  def invoke(multiline_arg)
23
- multiline_arg = Ast::PyString.new(multiline_arg) if String === multiline_arg
23
+ multiline_arg = Ast::DocString.new(multiline_arg) if String === multiline_arg
24
24
  all_args = args
25
25
  all_args << multiline_arg.to_step_definition_arg if multiline_arg
26
26
  @step_definition.invoke(all_args)
@@ -107,5 +107,9 @@ module Cucumber
107
107
  def text_length
108
108
  @step.text_length
109
109
  end
110
+
111
+ def step_arguments
112
+ []
113
+ end
110
114
  end
111
115
  end
@@ -1,431 +1,10 @@
1
- require 'cucumber/constantize'
2
- require 'cucumber/core_ext/instance_exec'
3
- require 'cucumber/language_support/language_methods'
4
- require 'cucumber/formatter/duration'
5
- require 'cucumber/cli/options'
6
- require 'timeout'
1
+ require 'cucumber/runtime'
7
2
 
8
3
  module Cucumber
9
- # Raised when there is no matching StepDefinition for a step.
10
- class Undefined < StandardError
11
- attr_reader :step_name
12
-
13
- def initialize(step_name)
14
- super %{Undefined step: "#{step_name}"}
15
- @step_name = step_name
16
- end
17
-
18
- def nested!
19
- @nested = true
20
- end
21
-
22
- def nested?
23
- @nested
24
- end
25
- end
26
-
27
- # Raised when a StepDefinition's block invokes World#pending
28
- class Pending < StandardError
29
- end
30
-
31
- # Raised when a step matches 2 or more StepDefinitions
32
- class Ambiguous < StandardError
33
- def initialize(step_name, step_definitions, used_guess)
34
- message = "Ambiguous match of \"#{step_name}\":\n\n"
35
- message << step_definitions.map{|sd| sd.backtrace_line}.join("\n")
36
- message << "\n\n"
37
- message << "You can run again with --guess to make Cucumber be more smart about it\n" unless used_guess
38
- super(message)
39
- end
40
- end
41
-
42
- class TagExcess < StandardError
43
- def initialize(messages)
44
- super(messages.join("\n"))
45
- end
46
- end
47
-
48
- # This is the meaty part of Cucumber that ties everything together.
49
- class StepMother
50
- include Constantize
51
- include Formatter::Duration
52
- attr_writer :options, :visitor, :log
53
-
54
- def initialize
55
- @unsupported_programming_languages = []
56
- @programming_languages = []
57
- @language_map = {}
58
- @current_scenario = nil
59
- end
60
-
61
- def load_plain_text_features(feature_files)
62
- features = Ast::Features.new
63
-
64
- tag_counts = {}
65
- start = Time.new
66
- log.debug("Features:\n")
67
- feature_files.each do |f|
68
- feature_file = FeatureFile.new(f)
69
- feature = feature_file.parse(options, tag_counts)
70
- if feature
71
- features.add_feature(feature)
72
- log.debug(" * #{f}\n")
73
- end
74
- end
75
- duration = Time.now - start
76
- log.debug("Parsing feature files took #{format_duration(duration)}\n\n")
77
-
78
- check_tag_limits(tag_counts)
79
-
80
- features
81
- end
82
-
83
- def check_tag_limits(tag_counts)
84
- error_messages = []
85
- options[:tag_expression].limits.each do |tag_name, tag_limit|
86
- tag_locations = (tag_counts[tag_name] || [])
87
- tag_count = tag_locations.length
88
- if tag_count > tag_limit
89
- error = "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
90
- tag_locations.join("\n ")
91
- error_messages << error
92
- end
93
- end
94
- raise TagExcess.new(error_messages) if error_messages.any?
95
- end
96
-
97
- def load_code_files(step_def_files)
98
- log.debug("Code:\n")
99
- step_def_files.each do |step_def_file|
100
- load_code_file(step_def_file)
101
- end
102
- log.debug("\n")
103
- end
104
-
105
- def load_code_file(step_def_file)
106
- if programming_language = programming_language_for(step_def_file)
107
- log.debug(" * #{step_def_file}\n")
108
- programming_language.load_code_file(step_def_file)
109
- else
110
- log.debug(" * #{step_def_file} [NOT SUPPORTED]\n")
111
- end
112
- end
113
-
114
- # Loads and registers programming language implementation.
115
- # Instances are cached, so calling with the same argument
116
- # twice will return the same instance.
117
- #
118
- def load_programming_language(ext)
119
- return @language_map[ext] if @language_map[ext]
120
- programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
121
- programming_language = programming_language_class.new(self)
122
- @programming_languages << programming_language
123
- @language_map[ext] = programming_language
124
- programming_language
125
- end
126
-
127
- # Returns the options passed on the command line.
128
- def options
129
- @options ||= Cli::Options.new
130
- end
131
-
132
- def step_visited(step) #:nodoc:
133
- steps << step unless steps.index(step)
134
- end
135
-
136
- def steps(status = nil) #:nodoc:
137
- @steps ||= []
138
- if(status)
139
- @steps.select{|step| step.status == status}
140
- else
141
- @steps
142
- end
143
- end
144
-
145
- # Output +announcement+ alongside the formatted output.
146
- # This is an alternative to using Kernel#puts - it will display
147
- # nicer, and in all outputs (in case you use several formatters)
148
- #
149
- def announce(msg)
150
- msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s)
151
- end
152
-
153
- # Suspends execution and prompts +question+ to the console (STDOUT).
154
- # An operator (manual tester) can then enter a line of text and hit
155
- # <ENTER>. The entered text is returned, and both +question+ and
156
- # the result is added to the output using #announce.
157
- #
158
- # If you want a beep to happen (to grab the manual tester's attention),
159
- # just prepend ASCII character 7 to the question:
160
- #
161
- # ask("#{7.chr}How many cukes are in the external system?")
162
- #
163
- # If that doesn't issue a beep, you can shell out to something else
164
- # that makes a sound before invoking #ask.
165
- #
166
- def ask(question, timeout_seconds)
167
- STDOUT.puts(question)
168
- STDOUT.flush
169
- announce(question)
170
-
171
- if(Cucumber::JRUBY)
172
- answer = jruby_gets(timeout_seconds)
173
- else
174
- answer = mri_gets(timeout_seconds)
175
- end
176
-
177
- if(answer)
178
- announce(answer)
179
- answer
180
- else
181
- raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
182
- end
183
- end
184
-
185
- # Embed +file+ of MIME type +mime_type+ into the output. This may or may
186
- # not be ignored, depending on what kind of formatter(s) are active.
187
- #
188
- def embed(file, mime_type)
189
- @visitor.embed(file, mime_type)
190
- end
191
-
192
- def scenarios(status = nil) #:nodoc:
193
- @scenarios ||= []
194
- if(status)
195
- @scenarios.select{|scenario| scenario.status == status}
196
- else
197
- @scenarios
198
- end
199
- end
200
-
201
- def invoke(step_name, multiline_argument=nil)
202
- begin
203
- step_match(step_name).invoke(multiline_argument)
204
- rescue Exception => e
205
- e.nested! if Undefined === e
206
- raise e
207
- end
208
- end
209
-
210
- # Invokes a series of steps +steps_text+. Example:
211
- #
212
- # invoke(%Q{
213
- # Given I have 8 cukes in my belly
214
- # Then I should not be thirsty
215
- # })
216
- def invoke_steps(steps_text, i18n, file_colon_line)
217
- file, line = file_colon_line.split(':')
218
- parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps')
219
- parser.parse(steps_text, file, line.to_i)
220
- end
221
-
222
- class StepInvoker
223
- def initialize(step_mother)
224
- @step_mother = step_mother
225
- end
226
-
227
- def step(statement, multiline_arg, result)
228
- cucumber_multiline_arg = case(multiline_arg)
229
- when Gherkin::Formatter::Model::PyString
230
- multiline_arg.value
231
- when Array
232
- Ast::Table.new(multiline_arg.map{|row| row.cells})
233
- else
234
- nil
235
- end
236
- @step_mother.invoke(*[statement.name, cucumber_multiline_arg].compact)
237
- end
238
-
239
- def eof
240
- end
241
- end
242
-
243
- # Returns a Cucumber::Ast::Table for +text_or_table+, which can either
244
- # be a String:
245
- #
246
- # table(%{
247
- # | account | description | amount |
248
- # | INT-100 | Taxi | 114 |
249
- # | CUC-101 | Peeler | 22 |
250
- # })
251
- #
252
- # or a 2D Array:
253
- #
254
- # table([
255
- # %w{ account description amount },
256
- # %w{ INT-100 Taxi 114 },
257
- # %w{ CUC-101 Peeler 22 }
258
- # ])
259
- #
260
- def table(text_or_table, file=nil, line_offset=0)
261
- if Array === text_or_table
262
- Ast::Table.new(text_or_table)
263
- else
264
- Ast::Table.parse(text_or_table, file, line_offset)
265
- end
266
- end
267
-
268
- # Returns a regular String for +string_with_triple_quotes+. Example:
269
- #
270
- # """
271
- # hello
272
- # world
273
- # """
274
- #
275
- # Is retured as: " hello\nworld"
276
- #
277
- def py_string(string_with_triple_quotes, file=nil, line_offset=0)
278
- Ast::PyString.parse(string_with_triple_quotes)
279
- end
280
-
281
- def step_match(step_name, name_to_report=nil) #:nodoc:
282
- matches = @programming_languages.map do |programming_language|
283
- programming_language.step_matches(step_name, name_to_report).to_a
284
- end.flatten
285
- raise Undefined.new(step_name) if matches.empty?
286
- matches = best_matches(step_name, matches) if matches.size > 1 && options[:guess]
287
- raise Ambiguous.new(step_name, matches, options[:guess]) if matches.size > 1
288
- matches[0]
289
- end
290
-
291
- def best_matches(step_name, step_matches) #:nodoc:
292
- no_groups = step_matches.select {|step_match| step_match.args.length == 0}
293
- max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
294
- top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
295
-
296
- if no_groups.any?
297
- longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max
298
- no_groups.select {|step_match| step_match.text_length == longest_regexp_length }
299
- elsif top_groups.any?
300
- shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min
301
- top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length }
302
- else
303
- top_groups
304
- end
305
- end
306
-
307
- def unmatched_step_definitions
308
- @programming_languages.map do |programming_language|
309
- programming_language.unmatched_step_definitions
310
- end.flatten
311
- end
312
-
313
- def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
314
- load_programming_language('rb') if unknown_programming_language?
315
- @programming_languages.map do |programming_language|
316
- programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
317
- end.join("\n")
318
- end
319
-
320
- def unknown_programming_language?
321
- @programming_languages.empty?
322
- end
323
-
324
- def with_hooks(scenario, skip_hooks=false)
325
- around(scenario, skip_hooks) do
326
- before_and_after(scenario, skip_hooks) do
327
- yield scenario
328
- end
329
- end
330
- end
331
-
332
- def around(scenario, skip_hooks=false, &block) #:nodoc:
333
- unless skip_hooks
334
- @programming_languages.reverse.inject(block) do |blk, programming_language|
335
- proc do
336
- programming_language.around(scenario) do
337
- blk.call(scenario)
338
- end
339
- end
340
- end.call
341
- else
342
- yield
343
- end
344
- end
345
-
346
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
347
- before(scenario) unless skip_hooks
348
- yield scenario
349
- after(scenario) unless skip_hooks
350
- scenario_visited(scenario)
351
- end
352
-
353
- def before(scenario) #:nodoc:
354
- return if options[:dry_run] || @current_scenario
355
- @current_scenario = scenario
356
- @programming_languages.each do |programming_language|
357
- programming_language.before(scenario)
358
- end
359
- end
360
-
361
- def after(scenario) #:nodoc:
362
- @current_scenario = nil
363
- return if options[:dry_run]
364
- @programming_languages.each do |programming_language|
365
- programming_language.after(scenario)
366
- end
367
- end
368
-
369
- def after_step #:nodoc:
370
- return if options[:dry_run]
371
- @programming_languages.each do |programming_language|
372
- programming_language.execute_after_step(@current_scenario)
373
- end
374
- end
375
-
376
- def after_configuration(configuration) #:nodoc
377
- @programming_languages.each do |programming_language|
378
- programming_language.after_configuration(configuration)
379
- end
380
- end
381
-
382
- private
383
-
384
- def programming_language_for(step_def_file) #:nodoc:
385
- if ext = File.extname(step_def_file)[1..-1]
386
- return nil if @unsupported_programming_languages.index(ext)
387
- begin
388
- load_programming_language(ext)
389
- rescue LoadError => e
390
- log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")
391
- @unsupported_programming_languages << ext
392
- nil
393
- end
394
- else
395
- nil
396
- end
397
- end
398
-
399
- def max_step_definition_length #:nodoc:
400
- @max_step_definition_length ||= step_definitions.map{|step_definition| step_definition.text_length}.max
401
- end
402
-
403
- def scenario_visited(scenario) #:nodoc:
404
- scenarios << scenario unless scenarios.index(scenario)
405
- end
406
-
407
- def log
408
- @log ||= Logger.new(STDOUT)
409
- end
410
-
411
- def mri_gets(timeout_seconds)
412
- begin
413
- Timeout.timeout(timeout_seconds) do
414
- STDIN.gets
415
- end
416
- rescue Timeout::Error => e
417
- nil
418
- end
419
- end
420
-
421
- def jruby_gets(timeout_seconds)
422
- answer = nil
423
- t = java.lang.Thread.new do
424
- answer = STDIN.gets
425
- end
426
- t.start
427
- t.join(timeout_seconds * 1000)
428
- answer
4
+ class StepMother < Runtime
5
+ def initialize(*args)
6
+ warn("StepMother has been deprecated and will be gently put to sleep at the next major release. Please use Runtime instead. #{caller[0]}")
7
+ super
429
8
  end
430
9
  end
431
- end
10
+ end