cucumber 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
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