cucumber 0.3.93 → 0.3.94

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +67 -38
  2. data/Manifest.txt +8 -0
  3. data/Rakefile +1 -1
  4. data/cucumber.yml +2 -2
  5. data/examples/python/features/fibonacci.feature +19 -0
  6. data/examples/python/features/step_definitions/fib_steps.rb +7 -0
  7. data/examples/python/features/support/env.rb +21 -0
  8. data/examples/python/lib/fib.py +7 -0
  9. data/examples/self_test/features/tags_sample.feature +17 -0
  10. data/examples/sinatra/README.textile +13 -0
  11. data/features/cucumber_cli.feature +85 -3
  12. data/features/custom_formatter.feature +2 -2
  13. data/features/html_formatter/a.html +1 -1
  14. data/features/junit_formatter.feature +19 -12
  15. data/features/step_definitions/cucumber_steps.rb +8 -15
  16. data/features/support/env.rb +4 -5
  17. data/gem_tasks/contributors.rake +4 -0
  18. data/lib/cucumber/ast/feature.rb +13 -0
  19. data/lib/cucumber/ast/feature_element.rb +8 -4
  20. data/lib/cucumber/ast/features.rb +6 -1
  21. data/lib/cucumber/ast/table.rb +13 -6
  22. data/lib/cucumber/ast/tags.rb +9 -1
  23. data/lib/cucumber/cli/configuration.rb +3 -15
  24. data/lib/cucumber/cli/main.rb +25 -8
  25. data/lib/cucumber/cli/options.rb +53 -31
  26. data/lib/cucumber/filter.rb +4 -3
  27. data/lib/cucumber/formatter/ansicolor.rb +42 -9
  28. data/lib/cucumber/formatter/console.rb +38 -6
  29. data/lib/cucumber/formatter/html.rb +2 -8
  30. data/lib/cucumber/formatter/junit.rb +65 -24
  31. data/lib/cucumber/formatter/ordered_xml_markup.rb +24 -0
  32. data/lib/cucumber/formatter/pretty.rb +9 -4
  33. data/lib/cucumber/formatter/progress.rb +7 -1
  34. data/lib/cucumber/rake/task.rb +3 -3
  35. data/lib/cucumber/step_mother.rb +3 -1
  36. data/lib/cucumber/version.rb +1 -1
  37. data/rails_generators/cucumber/templates/cucumber.rake +18 -6
  38. data/spec/cucumber/ast/scenario_outline_spec.rb +2 -2
  39. data/spec/cucumber/ast/table_spec.rb +5 -0
  40. data/spec/cucumber/cli/configuration_spec.rb +2 -1
  41. data/spec/cucumber/cli/options_spec.rb +11 -6
  42. metadata +10 -2
@@ -2,8 +2,9 @@ module Cucumber
2
2
  class Filter
3
3
  def initialize(lines, options)
4
4
  @lines = lines
5
- @include_tags = options[:include_tags] || []
6
- @exclude_tags = options[:exclude_tags] || []
5
+
6
+ @include_tags = options[:include_tags] ? options[:include_tags].keys : []
7
+ @exclude_tags = options[:exclude_tags] ? options[:exclude_tags].keys : []
7
8
  @name_regexps = options[:name_regexps] || []
8
9
  end
9
10
 
@@ -14,7 +15,7 @@ module Cucumber
14
15
  end
15
16
 
16
17
  def accept_example?(syntax_node, outline)
