cucumber 0.3.102 → 0.3.103

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 (47) hide show
  1. data/History.txt +18 -0
  2. data/Manifest.txt +6 -2
  3. data/examples/java/README.textile +3 -3
  4. data/examples/self_test/features/sample.feature +1 -1
  5. data/examples/self_test/features/search_sample.feature +1 -1
  6. data/features/custom_formatter.feature +4 -4
  7. data/features/steps_formatter.feature +2 -1
  8. data/lib/cucumber/ast.rb +1 -0
  9. data/lib/cucumber/ast/table.rb +4 -4
  10. data/lib/cucumber/ast/tree_walker.rb +185 -0
  11. data/lib/cucumber/ast/visitor.rb +2 -106
  12. data/lib/cucumber/cli/configuration.rb +28 -28
  13. data/lib/cucumber/cli/language_help_formatter.rb +5 -7
  14. data/lib/cucumber/cli/main.rb +3 -3
  15. data/lib/cucumber/core_ext/string.rb +0 -22
  16. data/lib/cucumber/formatter/html.rb +203 -113
  17. data/lib/cucumber/formatter/junit.rb +29 -23
  18. data/lib/cucumber/formatter/pdf.rb +74 -69
  19. data/lib/cucumber/formatter/pretty.rb +93 -78
  20. data/lib/cucumber/formatter/profile.rb +2 -2
  21. data/lib/cucumber/formatter/progress.rb +16 -10
  22. data/lib/cucumber/formatter/rerun.rb +4 -5
  23. data/lib/cucumber/formatter/steps.rb +6 -7
  24. data/lib/cucumber/formatter/tag_cloud.rb +7 -6
  25. data/lib/cucumber/formatter/usage.rb +7 -10
  26. data/lib/cucumber/language_support/step_definition_methods.rb +4 -4
  27. data/lib/cucumber/rails/action_controller.rb +1 -1
  28. data/lib/cucumber/rails/active_record.rb +27 -14
  29. data/lib/cucumber/rb_support/rb_language.rb +5 -0
  30. data/lib/cucumber/rb_support/rb_step_definition.rb +9 -16
  31. data/lib/cucumber/rb_support/regexp_argument_matcher.rb +21 -0
  32. data/lib/cucumber/step_argument.rb +9 -0
  33. data/lib/cucumber/step_match.rb +24 -5
  34. data/lib/cucumber/version.rb +1 -1
  35. data/rails_generators/cucumber/templates/env.rb +14 -0
  36. data/spec/cucumber/ast/background_spec.rb +1 -2
  37. data/spec/cucumber/ast/scenario_outline_spec.rb +3 -2
  38. data/spec/cucumber/ast/scenario_spec.rb +1 -1
  39. data/spec/cucumber/ast/tree_walker_spec.rb +18 -0
  40. data/spec/cucumber/formatter/html_spec.rb +221 -2
  41. data/spec/cucumber/formatter/progress_spec.rb +9 -4
  42. data/spec/cucumber/parser/feature_parser_spec.rb +31 -27
  43. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +18 -0
  44. data/spec/cucumber/step_match_spec.rb +40 -0
  45. metadata +8 -4
  46. data/lib/cucumber/rb_support/rb_group.rb +0 -11
  47. data/spec/cucumber/core_ext/string_spec.rb +0 -41
@@ -12,13 +12,13 @@ module Cucumber
12
12
  @step_definition_durations = Hash.new { |h,step_definition| h[step_definition] = [] }
13
13
  end
14
14
 
15
- def visit_step(step)
15
+ def step(step)
16
16
  @step_duration = Time.now
17
17
  @step = step
18
18
  super
19
19
  end
20
20
 
21
- def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
21
+ def step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
22
22
  duration = Time.now - @step_duration
23
23
  super
24
24
 
@@ -3,33 +3,39 @@ require 'cucumber/formatter/console'
3
3
  module Cucumber
4
4
  module Formatter
5
5
  # The formatter used for <tt>--format progress</tt>
6
- class Progress < Ast::Visitor
6
+ class Progress
7
7
  include Console
8
+ attr_reader :step_mother
8
9
 
9
10
  def initialize(step_mother, io, options)
10
- super(step_mother)
11
- @io = io
12
- @options = options
11
+ @step_mother, @io, @options = step_mother, io, options
13
12
  end
14
13
 
