cucumber 0.3.96 → 0.3.97

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 (93) hide show
  1. data/History.txt +22 -3
  2. data/License.txt +2 -0
  3. data/Manifest.txt +9 -5
  4. data/config/hoe.rb +1 -0
  5. data/examples/i18n/Rakefile +5 -3
  6. data/examples/i18n/fi/features/yhteenlasku.feature +5 -4
  7. data/examples/python/features/step_definitions/fib_steps.py +3 -0
  8. data/examples/python/features/support/env.rb +21 -21
  9. data/gem_tasks/contributors.rake +8 -0
  10. data/gem_tasks/features.rake +1 -0
  11. data/gem_tasks/sdoc.rake +7 -0
  12. data/lib/README.rdoc +12 -0
  13. data/lib/cucumber/ast/background.rb +1 -1
  14. data/lib/cucumber/ast/comment.rb +1 -1
  15. data/lib/cucumber/ast/examples.rb +9 -4
  16. data/lib/cucumber/ast/feature.rb +1 -1
  17. data/lib/cucumber/ast/feature_element.rb +1 -1
  18. data/lib/cucumber/ast/features.rb +1 -1
  19. data/lib/cucumber/ast/outline_table.rb +2 -2
  20. data/lib/cucumber/ast/py_string.rb +1 -1
  21. data/lib/cucumber/ast/scenario.rb +1 -1
  22. data/lib/cucumber/ast/scenario_outline.rb +8 -7
  23. data/lib/cucumber/ast/step.rb +1 -1
  24. data/lib/cucumber/ast/step_collection.rb +1 -1
  25. data/lib/cucumber/ast/step_invocation.rb +1 -1
  26. data/lib/cucumber/ast/table.rb +65 -45
  27. data/lib/cucumber/ast/tags.rb +2 -2
  28. data/lib/cucumber/ast/visitor.rb +6 -8
  29. data/lib/cucumber/broadcaster.rb +1 -1
  30. data/lib/cucumber/cli/language_help_formatter.rb +1 -1
  31. data/lib/cucumber/cli/main.rb +15 -52
  32. data/lib/cucumber/constantize.rb +1 -1
  33. data/lib/cucumber/core_ext/exception.rb +1 -1
  34. data/lib/cucumber/core_ext/instance_exec.rb +8 -3
  35. data/lib/cucumber/core_ext/proc.rb +1 -1
  36. data/lib/cucumber/core_ext/string.rb +1 -1
  37. data/lib/cucumber/feature_file.rb +4 -3
  38. data/lib/cucumber/filter.rb +2 -1
  39. data/lib/cucumber/formatter/ansicolor.rb +6 -5
  40. data/lib/cucumber/formatter/color_io.rb +2 -2
  41. data/lib/cucumber/formatter/console.rb +2 -0
  42. data/lib/cucumber/formatter/duration.rb +3 -0
  43. data/lib/cucumber/formatter/html.rb +1 -0
  44. data/lib/cucumber/formatter/junit.rb +1 -0
  45. data/lib/cucumber/formatter/ordered_xml_markup.rb +1 -1
  46. data/lib/cucumber/formatter/pretty.rb +18 -4
  47. data/lib/cucumber/formatter/profile.rb +1 -0
  48. data/lib/cucumber/formatter/progress.rb +1 -0
  49. data/lib/cucumber/formatter/rerun.rb +2 -0
  50. data/lib/cucumber/formatter/steps.rb +1 -0
  51. data/lib/cucumber/formatter/tag_cloud.rb +2 -1
  52. data/lib/cucumber/formatter/unicode.rb +1 -1
  53. data/lib/cucumber/formatter/usage.rb +1 -0
  54. data/lib/cucumber/language_support.rb +30 -0
  55. data/lib/cucumber/language_support/language_methods.rb +34 -12
  56. data/lib/cucumber/parser/feature.rb +81 -57
  57. data/lib/cucumber/parser/feature.tt +3 -3
  58. data/lib/cucumber/parser/natural_language.rb +1 -1
  59. data/lib/cucumber/parser/table.rb +3 -0
  60. data/lib/cucumber/parser/treetop_ext.rb +6 -5
  61. data/lib/cucumber/platform.rb +1 -2
  62. data/lib/cucumber/py_support/py_dsl.py +8 -0
  63. data/lib/cucumber/py_support/py_language.py +2 -0
  64. data/lib/cucumber/py_support/py_language.rb +68 -0
  65. data/lib/cucumber/rails/world.rb +2 -1
  66. data/lib/cucumber/rake/task.rb +13 -11
  67. data/lib/cucumber/rb_support/rb_dsl.rb +27 -15
  68. data/lib/cucumber/rb_support/rb_hook.rb +1 -2
  69. data/lib/cucumber/rb_support/rb_language.rb +57 -33
  70. data/lib/cucumber/rb_support/rb_step_definition.rb +42 -38
  71. data/lib/cucumber/rb_support/rb_world.rb +93 -0
  72. data/lib/cucumber/rspec_neuter.rb +3 -3
  73. data/lib/cucumber/step_match.rb +2 -2
  74. data/lib/cucumber/step_mother.rb +91 -65
  75. data/lib/cucumber/version.rb +1 -1
  76. data/lib/cucumber/webrat/element_locator.rb +3 -3
  77. data/rails_generators/cucumber/templates/cucumber.rake +2 -0
  78. data/rails_generators/cucumber/templates/webrat_steps.rb +4 -0
  79. data/spec/cucumber/ast/background_spec.rb +8 -1
  80. data/spec/cucumber/ast/scenario_outline_spec.rb +1 -0
  81. data/spec/cucumber/ast/table_spec.rb +10 -0
  82. data/spec/cucumber/cli/options_spec.rb +1 -1
  83. data/spec/cucumber/parser/feature_parser_spec.rb +4 -0
  84. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +114 -0
  85. data/spec/cucumber/step_mother_spec.rb +29 -10
  86. data/spec/cucumber/treetop_parser/with_comments.feature +14 -1
  87. data/spec/cucumber/world/pending_spec.rb +1 -1
  88. metadata +21 -7
  89. data/gem_tasks/yard.rake +0 -8
  90. data/lib/cucumber/language_support/hook_methods.rb +0 -9
  91. data/lib/cucumber/world.rb +0 -89
  92. data/spec/cucumber/ast/visitor_spec.rb +0 -27
  93. data/spec/cucumber/step_definition_spec.rb +0 -102
