cucumber 0.3.101 → 0.3.102

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/History.txt +31 -2
  2. data/Manifest.txt +7 -4
  3. data/config/hoe.rb +2 -2
  4. data/examples/pure_java/README.textile +2 -2
  5. data/examples/self_test/features/step_definitions/sample_steps.rb +0 -12
  6. data/examples/self_test/features/support/env.rb +0 -6
  7. data/features/cucumber_cli.feature +11 -24
  8. data/features/custom_formatter.feature +38 -3
  9. data/features/default_snippets.feature +42 -0
  10. data/features/html_formatter/a.html +1 -1
  11. data/features/negative_tagged_hooks.feature +61 -0
  12. data/features/step_definitions/cucumber_steps.rb +1 -1
  13. data/features/support/env.rb +1 -1
  14. data/features/transform.feature +88 -12
  15. data/features/usage.feature +0 -6
  16. data/lib/cucumber/ast/feature.rb +4 -0
  17. data/lib/cucumber/ast/feature_element.rb +49 -42
  18. data/lib/cucumber/ast/step_invocation.rb +1 -1
  19. data/lib/cucumber/ast/tags.rb +25 -3
  20. data/lib/cucumber/cli/drb_client.rb +7 -1
  21. data/lib/cucumber/cli/main.rb +5 -3
  22. data/lib/cucumber/cli/options.rb +15 -24
  23. data/lib/cucumber/constantize.rb +8 -2
  24. data/lib/cucumber/core_ext/instance_exec.rb +2 -1
  25. data/lib/cucumber/core_ext/string.rb +12 -22
  26. data/lib/cucumber/filter.rb +2 -12
  27. data/lib/cucumber/formatter/console.rb +21 -21
  28. data/lib/cucumber/formatter/html.rb +1 -1
  29. data/lib/cucumber/formatter/pdf.rb +1 -1
  30. data/lib/cucumber/formatter/pretty.rb +1 -1
  31. data/lib/cucumber/language_support/language_methods.rb +19 -2
  32. data/lib/cucumber/language_support/step_definition_methods.rb +2 -25
  33. data/lib/cucumber/parser/feature.rb +13 -50
  34. data/lib/cucumber/parser/feature.tt +13 -47
  35. data/lib/cucumber/parser/natural_language.rb +1 -1
  36. data/lib/cucumber/rails/action_controller.rb +33 -0
  37. data/lib/cucumber/rails/active_record.rb +27 -0
  38. data/lib/cucumber/rails/rspec.rb +1 -1
  39. data/lib/cucumber/rails/test_unit.rb +9 -0
  40. data/lib/cucumber/rails/world.rb +7 -78
  41. data/lib/cucumber/rb_support/rb_dsl.rb +6 -4
  42. data/lib/cucumber/rb_support/rb_group.rb +11 -0
  43. data/lib/cucumber/rb_support/rb_language.rb +8 -2
  44. data/lib/cucumber/rb_support/rb_step_definition.rb +23 -8
  45. data/lib/cucumber/rb_support/rb_transform.rb +35 -0
  46. data/lib/cucumber/rb_support/rb_world.rb +7 -1
  47. data/lib/cucumber/step_match.rb +25 -6
  48. data/lib/cucumber/step_mother.rb +9 -32
  49. data/lib/cucumber/version.rb +1 -1
  50. data/rails_generators/cucumber/templates/cucumber_environment.rb +2 -2
  51. data/rails_generators/cucumber/templates/env.rb +1 -8
  52. data/spec/cucumber/ast/feature_element_spec.rb +24 -23
  53. data/spec/cucumber/ast/scenario_outline_spec.rb +1 -1
  54. data/spec/cucumber/cli/options_spec.rb +5 -14
  55. data/spec/cucumber/core_ext/string_spec.rb +11 -13
  56. data/spec/cucumber/parser/feature_parser_spec.rb +6 -6
  57. data/spec/cucumber/step_mother_spec.rb +41 -38
  58. metadata +11 -8
  59. data/examples/self_test/features/transform_sample.feature +0 -10
  60. data/spec/cucumber/rails/stubs/mini_rails.rb +0 -18
  61. data/spec/cucumber/rails/stubs/test_help.rb +0 -1
  62. data/spec/cucumber/rails/world_spec.rb +0 -16
@@ -1,5 +1,3 @@
1
- require 'cucumber/rb_support/rb_hook'
2
-
3
1
  module Cucumber
4
2
  module RbSupport
5
3
  # This module defines the methods you can use to define pure Ruby
@@ -21,6 +19,10 @@ module Cucumber
21
19
  @rb_language.register_rb_hook(phase, tag_names, proc)