15
- def visit_features(features)
16
- super
14
+ def after_features(features)
17
15
  @io.puts
18
16
  @io.puts
19
17
  print_summary(features)
20
18
  end
21
19
 
22
- def visit_feature_element(feature_element)
20
+ def before_feature_element(feature_element)
23
21
  record_tag_occurrences(feature_element, @options)
24
- super
25
22
  end
26
23
 
27
- def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
24
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
28
25
  progress(status)
29
26
  @status = status
30
27
  end
28
+
29
+ def before_outline_table(outline_table)
30
+ @outline_table = outline_table
31
+ end
32
+
33
+ def after_outline_table(outline_table)
34
+ @outline_table = nil
35
+ end
31
36
 
32
- def visit_table_cell_value(value, status)
37
+ def table_cell_value(value, status)
38
+ return unless @outline_table
33
39
  status ||= @status
34
40
  progress(status) unless table_header_cell?(status)
35
41
  end
@@ -10,16 +10,15 @@ module Cucumber
10
10
  # This formatter is used by AutoTest - it will use the output to decide what
11
11
  # to run the next time, simply passing the output string on the command line.
12
12
  #
13
- class Rerun < Ast::Visitor
13
+ class Rerun
14
14
  def initialize(step_mother, io, options)
15
- super(step_mother)
16
15
  @io = io
17
16
  @options = options
18
17
  @file_names = []
19
18
  @file_colon_lines = Hash.new{|h,k| h[k] = []}
20
19
  end
21
20
 
22
- def visit_features(features)
21
+ def features(features)
23
22
  super
24
23
  files = @file_names.uniq.map do |file|
25
24
  lines = @file_colon_lines[file]
@@ -28,7 +27,7 @@ module Cucumber
28
27
  @io.puts files.join(' ')
29
28
  end
30
29
 
31
- def visit_feature_element(feature_element)
30
+ def feature_element(feature_element)
32
31
  @rerun = false
33
32
  super
34
33
  if @rerun
@@ -38,7 +37,7 @@ module Cucumber
38
37
  end
39
38
  end
40
39
 
41
- def visit_step_name(keyword, step_match, status, source_indent, background)
40
+ def step_name(keyword, step_match, status, source_indent, background)
42
41
  @rerun = true if [:failed].index(status)
43
42
  end
44
43
  end
@@ -1,16 +1,15 @@
1
1
  module Cucumber
2
2
  module Formatter
3
3
  # The formatter used for <tt>--format steps</tt>
4
- class Steps < Ast::Visitor
4
+ class Steps
5
5
 
6
6
  def initialize(step_mother, io, options)
7
- super(step_mother)
8
7
  @io = io
9
8
  @options = options
10
9
  @step_definition_files = collect_steps(step_mother)
11
10
  end
12
11
 
13
- def visit_features(features)
12
+ def after_features(features)
14
13
  print_summary
15
14
  end
16
15
 
@@ -23,9 +22,9 @@ module Cucumber
23
22
 
24
23
  sources = @step_definition_files[step_definition_file]
25
24
  source_indent = source_indent(sources)
26
- sources.sort.each do |file_colon_line, regexp|
27
- @io.print "#{regexp}".indent(2)
28
- @io.print " # #{file_colon_line}".indent(source_indent - regexp.size)
25
+ sources.sort.each do |file_colon_line, regexp_source|
26
+ @io.print regexp_source.indent(2)
27
+ @io.print " # #{file_colon_line}".indent(source_indent - regexp_source.jlength)
29
28
  @io.puts
30
29
  end
31
30
  @io.puts
@@ -37,7 +36,7 @@ module Cucumber
37
36
  def collect_steps(step_mother)
38
37
  step_mother.step_definitions.inject({}) do |step_definitions, step_definition|
39
38
  step_definitions[step_definition.file] ||= []
40
- step_definitions[step_definition.file] << [ step_definition.file_colon_line, step_definition.regexp.inspect ]
39
+ step_definitions[step_definition.file] << [ step_definition.file_colon_line, step_definition.regexp_source ]
41
40
  step_definitions
42
41
  end
43
42
  end
@@ -2,27 +2,28 @@ module Cucumber
2
2
  module Formatter
3
3
  # The formatter used for <tt>--format tag_cloud</tt>
4
4
  # Custom formatter that prints a tag cloud as a table.