@@ -0,0 +1,93 @@
1
+ module Cucumber
2
+ module RbSupport
3
+ # All steps are run in the context of an object that extends this module.
4
+ module RbWorld
5
+ class << self
6
+ def alias_adverb(adverb)
7
+ alias_method adverb, :__cucumber_invoke
8
+ end
9
+ end
10
+
11
+ attr_writer :__cucumber_step_mother
12
+
13
+ # Call a step from within a step definition. This method is aliased to
14
+ # the same i18n as RbDsl.
15
+ def __cucumber_invoke(name, multiline_argument=nil) #:nodoc:
16
+ begin
17
+ step_match = @__cucumber_step_mother.step_match(name)
18
+ step_match.invoke(multiline_argument)
19
+ rescue Exception => e
20
+ e.nested! if Undefined === e
21
+ raise e
22
+ end
23
+ end
24
+
25
+ # Returns a Cucumber::Ast::Table for +text_or_table+, which can either
26
+ # be a String:
27
+ #
28
+ # table(%{
29
+ # | account | description | amount |
30
+ # | INT-100 | Taxi | 114 |
31
+ # | CUC-101 | Peeler | 22 |
32
+ # })
33
+ #
34
+ # or a 2D Array:
35
+ #
36
+ # table([
37
+ # %w{ account description amount },
38
+ # %w{ INT-100 Taxi 114 },
39
+ # %w{ CUC-101 Peeler 22 }
40
+ # ])
41
+ #
42
+ def table(text_or_table, file=nil, line_offset=0)
43
+ if Array === text_or_table
44
+ Ast::Table.new(text_or_table)
45
+ else
46
+ @table_parser ||= Parser::TableParser.new
47
+ @table_parser.parse_or_fail(text_or_table.strip, file, line_offset)
48
+ end
49
+ end
50
+
51
+ # Output +announcement+ alongside the formatted output.
52
+ # This is an alternative to using Kernel#puts - it will display
53
+ # nicer, and in all outputs (in case you use several formatters)
54
+ #
55
+ # Beware that the output will be printed *before* the corresponding
56
+ # step. This is because the step itself will not be printed until
57
+ # after it has run, so it can be coloured according to its status.
58
+ def announce(announcement)
59
+ @__cucumber_step_mother.announce(announcement)
60
+ end
61
+
62
+ # Mark the matched step as pending.
63
+ def pending(message = "TODO")
64
+ if block_given?
65
+ begin
66
+ yield
67
+ rescue Exception => e
68
+ raise Pending.new(message)
69
+ end
70
+ raise Pending.new("Expected pending '#{message}' to fail. No Error was raised. No longer pending?")
71
+ else
72
+ raise Pending.new(message)
73
+ end
74
+ end
75
+
76
+ # The default implementation of Object#inspect recursively
77
+ # traverses all instance variables and invokes inspect.
78
+ # This can be time consuming if the object graph is large.
79
+ #
80
+ # This can cause unnecessary delays when certain exceptions
81
+ # occur. For example, MRI internally invokes #inspect on an
82
+ # object that raises a NoMethodError. (JRuby does not do this).
83
+ #
84
+ # A World object can have many references created by the user
85
+ # or frameworks (Rails), so to avoid long waiting times on
86
+ # such errors in World we define it to just return a simple String.
87
+ #
88
+ def inspect #:nodoc:
89
+ sprintf("#<%s:0x%x>", self.class, self.object_id)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,11 +1,11 @@
1
1
  require 'optparse'