22
20
  end
23
21
 
22
+ def register_rb_transform(regexp, proc)
23
+ @rb_language.register_rb_transform(regexp, proc)
24
+ end
25
+
24
26
  def register_rb_step_definition(regexp, proc)
25
27
  @rb_language.register_rb_step_definition(regexp, proc)
26
28
  end
@@ -71,8 +73,8 @@ module Cucumber
71
73
  # the pattern contains captures then they will be yielded as arguments to the
72
74
  # provided proc. The return value of the proc is consequently yielded to the
73
75
  # step definition.
74
- def Transform(*args, &proc)
75
- StepMother.register_transform(*args, &proc)
76
+ def Transform(regexp, &proc)
77
+ RbDsl.register_rb_transform(regexp, proc)
76
78
  end
77
79
 
78
80
  # Registers a proc that will run after Cucumber is configured. You can register as
@@ -0,0 +1,11 @@
1
+ module Cucumber
2
+ module RbSupport
3
+ class RbGroup
4
+ attr_reader :val, :start
5
+
6
+ def initialize(val, start)
7
+ @val, @start = val, start
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,6 +1,8 @@
1
1
  require 'cucumber/rb_support/rb_dsl'
2
2
  require 'cucumber/rb_support/rb_world'
3
3
  require 'cucumber/rb_support/rb_step_definition'
4
+ require 'cucumber/rb_support/rb_hook'
5
+ require 'cucumber/rb_support/rb_transform'
4
6
 
5
7
  module Cucumber
6
8
  module RbSupport
@@ -82,6 +84,10 @@ module Cucumber
82
84
  add_hook(phase, RbHook.new(self, tag_names, proc))
83
85
  end
84
86
 
87
+ def register_rb_transform(regexp, proc)
88
+ add_transform(RbTransform.new(self, regexp, proc))
89
+ end
90
+
85
91
  def register_rb_step_definition(regexp, proc)
86
92
  add_step_definition(RbStepDefinition.new(self, regexp, proc))
87
93
  end
@@ -98,7 +104,7 @@ module Cucumber
98
104
  protected
99
105
 
100
106
  def load_code_file(code_file)
101
- require code_file # This will cause self.add_step_definition and self.add_hook to be called from RbDsl
107
+ require code_file # This will cause self.add_step_definition, self.add_hook, and self.add_transform to be called from RbDsl
102
108
  end
103
109
 
104
110
  def begin_scenario
@@ -150,4 +156,4 @@ module Cucumber
150
156
  end
151
157
  end
152
158
  end
153
- end
159
+ end
@@ -1,6 +1,7 @@
1
1
  require 'cucumber/step_match'
2
2
  require 'cucumber/core_ext/string'
3
3
  require 'cucumber/core_ext/proc'
4
+ require 'cucumber/rb_support/rb_group'
4
5
 
5
6
  module Cucumber
6
7
  module RbSupport
@@ -23,24 +24,38 @@ module Cucumber
23
24
  end
24
25
  end
25
26
 
26
- attr_reader :proc
27
+ attr_reader :proc, :regexp
27
28
 
28
- def initialize(rb_language, pattern, proc)
29
+ def initialize(rb_language, regexp, proc)
29
30
  raise MissingProc if proc.nil?
30
- if String === pattern
31
- p = pattern.gsub(/\$\w+/, '(.*)') # Replace $var with (.*)
32
- pattern = Regexp.new("^#{p}$")
31
+ if String === regexp
32
+ p = regexp.gsub(/\$\w+/, '(.*)') # Replace $var with (.*)
33
+ regexp = Regexp.new("^#{p}$")
33
34
  end
34
- @rb_language, @regexp, @proc = rb_language, pattern, proc
35
+ @rb_language, @regexp, @proc = rb_language, regexp, proc
35
36
  end
36
37
 
37
- def regexp
38
- @regexp
38
+ def ==(step_definition)
39
+ self.regexp == step_definition.regexp
40
+ end
41
+
42
+ def groups(step_name)
43
+ match = regexp.match(step_name)
44
+ if match
45
+ n = 0
46
+ match.captures.map do |val|
47
+ n += 1
48
+ RbGroup.new(val, match.offset(n)[0])
49
+ end
50
+ else
51
+ nil
52
+ end
39
53
  end
40
54
 
41
55
  def invoke(args)
42
56
  args = args.map{|arg| Ast::PyString === arg ? arg.to_s : arg}
43
57
  begin
58
+ args = @rb_language.execute_transforms(args)
44
59
  @rb_language.current_world.cucumber_instance_exec(true, regexp.inspect, *args, &@proc)