5
- class TagCloud < Cucumber::Ast::Visitor
5
+ class TagCloud
6
6
  def initialize(step_mother, io, options)
7
- super(step_mother)
8
7
  @io = io
9
8
  @options = options
10
9
  @counts = Hash.new{|h,k| h[k] = 0}
11
10
  end
12
11
 
13
- def visit_features(features)
14
- super
12
+ def after_features(features)
15
13
  print_summary(features)
16
14
  end
17
15
 
18
- def visit_tag_name(tag_name)
16
+ def tag_name(tag_name)
19
17
  @counts[tag_name] += 1
20
18
  end
19
+
20
+ private
21
21
 
22
22
  def print_summary(features)
23
23
  matrix = @counts.to_a.sort{|paira, pairb| paira[0] <=> pairb[0]}.transpose
24
24
  table = Cucumber::Ast::Table.new(matrix)
25
- Cucumber::Formatter::Pretty.new(@step_mother, @io, {}).visit_multiline_arg(table)
25
+ formatter = Cucumber::Formatter::Pretty.new(@step_mother, @io, {})
26
+ Cucumber::Ast::TreeWalker.new(@step_mother, [formatter], {}).visit_multiline_arg(table)
26
27
  end
27
28
  end
28
29
  end
@@ -3,11 +3,10 @@ require 'cucumber/formatter/progress'
3
3
  module Cucumber
4
4
  module Formatter
5
5
  # The formatter used for <tt>--format usage</tt>
6
- class Usage < Ast::Visitor
6
+ class Usage
7
7
  include Console
8
8
 
9
9
  def initialize(step_mother, io, options)
10
- super(step_mother)
11
10
  @io = io
12
11
  @options = options
13
12
  @step_definitions = Hash.new { |h,step_definition| h[step_definition] = [] }
@@ -15,17 +14,15 @@ module Cucumber
15
14
  @locations = []
16
15
  end
17
16
 
18
- def visit_features(features)
19
- super
17
+ def after_features(features)
20
18
  print_summary(features)
21
19
  end
22
20
 
23
- def visit_step(step)
21
+ def before_step(step)
24
22
  @step = step
25
- super
26
23
  end
27
24
 
28
- def visit_step_name(keyword, step_match, status, source_indent, background)
25
+ def step_name(keyword, step_match, status, source_indent, background)
29
26
  if step_match.step_definition
30
27
  location = @step.file_colon_line
31
28
  return if @locations.index(location)
@@ -44,7 +41,7 @@ module Cucumber
44
41
  sorted_defs.each do |step_definition|
45
42
  step_matches_and_descriptions = @step_definitions[step_definition].sort_by do |step_match_and_description|
46
43
  step_match = step_match_and_description[0]
47
- step_match.step_definition.regexp.inspect
44
+ step_match.step_definition.regexp_source
48
45
  end
49
46
 
50
47
  step_matches = step_matches_and_descriptions.map{|step_match_and_description| step_match_and_description[0]}
@@ -55,7 +52,7 @@ module Cucumber
55
52
  lengths << step_definition.text_length
56
53
  max_length = lengths.max
57
54
 
58
- @io.print step_definition.regexp.inspect
55
+ @io.print step_definition.regexp_source
59
56
  @io.puts format_string(" # #{step_definition.file_colon_line}".indent(max_length - step_definition.text_length), :comment)
60
57
  da = step_matches_and_descriptions.map do |step_match_and_description|
61
58
  step_match = step_match_and_description[0]
@@ -76,7 +73,7 @@ module Cucumber
76
73
 
77
74
  @io.puts format_string("(::) UNUSED (::)", :failed)
78
75
  @all_step_definitions.each do |step_definition|
79
- @io.print format_string(step_definition.regexp.inspect, :failed)
76
+ @io.print format_string(step_definition.regexp_source, :failed)
80
77
  @io.puts format_string(" # #{step_definition.file_colon_line}".indent(max_length - step_definition.text_length), :comment)
81
78
  end
82
79
  end
@@ -4,19 +4,19 @@ module Cucumber
4
4
  module LanguageSupport
5
5
  module StepDefinitionMethods
6
6
  def step_match(name_to_match, name_to_report)
7
- if(groups = groups(name_to_match))
8
- StepMatch.new(self, name_to_match, name_to_report, groups)
7
+ if(arguments = arguments_from(name_to_match))
8
+ StepMatch.new(self, name_to_match, name_to_report, arguments)
9
9
  else