2
2
 
3
- module Spec
4
- module Runner
3
+ module Spec #:nodoc:
4
+ module Runner #:nodoc:
5
5
  # Neuters RSpec's option parser.
6
6
  # (RSpec's option parser tries to parse ARGV, which
7
7
  # will fail when running cucumber)
8
- class OptionParser < ::OptionParser
8
+ class OptionParser < ::OptionParser #:nodoc:
9
9
  NEUTERED_RSPEC = Object.new
10
10
  def NEUTERED_RSPEC.method_missing(m, *args); self; end
11
11
 
@@ -1,5 +1,5 @@
1
1
  module Cucumber
2
- class StepMatch
2
+ class StepMatch #:nodoc:
3
3
  attr_reader :step_definition, :args
4
4
 
5
5
  def initialize(step_definition, step_name, formatted_step_name, args)
@@ -33,7 +33,7 @@ module Cucumber
33
33
  end
34
34
  end
35
35
 
36
- class NoStepMatch
36
+ class NoStepMatch #:nodoc:
37
37
  attr_reader :step_definition, :name
38
38
 
39
39
  def initialize(step, name)
@@ -1,12 +1,12 @@
1
+ require 'logger'
1
2
  require 'cucumber/constantize'
2
- require 'cucumber/world'
3
3
  require 'cucumber/core_ext/instance_exec'
4
4
  require 'cucumber/parser/natural_language'
5
- require 'cucumber/language_support/hook_methods'
6
5
  require 'cucumber/language_support/language_methods'
7
6
  require 'cucumber/language_support/step_definition_methods'
8
7
 
9
8
  module Cucumber
9
+ # Raised when there is no matching StepDefinition for a step.
10
10
  class Undefined < StandardError
11
11
  attr_reader :step_name
12
12
 
@@ -28,7 +28,7 @@ module Cucumber
28
28
  class Pending < StandardError
29
29
  end
30
30
 
31
- # Raised when a step matches 2 or more StepDefinition
31
+ # Raised when a step matches 2 or more StepDefinitions
32
32
  class Ambiguous < StandardError
33
33
  def initialize(step_name, step_definitions, used_guess)
34
34
  message = "Ambiguous match of \"#{step_name}\":\n\n"
@@ -49,27 +49,61 @@ module Cucumber
49
49
  end
50
50
  end
51
51
 
52
- # This is the main interface for registering step definitions, which is done
53
- # from <tt>*_steps.rb</tt> files. This module is included right at the top-level
54
- # so #register_step_definition (and more interestingly - its aliases) are
55
- # available from the top-level.
52
+ # This is the meaty part of Cucumber that ties everything together.
56
53
  class StepMother
57
54
  include Constantize
58
55
 
59
- attr_writer :options, :visitor
56
+ attr_writer :options, :visitor, :log
60
57
 
61
58
  def initialize
59
+ @unsupported_programming_languages = []
62
60
  @programming_languages = []
63
61
  @language_map = {}
64
62
  load_natural_language('en')
65
63
  end
66
64
 
65
+ def load_plain_text_features(feature_files)
66
+ features = Ast::Features.new
67
+
68
+ log.debug("Features:\n")
69
+ feature_files.each do |f|
70
+ feature_file = FeatureFile.new(f)
71
+ feature = feature_file.parse(self, options)
72
+ if feature
73
+ features.add_feature(feature)
74
+ log.debug(" * #{f}\n")
75
+ end
76
+ end
77
+ log.debug("\n")
78
+ features
79
+ end
80
+
81
+ def load_code_files(step_def_files)
82
+ log.debug("Code:\n")
83
+ step_def_files.each do |step_def_file|
84
+ load_code_file(step_def_file)
85
+ end
86
+ log.debug("\n")
87
+ end
88
+
89
+ def load_code_file(step_def_file)
90
+ if programming_language = programming_language_for(step_def_file)
91
+ log.debug(" * #{step_def_file}\n")
92
+ step_definitions = programming_language.step_definitions_for(step_def_file)
93
+ register_step_definitions(step_definitions)
94
+ else
95
+ log.debug(" * #{step_def_file} [NOT SUPPORTED]\n")
96
+ end
97
+ end
98
+
99
+ def register_step_definitions(step_definitions)
100
+ step_definitions.each{|step_definition| register_step_definition(step_definition)}
101
+ end
102
+
67
103
  # Loads and registers programming language implementation.