17
- (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
+ (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
19
  (matches_names?(syntax_node) || outline_matches_names?(outline))
19
20
  end
20
21
 
@@ -60,15 +60,6 @@ module Cucumber
60
60
  module ANSIColor
61
61
  include Term::ANSIColor
62
62
 
63
- # Not supported in Term::ANSIColor
64
- def grey(m)
65
- if ::Term::ANSIColor.coloring?
66
- "\e[90m#{m}\e[0m"
67
- else
68
- m
69
- end
70
- end
71
-
72
63
  ALIASES = Hash.new do |h,k|
73
64
  if k.to_s =~ /(.*)_param/
74
65
  h[$1] + ',bold'
@@ -105,6 +96,48 @@ module Cucumber
105
96
  eval(code)
106
97
  end
107
98
  end
99
+
100
+ def self.define_grey
101
+ begin
102
+ gem 'genki-ruby-terminfo'
103
+ require 'terminfo'
104
+ puts TermInfo.default_object.tigetnum("colors")
105
+ case TermInfo.default_object.tigetnum("colors")
106
+ when 0
107
+ raise "Your terminal doesn't support colours"
108
+ when 1
109
+ ::Term::ANSIColor.coloring = false
110
+ alias grey white
111
+ when 2..8
112
+ alias grey white
113
+ else
114
+ define_real_grey
115
+ end
116
+ rescue Exception => e
117
+ if e.class.name == 'TermInfo::TermInfoError'
118
+ STDERR.puts "*** WARNING ***"
119
+ STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
120
+ STDERR.puts "Try setting it to TERM=xterm-256color to get grey colour in output"
121
+ STDERR.puts "\n"
122
+ alias grey white
123
+ else
124
+ define_real_grey
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.define_real_grey
130
+ def grey(m)
131
+ if ::Term::ANSIColor.coloring?
132
+ "\e[90m#{m}\e[0m"
133
+ else
134
+ m
135
+ end
136
+ end
137
+ end
138
+
139
+ define_grey
140
+
108
141
  end
109
142
  end
110
143
  end
@@ -6,7 +6,7 @@ module Cucumber
6
6
  module Console
7
7
  extend ANSIColor
8
8
  include Duration
9
-
9
+
10
10
  FORMATS = Hash.new{|hash, format| hash[format] = method(format).to_proc}
11
11
 
12
12
  def format_step(keyword, step_match, status, source_indent)
@@ -59,9 +59,9 @@ module Cucumber
59
59
  end
60
60
 
61
61
  def print_stats(features)
62
-
62
+
63
63
  @failures = step_mother.scenarios(:failed).select { |s| s.is_a?(Cucumber::Ast::Scenario) }
64
-
64
+
65
65
  if !@failures.empty?
66
66
  @io.puts format_string("Failing Scenarios:", :failed)
67
67
  @failures.each do |failure|
@@ -70,7 +70,7 @@ module Cucumber
70
70
  end
71
71
  @io.puts
72
72
  end
73
-
73
+
74
74
  @io.print dump_count(step_mother.scenarios.length, "scenario")
75
75
  print_status_counts{|status| step_mother.scenarios(status)}
76
76
 
@@ -81,7 +81,7 @@ module Cucumber
81
81
 
82
82
  @io.flush
83
83
  end
84
-
84
+
85
85
  def print_exception(e, status, indent)
86
86
  @io.puts(format_string("#{e.message} (#{e.class})\n#{e.backtrace.join("\n")}".indent(indent), status))
87
87
  end
@@ -114,6 +114,29 @@ module Cucumber
114
114
  end
115
115
  end
116
116
 
117
+ def print_tag_limit_warnings(options)
118
+ return unless tag_limit_breached?(options, @tag_occurences)
119
+ @io.puts
120
+ @io.puts format_string("Failed due to exceeding the tag limit", :failed)
121
+ options[:include_tags].each do |tag_name, limit|
122
+ tag_frequnecy = @tag_occurences[tag_name].size
123
+ if limit && tag_frequnecy > limit
124
+ @io.puts format_string("@#{tag_name} occurred:#{tag_frequnecy} limit:#{limit}", :failed)
125
+ @tag_occurences[tag_name].each {|location| @io.puts format_string(" #{location}", :failed)}
126
+ @io.flush
127
+ end
128
+ end
129
+ end
130
+
131
+ def record_tag_occurrences(feature_element, options)
132
+ @tag_occurences ||= Hash.new{|k,v| k[v] = []}
133
+ options[:include_tags].each do |tag_name, limit|
134
+ if feature_element.tag_count(tag_name) > 0
135
+ @tag_occurences[tag_name] << feature_element.file_colon_line
136
+ end
137
+ end
138
+ end
139
+
117
140
  def announce(announcement)
118
141
  @io.puts
119
142
  @io.puts(format_string(announcement, :tag))
@@ -144,6 +167,15 @@ module Cucumber
144
167
  raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
145
168
  fmt
146
169
  end
170
+
171
+ def tag_limit_breached?(options, tag_occurences)
172
+ return if tag_occurences.nil?
173
+ tag_limit_breached = false
174
+ options[:include_tags].each do |tag_name, limit|
175
+ tag_limit_breached ||= limit && (tag_occurences[tag_name].size > limit)
176
+ end
177
+ tag_limit_breached
178
+ end
147
179
  end
148
180
  end
149
- end
181
+ end
@@ -1,10 +1,4 @@
1
- require 'erb'
2
- begin
3
- require 'builder'
4
- rescue LoadError
5
- gem 'builder'
6
- require 'builder'
7
- end
1
+ require 'cucumber/formatter/ordered_xml_markup'
8
2
  require 'cucumber/formatter/duration'
9
3
 
10
4
  module Cucumber
@@ -20,7 +14,7 @@ module Cucumber
20
14
  end
21
15
 
22
16
  def create_builder(io)
23
- Builder::XmlMarkup.new(:target => io, :indent => 0)
17
+ OrderedXmlMarkup.new(:target => io, :indent => 0)
24
18
  end
25
19
 
26
20
  def visit_features(features)
@@ -1,9 +1,4 @@
1
- begin
2
- require 'builder'
3
- rescue LoadError
4
- gem 'builder'
5
- require 'builder'
6
- end
1
+ require 'cucumber/formatter/ordered_xml_markup'
7
2
 
8
3
  module Cucumber
9
4
  module Formatter
@@ -18,16 +13,18 @@ module Cucumber
18
13
 
19
14
  def visit_feature(feature)
20
15
  @failures = @errors = @tests = 0
21
- @builder = Builder::XmlMarkup.new( :indent => 2 )
16
+ @builder = OrderedXmlMarkup.new( :indent => 2 )
17
+ @time = 0
22
18
  super
23
19
 
24
- @testsuite = Builder::XmlMarkup.new( :indent => 2 )
20
+ @testsuite = OrderedXmlMarkup.new( :indent => 2 )
25
21
  @testsuite.instruct!
26
22
  @testsuite.testsuite(
27
23
  :failures => @failures,
28
24
  :errors => @errors,
29
25
  :tests => @tests,
30
- :name => @feature_name ) do
26
+ :time => "%.6f" % @time,
27
+ :name => @feature_name ) do
31
28
  @testsuite << @builder.target!
32
29
  end
33
30
 
@@ -36,40 +33,84 @@ module Cucumber
36
33
  File.open(feature_filename, 'w') { |file| file.write(@testsuite.target!) }
37
34
  end
38
35
 
36
+ def visit_background(name)
37
+ @in_background = true
38
+ super
39
+ @in_background = false
40
+ end
41
+
39
42
  def visit_feature_name(name)
40
43
  lines = name.split(/\r?\n/)
41
44
  @feature_name = lines[0].sub(/Feature\:/, '').strip
42
45
  end
43
46
 
44
47
  def visit_scenario_name(keyword, name, file_colon_line, source_indent)
45
- @scenario = name
48
+ scenario_name = name.strip
49
+ scenario_name = "Unnamed scenario" if name == ""
50
+ @scenario = scenario_name
51
+ @outline = keyword.include?('Scenario Outline')
52
+ @output = "Scenario#{ " outline" if @outline}: #{@scenario}\n\n"
46
53
  end
47
54
 
48
55
  def visit_steps(steps)
49
- @steps_failed = false
56
+ return if @in_background
57
+ start = Time.now
50
58
  super
51
- @failures += 1 if @steps_failed
52
- @tests += 1
59
+ duration = Time.now - start
60
+ unless @outline
61
+ if steps.failed?
62
+ steps.each { |step| @output += "#{step.keyword} #{step.name}\n" }
63
+ @output += "\nMessage:\n"
64
+ end
65
+ build_testcase(duration, steps.status, steps.exception)
66
+ end
67
+ end
68
+
69
+ def visit_outline_table(outline_table)
70
+ @header_row = true
71
+ super(outline_table)
53
72
  end
54
73
 
55
- def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
56
- step_name = keyword + " " + step_match.format_args(lambda{|param| "*#{param}*"})
57
- @builder.testcase(:classname => "#{@feature_name}.#{@scenario}", :name => step_name) do
58
- if status != :passed
59
- @builder.failure(:message => step_name) do
60
- @builder.text!(format_exception(exception)) if exception
74
+ def visit_table_row(table_row)
75
+ if @outline
76
+ start = Time.now
77
+ super(table_row)
78
+ duration = Time.now - start
79
+ unless @header_row
80
+ name_suffix = " (outline example : #{table_row.name})"
81
+ if table_row.failed?
82
+ @output += "Example row: #{table_row.name}\n"
83
+ @output += "\nMessage:\n"
61
84
  end
62
- @steps_failed = true
85
+ build_testcase(duration, table_row.status, table_row.exception, name_suffix)
63
86
  end
87
+ else
88
+ super(table_row)
64
89
  end
90
+ @header_row = false
65
91
  end
66
92
 
67
93
  private
68
94
 
69
- def format_exception(exception)
70
- (["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
71
- end
72
- end
95
+ def build_testcase(duration, status, exception = nil, suffix = "")
96
+ @time += duration
97
+ classname = "#{@feature_name}.#{@scenario}"
98
+ name = "#{@scenario}#{suffix}"
99
+ @builder.testcase(:classname => classname, :name => name, :time => "%.6f" % duration) do
100
+ if status != :passed
101
+ @builder.failure(:message => "#{status.to_s} #{name}", :type => status.to_s) do
102
+ @builder.text! @output
103
+ @builder.text!(format_exception(exception)) if exception
104
+ end
105
+ @failures += 1
106
+ end
107
+ end
108
+ @tests += 1
109
+ end
73
110
 
111
+ def format_exception(exception)
112
+ (["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
113
+ end
114
+ end
74
115
  end
75
116
  end
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'builder'
3
+ rescue LoadError
4
+ gem 'builder'
5
+ require 'builder'
6
+ end
7
+
8
+ module Cucumber
9
+ module Formatter
10
+ # Emits attributes ordered alphabetically, so that we can predicatbly test output.
11
+ class OrderedXmlMarkup < Builder::XmlMarkup
12
+ def _insert_attributes(attrs, order=[])
13
+ return if attrs.nil?
14
+ keys = attrs.keys.map{|k| k.to_s}
15
+ keys.sort!
16
+ keys.reverse! if (attrs.keys - [:version, :encoding] == []) #HACK to ensure the 'version' attribute is first in xml declaration.
17
+ keys.each do |k|
18
+ v = attrs[k.to_sym] || attrs[k]
19
+ @target << %{ #{k}="#{_attr_value(v)}"} if v
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -48,14 +48,14 @@ module Cucumber
48
48
  end
49
49
 
50
50
  def visit_comment_line(comment_line)
51
- @io.puts(comment_line.indent(@indent))
51
+ @io.puts(comment_line.indent(@indent))
52
52
  @io.flush
53
53
  end
54
54
 
55
55
  def visit_tags(tags)
56
56
  tags.accept(self)
57
57
  if @indent == 1
58
- @io.puts
58
+ @io.puts
59
59
  @io.flush
60
60
  end
61
61
  end
@@ -74,6 +74,7 @@ module Cucumber
74
74
  end
75
75
 
76
76
  def visit_feature_element(feature_element)
77
+ record_tag_occurrences(feature_element, @options)
77
78
  @indent = 2
78
79
  @scenario_indent = 2
79
80
  super
@@ -160,7 +161,7 @@ module Cucumber
160
161
  super
161
162
  @io.puts
162
163
  if table_row.exception && !@exceptions.index(table_row.exception)
163
- print_exception(table_row.exception, :failed, @indent)
164
+ print_exception(table_row.exception, :failed, @indent)
164
165
  end
165
166
  end
166
167
 
@@ -187,7 +188,10 @@ module Cucumber
187
188
  end
188
189
 
189
190
  private
190
-
191
+ def cell_prefix(status)
192
+ @prefixes[status]
193
+ end
194
+
191
195
  def cell_prefix(status)
192
196
  @prefixes[status]
193
197
  end
@@ -196,6 +200,7 @@ module Cucumber
196
200
  print_stats(features)
197
201
  print_snippets(@options)
198
202
  print_passing_wip(@options)
203
+ print_tag_limit_warnings(@options)
199
204
  end
200
205
 
201
206
  end
@@ -18,6 +18,11 @@ module Cucumber
18
18
  print_summary(features)
19
19
  end
20
20
 
21
+ def visit_feature_element(feature_element)
22
+ record_tag_occurrences(feature_element, @options)
23
+ super
24
+ end
25
+
21
26
  def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
22
27
  progress(status)
23
28
  @status = status
@@ -36,6 +41,7 @@ module Cucumber
36
41
  print_stats(features)
37
42
  print_snippets(@options)
38
43
  print_passing_wip(@options)
44
+ print_tag_limit_warnings(@options)
39
45
  end
40
46
 
41
47
  CHARS = {
@@ -51,7 +57,7 @@ module Cucumber
51
57
  @io.print(format_string(char, status))
52
58
  @io.flush
53
59
  end
54
-
60
+
55
61
  def table_header_cell?(status)
56
62
  status == :skipped_param
57
63
  end
@@ -8,8 +8,8 @@ module Cucumber
8
8
  #
9
9
  # Cucumber::Rake::Task.new
10
10
  #
11
- # This will create a task named 'features' described as 'Run Features with
12
- # Cucumber'. It will use steps from 'features/**/*.rb' and features in 'features/**/*.feature'.
11
+ # This will create a task named 'cucumber' described as 'Run Cucumber features'.
12
+ # It will use steps from 'features/**/*.rb' and features in 'features/**/*.feature'.
13
13
  #
14
14
  # To further configure the task, you can pass a block:
15
15
  #
@@ -148,7 +148,7 @@ module Cucumber
148
148
  end
149
149
 
150
150
  # Define Cucumber Rake task
151
- def initialize(task_name = "features", desc = "Run Features with Cucumber")
151
+ def initialize(task_name = "cucumber", desc = "Run Cucumber features")
152
152
  @task_name, @desc = task_name, desc
153
153
  @fork = true
154
154
  @libs = ['lib']