10
10
  nil
11
11
  end
12
12
  end
13
13
 
14
14
  def backtrace_line
15
- "#{file_colon_line}:in `#{regexp.inspect}'"
15
+ "#{file_colon_line}:in `#{regexp_source}'"
16
16
  end
17
17
 
18
18
  def text_length
19
- regexp.inspect.jlength
19
+ regexp_source.jlength
20
20
  end
21
21
  end
22
22
  end
@@ -28,6 +28,6 @@ rescue NameError # Failsafe was introduced in Rails 2.3.2
28
28
  end
29
29
  end
30
30
 
31
- Before('@allow_rescue') do
31
+ Before('@allow-rescue') do
32
32
  ActionController::Base.allow_rescue = true
33
33
  end
@@ -1,24 +1,37 @@
1
1
  if defined?(ActiveRecord::Base)
2
- Cucumber::Rails::World.use_transactional_fixtures = true
3
-
2
+ Before('~@no-txn') do
3
+ @__cucumber_use_txn = Cucumber::Rails::World.use_transactional_fixtures
4
+ Cucumber::Rails::World.use_transactional_fixtures = true
5
+ end
6
+
7
+ Before('@no-txn') do
8
+ @__cucumber_use_txn = Cucumber::Rails::World.use_transactional_fixtures
9
+ Cucumber::Rails::World.use_transactional_fixtures = false
10
+ end
11
+
4
12
  Before do
5
- @__cucumber_ar_connection = ActiveRecord::Base.connection
6
- if @__cucumber_ar_connection.respond_to?(:increment_open_transactions)
7
- @__cucumber_ar_connection.increment_open_transactions
8
- else
9
- ActiveRecord::Base.__send__(:increment_open_transactions)
13
+ if Cucumber::Rails::World.use_transactional_fixtures
14
+ @__cucumber_ar_connection = ActiveRecord::Base.connection
15
+ if @__cucumber_ar_connection.respond_to?(:increment_open_transactions)
16
+ @__cucumber_ar_connection.increment_open_transactions
17
+ else
18
+ ActiveRecord::Base.__send__(:increment_open_transactions)
19
+ end
20
+ @__cucumber_ar_connection.begin_db_transaction
10
21
  end
11
- @__cucumber_ar_connection.begin_db_transaction
12
22
  ActionMailer::Base.deliveries = [] if defined?(ActionMailer::Base)
13
23
  end
14
-
24
+
15
25
  After do
16
- @__cucumber_ar_connection.rollback_db_transaction
17
- if @__cucumber_ar_connection.respond_to?(:decrement_open_transactions)
18
- @__cucumber_ar_connection.decrement_open_transactions
19
- else
20
- ActiveRecord::Base.__send__(:decrement_open_transactions)
26
+ if Cucumber::Rails::World.use_transactional_fixtures
27
+ @__cucumber_ar_connection.rollback_db_transaction
28
+ if @__cucumber_ar_connection.respond_to?(:decrement_open_transactions)
29
+ @__cucumber_ar_connection.decrement_open_transactions
30
+ else
31
+ ActiveRecord::Base.__send__(:decrement_open_transactions)
32
+ end
21
33
  end
34
+ Cucumber::Rails::World.use_transactional_fixtures = @__cucumber_use_txn
22
35
  end
23
36
  else
24
37
  module Cucumber::Rails
@@ -3,6 +3,7 @@ require 'cucumber/rb_support/rb_world'
3
3
  require 'cucumber/rb_support/rb_step_definition'
4
4
  require 'cucumber/rb_support/rb_hook'
5
5
  require 'cucumber/rb_support/rb_transform'
6
+ require 'cucumber/rb_support/regexp_argument_matcher'
6
7
 
7
8
  module Cucumber
8
9
  module RbSupport
@@ -55,6 +56,10 @@ module Cucumber
55
56
  end
56
57
  end
57
58
 
59
+ def arguments_from(regexp, step_name)
60
+ @regexp_argument_matcher.arguments_from(regexp, step_name)
61
+ end
62
+
58
63
  def snippet_text(step_keyword, step_name, multiline_arg_class = nil)
59
64
  escaped = Regexp.escape(step_name).gsub('\ ', ' ').gsub('/', '\/')
60
65
  escaped = escaped.gsub(PARAM_PATTERN, ESCAPED_PARAM_PATTERN)