68
104
  # Instances are cached, so calling with the same argument
69
105
  # twice will return the same instance.
70
106
  #
71
- # Raises an exception if the language can't be loaded.
72
- #
73
107
  def load_programming_language(ext)
74
108
  return @language_map[ext] if @language_map[ext]
75
109
  programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
@@ -88,26 +122,16 @@ module Cucumber
88
122
  Parser::NaturalLanguage.get(self, lang)
89
123
  end
90
124
 
91
- # Registers a StepDefinition. This can be a Ruby StepDefintion,
92
- # or any other kind of object that implements the StepDefintion
93
- # contract (API).
94
- def register_step_definition(step_definition)
95
- step_definitions.each do |already|
96
- raise Redundant.new(already, step_definition) if already.same_regexp?(step_definition.regexp)
97
- end
98
- step_definitions << step_definition
99
- step_definition
100
- end
101
-
125
+ # Returns the options passed on the command line.
102
126
  def options
103
127
  @options ||= {}
104
128
  end
105
129
 
106
- def step_visited(step)
130
+ def step_visited(step) #:nodoc:
107
131
  steps << step unless steps.index(step)
108
132
  end
109
-
110
- def steps(status = nil)
133
+
134
+ def steps(status = nil) #:nodoc:
111
135
  @steps ||= []
112
136
  if(status)
113
137
  @steps.select{|step| step.status == status}
@@ -116,11 +140,11 @@ module Cucumber
116
140
  end
117
141
  end
118
142
 
119
- def announce(msg)
143
+ def announce(msg) #:nodoc:
120
144
  @visitor.announce(msg)
121
145
  end
122
146
 
123
- def scenarios(status = nil)
147
+ def scenarios(status = nil) #:nodoc:
124
148
  @scenarios ||= []
125
149
  if(status)
126
150
  @scenarios.select{|scenario| scenario.status == status}
@@ -129,20 +153,7 @@ module Cucumber
129
153
  end
130
154
  end
131
155
 
132
- def register_hook(phase, hook)
133
- hooks[phase.to_sym] << hook
134
- hook
135
- end
136
-
137
- def hooks
138
- @hooks ||= Hash.new {|hash, phase| hash[phase] = []}
139
- end
140
-
141
- def hooks_for(phase, scenario)
142
- hooks[phase.to_sym].select{|hook| scenario.accept_hook?(hook)}
143
- end
144
-
145
- def step_match(step_name, formatted_step_name=nil)
156
+ def step_match(step_name, formatted_step_name=nil) #:nodoc:
146
157
  matches = step_definitions.map { |d| d.step_match(step_name, formatted_step_name) }.compact
147
158
  raise Undefined.new(step_name) if matches.empty?
148
159
  matches = best_matches(step_name, matches) if matches.size > 1 && options[:guess]
@@ -150,7 +161,7 @@ module Cucumber
150
161
  matches[0]
151
162
  end
152
163
 
153
- def best_matches(step_name, step_matches)
164
+ def best_matches(step_name, step_matches) #:nodoc:
154
165
  no_groups = step_matches.select {|step_match| step_match.args.length == 0}
155
166
  max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
156
167
  top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
@@ -166,31 +177,31 @@ module Cucumber
166
177
  end
167
178
  end
168
179
 
169
- def clear!
180
+ def clear! #:nodoc:
170
181
  step_definitions.clear
171
182
  hooks.clear
172
183
  steps.clear
173
184
  scenarios.clear
174
185
  end
175
186
 
176
- def step_definitions
187
+ def step_definitions #:nodoc:
177
188
  @step_definitions ||= []
178
189
  end
179
190
 
180
- def snippet_text(step_keyword, step_name, multiline_arg_class)
191
+ def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
181
192
  @programming_languages.map do |programming_language|
182
193
  programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
183
194
  end.join("\n")
184
195
  end
185
196
 
186
- def before_and_after(scenario, skip_hooks=false)
197
+ def before_and_after(scenario, skip_hooks=false) #:nodoc:
187
198
  before(scenario) unless skip_hooks