45
60
  rescue Cucumber::ArityMismatchError => e
46
61
  e.backtrace.unshift(self.backtrace_line)
@@ -0,0 +1,35 @@
1
+ module Cucumber
2
+ module RbSupport
3
+ # A Ruby Transform holds a Regexp and a Proc, and is created
4
+ # by calling <tt>Transform in the <tt>support</tt> ruby files.
5
+ # See also RbDsl.
6
+ #
7
+ # Example:
8
+ #
9
+ # Transform /^(\d+) cucumbers$/ do |cucumbers_string|
10
+ # cucumbers_string.to_i
11
+ # end
12
+ #
13
+ class RbTransform
14
+ class MissingProc < StandardError
15
+ def message
16
+ "Transforms must always have a proc with at least one argument"
17
+ end
18
+ end
19
+
20
+ attr_reader :proc, :regexp
21
+
22
+ def initialize(rb_language, pattern, proc)
23
+ raise MissingProc if proc.nil? || proc.arity < 1
24
+ @rb_language, @regexp, @proc = rb_language, Regexp.new(pattern), proc
25
+ end
26
+
27
+ def invoke(arg)
28
+ if matched = regexp.match(arg)
29
+ args = Array(matched.captures.empty? ? arg : matched.captures)
30
+ @rb_language.current_world.cucumber_instance_exec(true, regexp.inspect, *args, &@proc)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -7,6 +7,12 @@ module Cucumber
7
7
  alias_method adverb, :__cucumber_invoke
8
8
  end
9
9
  end
10
+
11
+ # Call a Transform with a string from another Transform definition
12
+ def Transform(arg)
13
+ rb = @__cucumber_step_mother.load_programming_language('rb')
14
+ rb.execute_transforms([arg]).first
15
+ end
10
16
 
11
17
  attr_writer :__cucumber_step_mother
12
18
 
@@ -90,4 +96,4 @@ module Cucumber
90
96
  end
91
97
  end
92
98
  end
93
- end
99
+ end
@@ -1,9 +1,13 @@
1
1
  module Cucumber
2
2
  class StepMatch #:nodoc:
3
- attr_reader :step_definition, :args
3
+ attr_reader :step_definition
4
4
 
5
- def initialize(step_definition, step_name, formatted_step_name, args)
6
- @step_definition, @step_name, @formatted_step_name, @args = step_definition, step_name, formatted_step_name, args
5
+ def initialize(step_definition, step_name, formatted_step_name, groups)
6
+ @step_definition, @step_name, @formatted_step_name, @groups = step_definition, step_name, formatted_step_name, groups
7
+ end
8
+
9
+ def args
10
+ @groups.map{|g| g.val}
7
11
  end
8
12
 
9
13
  def name
@@ -11,13 +15,28 @@ module Cucumber
11
15
  end
12
16
 
13
17
  def invoke(multiline_arg)
14
- all_args = @args.dup
18
+ all_args = args
15
19
  all_args << multiline_arg if multiline_arg
16
20
  @step_definition.invoke(all_args)
17
21
  end
18
22
 
23
+ # Formats the matched arguments of the associated Step. This method
24
+ # is usually called from visitors, which render output.
25
+ #
26
+ # The +format+ can either be a String or a Proc.
27
+ #
28
+ # If it is a String it should be a format string according to
29
+ # <tt>Kernel#sprinf</tt>, for example:
30
+ #
31
+ # '<span class="param">%s</span></tt>'
32
+ #
33
+ # If it is a Proc, it should take one argument and return the formatted
34
+ # argument, for example:
35
+ #
36
+ # lambda { |param| "[#{param}]" }
37
+ #
19
38
  def format_args(format = lambda{|a| a})
20
- @formatted_step_name || @step_definition.format_args(@step_name, format)
39
+ @formatted_step_name || @step_name.gzub(@groups, format)
21
40
  end
22
41
 
23
42
  def file_colon_line
@@ -58,4 +77,4 @@ module Cucumber
58
77
  @step.text_length
59
78
  end
60
79
  end
61
- end
80
+ end
@@ -50,9 +50,7 @@ module Cucumber
50
50
 
51
51
  # This is the meaty part of Cucumber that ties everything together.
52
52
  class StepMother
53
- include Constantize
54
- @@transforms = []
55
- StepArgumentTransform = Struct.new(:pattern, :transformer)
53
+ include Constantize
56
54
  attr_writer :options, :visitor, :log
57
55
 
58
56
  def initialize
@@ -61,32 +59,6 @@ module Cucumber
61
59
  @language_map = {}
62
60
  load_natural_language('en')
