cucumber 0.3.96 → 0.3.97

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