188
199
  yield scenario
189
200
  after(scenario) unless skip_hooks
190
201
  scenario_visited(scenario)
191
202
  end
192
203
 
193
- def register_adverbs(adverbs)
204
+ def register_adverbs(adverbs) #:nodoc:
194
205
  @adverbs ||= []
195
206
  @adverbs += adverbs
196
207
  @adverbs.uniq!
@@ -198,22 +209,8 @@ module Cucumber
198
209
  programming_language.alias_adverbs(@adverbs)
199
210
  end
200
211
  end
201
-
202
- def begin_scenario
203
- return if options[:dry_run]
204
- @programming_languages.each do |programming_language|
205
- programming_language.begin_scenario
206
- end
207
- end
208
-
209
- def end_scenario
210
- return if options[:dry_run]
211
- @programming_languages.each do |programming_language|
212
- programming_language.end_scenario
213
- end
214
- end
215
212
 
216
- def before(scenario)
213
+ def before(scenario) #:nodoc:
217
214
  return if options[:dry_run] || @current_scenario
218
215
  @current_scenario = scenario
219
216
  @programming_languages.each do |programming_language|
@@ -221,7 +218,7 @@ module Cucumber
221
218
  end
222
219
  end
223
220
 
224
- def after(scenario)
221
+ def after(scenario) #:nodoc:
225
222
  @current_scenario = nil
226
223
  return if options[:dry_run]
227
224
  @programming_languages.each do |programming_language|
@@ -229,7 +226,7 @@ module Cucumber
229
226
  end
230
227
  end
231
228
 
232
- def after_step
229
+ def after_step #:nodoc:
233
230
  return if options[:dry_run]
234
231
  @programming_languages.each do |programming_language|
235
232
  programming_language.execute_after_step(@current_scenario)
@@ -238,12 +235,41 @@ module Cucumber
238
235
 
239
236
  private
240
237
 
241
- def max_step_definition_length
238
+ # Registers a StepDefinition. This can be a Ruby StepDefintion,
239
+ # or any other kind of object that implements the StepDefintion
240
+ # contract (API).
241
+ def register_step_definition(step_definition)
242
+ step_definitions.each do |already|
243
+ raise Redundant.new(already, step_definition) if already.same_regexp?(step_definition.regexp)
244
+ end
245
+ step_definitions << step_definition
246
+ step_definition
247
+ end
248
+
249
+ def programming_language_for(step_def_file) #:nodoc:
250
+ if ext = File.extname(step_def_file)[1..-1]
251
+ return nil if @unsupported_programming_languages.index(ext)
252
+ begin
253
+ load_programming_language(ext)
254
+ rescue LoadError
255
+ @unsupported_programming_languages << ext
256
+ nil
257
+ end
258
+ else
259
+ nil
260
+ end
261
+ end
262
+
263
+ def max_step_definition_length #:nodoc:
242
264
  @max_step_definition_length ||= step_definitions.map{|step_definition| step_definition.text_length}.max
243
265
  end
244
266
 
245
- def scenario_visited(scenario)
267
+ def scenario_visited(scenario) #:nodoc:
246
268
  scenarios << scenario unless scenarios.index(scenario)
247
269
  end
270
+
271
+ def log
272
+ @log ||= Logger.new(STDOUT)
273
+ end
248
274
  end
249
275
  end
@@ -2,7 +2,7 @@ module Cucumber #:nodoc:
2
2
  class VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 96
5
+ TINY = 97
6
6
  PATCH = nil # Set to nil for official release
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
@@ -55,7 +55,7 @@ module Webrat
55
55
  end
56
56
 
57
57
  module Locators
58
- class ElementLocator < Locator
58
+ class ElementLocator < Locator #:nodoc:
59
59
  def locate
60
60
  Element.load(@session, table_element)
61
61
  end
@@ -77,11 +77,11 @@ module Webrat
77
77
  alias table_at element_at # Backwards compatibility with Cucumber
78
78
  end
79
79
 
80
- module Methods
80
+ module Methods #:nodoc:
81
81
  delegate_to_session :element_at, :table_at
82
82
  end
83
83
 
84
- class Session
84
+ class Session #:nodoc:
85
85
  def_delegators :current_scope, :element_at, :table_at
86
86
  end
87
87
  end
@@ -21,6 +21,8 @@ begin
21
21
  desc 'Alias for cucumber:ok'
22
22
  task :cucumber => 'cucumber:ok'
23
23
 
24
+ task :default => :cucumber
25
+
24
26
  task :features => :cucumber do
25
27
  STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
26
28
  end