63
61
  end
64
-
65
- def self.register_transform(pattern, &transformer)
66
- raise Cucumber::ArityMismatchError.new('Transform must be registered with at least a one-argument block') if !block_given? || transformer.arity < 1
67
- @@transforms.unshift StepArgumentTransform.new(Regexp.new(pattern), transformer.to_proc)
68
- end
69
-
70
- def self.transform_arguments(step_args)
71
- matched = nil
72
- step_args.map do |step_arg|
73
- if transform = @@transforms.detect {|t| matched = t.pattern.match(step_arg) if step_arg.is_a?(String) }
74
- if matched.captures.empty?
75
- unless transform.transformer.arity == 1
76
- raise Cucumber::ArityMismatchError.new("Transforms without Regexp captures only accept a single argument (the step argument)")
77
- end
78
- transform.transformer.call(step_arg)
79
- else
80
- if transform.transformer.arity != matched.captures.size
81
- raise Cucumber::ArityMismatchError.new("Number of arguments in Transform (#{transform.transformer.arity}) does not match number of Regexp captures (#{matched.captures.size})")
82
- end
83
- transform.transformer.call(*matched.captures)
84
- end
85
- else
86
- step_arg
87
- end
88
- end
89
- end
90
62
 
91
63
  def load_plain_text_features(feature_files)
92
64
  features = Ast::Features.new
@@ -215,11 +187,16 @@ module Cucumber
215
187
  end
216
188
 
217
189
  def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
190
+ load_programming_language('rb') if unknown_programming_language?
218
191
  @programming_languages.map do |programming_language|
219
192
  programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
220
193
  end.join("\n")
221
194
  end
222
195
 
196
+ def unknown_programming_language?
197
+ @programming_languages.empty?
198
+ end
199
+
223
200
  def before_and_after(scenario, skip_hooks=false) #:nodoc:
224
201
  before(scenario) unless skip_hooks
225
202
  yield scenario
@@ -263,8 +240,8 @@ module Cucumber
263
240
  @programming_languages.each do |programming_language|
264
241
  programming_language.after_configuration(configuration)
265
242
  end
266
- end
267
-
243
+ end
244
+
268
245
  private
269
246
 
270
247
  # Registers a StepDefinition. This can be a Ruby StepDefintion,
@@ -272,7 +249,7 @@ module Cucumber
272
249
  # contract (API).
273
250
  def register_step_definition(step_definition)
274
251
  step_definitions.each do |already|
275
- raise Redundant.new(already, step_definition) if already.same_regexp?(step_definition.regexp)
252
+ raise Redundant.new(already, step_definition) if already == step_definition
276
253
  end
277
254
  step_definitions << step_definition
278
255
  step_definition
@@ -2,7 +2,7 @@ module Cucumber #:nodoc:
2
2
  class VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 101
5
+ TINY = 102
6
6
  PATCH = nil # Set to nil for official release
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
@@ -18,8 +18,8 @@ config.action_mailer.delivery_method = :test
18
18
  config.gem 'cucumber', :lib => false, :version => '>=<%= cucumber_version %>' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber'))
19
19
  config.gem 'webrat', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
20
20
  <% if framework == :rspec -%>
21
- config.gem 'rspec', :lib => false, :version => '>=1.2.6' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
22
- config.gem 'rspec-rails', :lib => 'spec/rails', :version => '>=1.2.6' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
21
+ config.gem 'rspec', :lib => false, :version => '>=1.2.8' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
22
+ config.gem 'rspec-rails', :lib => false, :version => '>=1.2.7.1' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
23
23
  <% end %>
24
24
  <% if spork? -%>
25
25
  config.gem 'spork', :lib => false, :version => '>=0.5.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/spork'))
@@ -6,19 +6,12 @@ require 'cucumber/rails/world'
6
6
  # Comment out the next line if you don't want Cucumber Unicode support
7
7
  require 'cucumber/formatter/unicode'
8
8
 
9
- # Comment out the next line if you don't want transactions to
10
- # open/roll back around each scenario
11
- Cucumber::Rails.use_transactional_fixtures
12
-
13
- # Comment out the next line if you want Rails' own error handling
14
- # (e.g. rescue_action_in_public / rescue_responses / rescue_from)
15
- Cucumber::Rails.bypass_rescue
16
-
17
9
  require 'webrat'
18
10
  require 'cucumber/webrat/element_locator' # Lets you do table.diff!(element_at('#my_table_or_dl_or_ul_or_ol').to_table)
19
11
 
20
12
  Webrat.configure do |config|
21
13
  config.mode = :rails