@@ -1,7 +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
+ require 'cucumber/rb_support/regexp_argument_matcher'
5
5
 
6
6
  module Cucumber
7
7
  module RbSupport
@@ -24,8 +24,6 @@ module Cucumber
24
24
  end
25
25
  end
26
26
 
27
- attr_reader :proc, :regexp
28
-
29
27
  def initialize(rb_language, regexp, proc)
30
28
  raise MissingProc if proc.nil?
31
29
  if String === regexp
@@ -35,28 +33,23 @@ module Cucumber
35
33
  @rb_language, @regexp, @proc = rb_language, regexp, proc
36
34
  end
37
35
 
36
+ def regexp_source
37
+ @regexp.inspect
38
+ end
39
+
38
40
  def ==(step_definition)
39
- self.regexp == step_definition.regexp
41
+ regexp_source == step_definition.regexp_source
40
42
  end
41
43
 
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
44
+ def arguments_from(step_name)
45
+ RegexpArgumentMatcher.arguments_from(@regexp, step_name)
53
46
  end
54
47
 
55
48
  def invoke(args)
56
49
  args = args.map{|arg| Ast::PyString === arg ? arg.to_s : arg}
57
50
  begin
58
51
  args = @rb_language.execute_transforms(args)
59
- @rb_language.current_world.cucumber_instance_exec(true, regexp.inspect, *args, &@proc)
52
+ @rb_language.current_world.cucumber_instance_exec(true, regexp_source, *args, &@proc)
60
53
  rescue Cucumber::ArityMismatchError => e
61
54
  e.backtrace.unshift(self.backtrace_line)
62
55
  raise e
@@ -0,0 +1,21 @@
1
+ require 'cucumber/step_argument'
2
+
3
+ module Cucumber
4
+ module RbSupport
5
+ class RegexpArgumentMatcher
6
+ def self.arguments_from(regexp, step_name)
7
+ match = regexp.match(step_name)
8
+ if match
9
+ n = 0
10
+ match.captures.map do |val|
11
+ n += 1
12
+ start = match.offset(n)[0]
13
+ StepArgument.new(val, start)
14
+ end
15
+ else
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Cucumber
2
+ class StepArgument
3
+ attr_reader :val, :pos
4
+
5
+ def initialize(val, pos)
6
+ @val, @pos = val, pos
7
+ end
8
+ end
9
+ end
@@ -2,12 +2,12 @@ module Cucumber
2
2
  class StepMatch #:nodoc:
3
3
  attr_reader :step_definition
4
4
 
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
5
+ def initialize(step_definition, step_name, formatted_step_name, step_arguments)
6
+ @step_definition, @step_name, @formatted_step_name, @step_arguments = step_definition, step_name, formatted_step_name, step_arguments
7
7
  end
8
8
 
9
9
  def args
10
- @groups.map{|g| g.val}
10
+ @step_arguments.map{|g| g.val}
11
11
  end
12
12
 
13
13
  def name
@@ -35,8 +35,8 @@ module Cucumber
35
35
  #
36
36
  # lambda { |param| "[#{param}]" }
37
37
  #
38
- def format_args(format = lambda{|a| a})
39
- @formatted_step_name || @step_name.gzub(@groups, format)
38
+ def format_args(format = lambda{|a| a}, &proc)
39
+ @formatted_step_name || replace_arguments(@step_name, @step_arguments, format, &proc)
40
40
  end
41
41
 
42
42
  def file_colon_line
@@ -50,6 +50,25 @@ module Cucumber
50
50
  def text_length
51
51
  @step_definition.text_length
52
52
  end
53
+
54
+ def replace_arguments(string, step_arguments, format, &proc)
55
+ s = string.dup
56
+ offset = 0
57
+ step_arguments.each do |step_argument|
58
+ next if step_argument.pos.nil?
59
+ replacement = if block_given?
60
+ proc.call(step_argument.val)
61
+ elsif Proc === format
62
+ format.call(step_argument.val)
63
+ else
64
+ format % step_argument.val
65
+ end
66
+
67
+ s[step_argument.pos + offset, step_argument.val.jlength] = replacement
68
+ offset += replacement.length - step_argument.val.jlength
69
+ end
70
+ s
71
+ end
53
72
  end
54
73
 
55
74
  class NoStepMatch #:nodoc: