cucumber 0.3.93 → 0.3.94
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.
- data/History.txt +67 -38
- data/Manifest.txt +8 -0
- data/Rakefile +1 -1
- data/cucumber.yml +2 -2
- data/examples/python/features/fibonacci.feature +19 -0
- data/examples/python/features/step_definitions/fib_steps.rb +7 -0
- data/examples/python/features/support/env.rb +21 -0
- data/examples/python/lib/fib.py +7 -0
- data/examples/self_test/features/tags_sample.feature +17 -0
- data/examples/sinatra/README.textile +13 -0
- data/features/cucumber_cli.feature +85 -3
- data/features/custom_formatter.feature +2 -2
- data/features/html_formatter/a.html +1 -1
- data/features/junit_formatter.feature +19 -12
- data/features/step_definitions/cucumber_steps.rb +8 -15
- data/features/support/env.rb +4 -5
- data/gem_tasks/contributors.rake +4 -0
- data/lib/cucumber/ast/feature.rb +13 -0
- data/lib/cucumber/ast/feature_element.rb +8 -4
- data/lib/cucumber/ast/features.rb +6 -1
- data/lib/cucumber/ast/table.rb +13 -6
- data/lib/cucumber/ast/tags.rb +9 -1
- data/lib/cucumber/cli/configuration.rb +3 -15
- data/lib/cucumber/cli/main.rb +25 -8
- data/lib/cucumber/cli/options.rb +53 -31
- data/lib/cucumber/filter.rb +4 -3
- data/lib/cucumber/formatter/ansicolor.rb +42 -9
- data/lib/cucumber/formatter/console.rb +38 -6
- data/lib/cucumber/formatter/html.rb +2 -8
- data/lib/cucumber/formatter/junit.rb +65 -24
- data/lib/cucumber/formatter/ordered_xml_markup.rb +24 -0
- data/lib/cucumber/formatter/pretty.rb +9 -4
- data/lib/cucumber/formatter/progress.rb +7 -1
- data/lib/cucumber/rake/task.rb +3 -3
- data/lib/cucumber/step_mother.rb +3 -1
- data/lib/cucumber/version.rb +1 -1
- data/rails_generators/cucumber/templates/cucumber.rake +18 -6
- data/spec/cucumber/ast/scenario_outline_spec.rb +2 -2
- data/spec/cucumber/ast/table_spec.rb +5 -0
- data/spec/cucumber/cli/configuration_spec.rb +2 -1
- data/spec/cucumber/cli/options_spec.rb +11 -6
- metadata +10 -2
data/lib/cucumber/filter.rb
CHANGED
@@ -2,8 +2,9 @@ module Cucumber
|
|
2
2
|
class Filter
|
3
3
|
def initialize(lines, options)
|
4
4
|
@lines = lines
|
5
|
-
|
6
|
-
@
|
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 '
|
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
|
-
|
17
|
+
OrderedXmlMarkup.new(:target => io, :indent => 0)
|
24
18
|
end
|
25
19
|
|
26
20
|
def visit_features(features)
|
@@ -1,9 +1,4 @@
|
|
1
|
-
|
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 =
|
16
|
+
@builder = OrderedXmlMarkup.new( :indent => 2 )
|
17
|
+
@time = 0
|
22
18
|
super
|
23
19
|
|
24
|
-
@testsuite =
|
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
|
-
:
|
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
|
-
|
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
|
-
|
56
|
+
return if @in_background
|
57
|
+
start = Time.now
|
50
58
|
super
|
51
|
-
|
52
|
-
@
|
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
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
data/lib/cucumber/rake/task.rb
CHANGED
@@ -8,8 +8,8 @@ module Cucumber
|
|
8
8
|
#
|
9
9
|
# Cucumber::Rake::Task.new
|
10
10
|
#
|
11
|
-
# This will create a task named '
|
12
|
-
#
|
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 = "
|
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']
|