14
+ config.open_error_files = false # Set to true if you want error pages to pop up in the browser
22
15
  end
23
16
  <% if framework == :rspec -%>
24
17
 
@@ -3,38 +3,39 @@ require 'cucumber/step_mother'
3
3
  require 'cucumber/ast'
4
4
 
5
5
  module Cucumber
6
- describe FeatureElement do
7
- include FeatureElement
6
+ module Ast
7
+ describe FeatureElement do
8
+ include FeatureElement
8
9
 
9
- describe "with multiline names" do
10
- it "should select the longest line as the text length" do
11
- @keyword = "key"
12
- @name = "short\nvery longer\ntiny"
13
- text_length.should == 11 + Ast::Step::INDENT - 1
14
- end
10
+ describe "with multiline names" do
11
+ it "should select the longest line as the text length" do
12
+ @keyword = "key"
13
+ @name = "short\nvery longer\ntiny"
14
+ text_length.should == 11 + Ast::Step::INDENT - 1
15
+ end
15
16
 
16
- it "should add keyword to first lines length" do
17
- @keyword = "key"
18
- @name = "short\nvery longer\ntiny"
17
+ it "should add keyword to first lines length" do
18
+ @keyword = "key"
19
+ @name = "short\nvery longer\ntiny"
19
20
 
20
- first_line_length.should == (@keyword.jlength) + (first_line_name_length = 5)
21
+ first_line_length.should == (@keyword.jlength) + (first_line_name_length = 5)
22
+ end
21
23
  end
22
- end
23
24
 
24
- describe "with empty name" do
25
- it "should only return the length of the keyword" do
26
- @name = ""
27
- @keyword = "key"
25
+ describe "with empty name" do
26
+ it "should only return the length of the keyword" do
27
+ @name = ""
28
+ @keyword = "key"
28
29
 
29
- text_length.should == 3
30
+ text_length.should == 3
31
+ end
30
32
  end
31
- end
32
33
 
33
- it "should support checking if its name matches a list of regexps" do
34
- @name = 'test'
35
- matches_scenario_names?([/es/]).should be_true
34
+ it "should support checking if its name matches a list of regexps" do
35
+ @name = 'test'
36
+ matches_scenario_names?([/es/]).should be_true
37
+ end
36
38
  end
37
-
38
39
  end
39
40
  end
40
41
 
@@ -64,7 +64,7 @@ module Cucumber
64
64
 
65
65
  it "should pretty print" do
66
66
  require 'cucumber/formatter/pretty'
67
- visitor = Formatter::Pretty.new(@step_mother, STDOUT, {:comment => true, :include_tags => {}, :exclude_tags => {}})
67
+ visitor = Formatter::Pretty.new(@step_mother, STDOUT, {:comment => true, :tag_names => {}})
68
68
  visitor.visit_feature_element(@scenario_outline)
69
69
  end
70
70
  end
@@ -92,11 +92,8 @@ module Cli
92
92
  end
93
93
 
94
94
  context '-t TAGS --tags TAGS' do
95
- it "removes the @ prefix" do
96
- after_parsing('-t @foo,bar') { options[:include_tags].should == {'foo' => nil, 'bar' => nil} }
97
- end
98
95
  it "designates tags prefixed with ~ as tags to be excluded" do
99
- after_parsing('--tags ~@foo,bar') { options[:exclude_tags].should == {'foo' => nil} }
96
+ after_parsing('--tags ~@foo,@bar') { options[:tag_names].should == {'~@foo' => nil, '@bar' => nil} }
100
97
  end
101
98
  end
102
99
 
@@ -160,16 +157,10 @@ module Cli
160
157
  options[:require].should == %w[foo.rb features dog.rb]
161
158
  end
162
159
 
163
- it "combines the include_tags of both" do
164
- given_cucumber_yml_defined_as('baz' => %w[-t bar])
165
- options.parse!(%w[--tags foo -p baz])
166
- options[:include_tags].should == {'foo' => nil, 'bar' => nil}
167
- end
168
-
169
- it "combines the exclude_tags of both" do
170
- given_cucumber_yml_defined_as('baz' => %w[-t ~bar])
171
- options.parse!(%w[--tags ~foo -p baz])
172
- options[:exclude_tags].should == {'foo' => nil, 'bar' => nil}
160
+ it "combines the tag names of both" do
161
+ given_cucumber_yml_defined_as('baz' => %w[-t @bar])
162
+ options.parse!(%w[--tags @foo -p baz])
163
+ options[:tag_names].should == {'@foo' => nil, '@bar' => nil}
173
164
  end
174
165
 
175
166
  it "only takes the paths from the original options, and disgregards the profiles" do