aslakhellesoy-cucumber 0.3.103 → 0.3.104
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 +27 -2
- data/Manifest.txt +10 -4
- data/examples/ramaze/README.textile +7 -0
- data/examples/ramaze/Rakefile +6 -0
- data/examples/ramaze/app.rb +21 -0
- data/examples/ramaze/features/add.feature +11 -0
- data/examples/ramaze/features/step_definitions/add_steps.rb +15 -0
- data/examples/ramaze/features/support/env.rb +32 -0
- data/examples/ramaze/layout/default.html.erb +8 -0
- data/examples/ramaze/view/index.html.erb +5 -0
- data/examples/sinatra/features/support/env.rb +1 -1
- data/features/cucumber_cli.feature +5 -5
- data/features/usage_and_stepdefs_formatter.feature +169 -0
- data/lib/cucumber/ast/step_invocation.rb +7 -0
- data/lib/cucumber/ast/tags.rb +6 -1
- data/lib/cucumber/ast/tree_walker.rb +179 -0
- data/lib/cucumber/cli/options.rb +20 -11
- data/lib/cucumber/formatter/html.rb +0 -2
- data/lib/cucumber/formatter/stepdefs.rb +14 -0
- data/lib/cucumber/formatter/usage.rb +106 -50
- data/lib/cucumber/language_support/language_methods.rb +6 -9
- data/lib/cucumber/rb_support/rb_language.rb +16 -3
- data/lib/cucumber/rb_support/rb_step_definition.rb +7 -1
- data/lib/cucumber/step_match.rb +4 -0
- data/lib/cucumber/step_mother.rb +8 -37
- data/lib/cucumber/version.rb +1 -1
- data/spec/cucumber/ast/background_spec.rb +0 -6
- data/spec/cucumber/ast/tree_walker_spec.rb +11 -0
- data/spec/cucumber/cli/options_spec.rb +12 -0
- data/spec/cucumber/formatter/html_spec.rb +0 -1
- data/spec/cucumber/rb_support/rb_step_definition_spec.rb +0 -9
- data/spec/cucumber/step_mother_spec.rb +13 -34
- metadata +14 -6
- data/features/steps_formatter.feature +0 -26
- data/features/usage.feature +0 -126
- data/lib/cucumber/formatter/profile.rb +0 -78
- data/lib/cucumber/formatters/unicode.rb +0 -7
@@ -0,0 +1,179 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Ast
|
3
|
+
# Walks the AST, executing steps and notifying listeners
|
4
|
+
class TreeWalker
|
5
|
+
attr_accessor :options #:nodoc:
|
6
|
+
attr_reader :step_mother #:nodoc:
|
7
|
+
|
8
|
+
def initialize(step_mother, listeners = [], options = {}, io = STDOUT)
|
9
|
+
@step_mother, @listeners, @options, @io = step_mother, listeners, options, io
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_features(features)
|
13
|
+
broadcast(features) do
|
14
|
+
features.accept(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_feature(feature)
|
19
|
+
broadcast(feature) do
|
20
|
+
feature.accept(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_comment(comment)
|
25
|
+
broadcast(comment) do
|
26
|
+
comment.accept(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_comment_line(comment_line)
|
31
|
+
broadcast(comment_line)
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_tags(tags)
|
35
|
+
broadcast(tags) do
|
36
|
+
tags.accept(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_tag_name(tag_name)
|
41
|
+
broadcast(tag_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_feature_name(name)
|
45
|
+
broadcast(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# +feature_element+ is either Scenario or ScenarioOutline
|
49
|
+
def visit_feature_element(feature_element)
|
50
|
+
broadcast(feature_element) do
|
51
|
+
feature_element.accept(self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_background(background)
|
56
|
+
broadcast(background) do
|
57
|
+
background.accept(self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_background_name(keyword, name, file_colon_line, source_indent)
|
62
|
+
broadcast(keyword, name, file_colon_line, source_indent)
|
63
|
+
end
|
64
|
+
|
65
|
+
def visit_examples_array(examples_array)
|
66
|
+
broadcast(examples_array) do
|
67
|
+
examples_array.accept(self)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def visit_examples(examples)
|
72
|
+
broadcast(examples) do
|
73
|
+
examples.accept(self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def visit_examples_name(keyword, name)
|
78
|
+
broadcast(keyword, name)
|
79
|
+
end
|
80
|
+
|
81
|
+
def visit_outline_table(outline_table)
|
82
|
+
broadcast(outline_table) do
|
83
|
+
outline_table.accept(self)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def visit_scenario_name(keyword, name, file_colon_line, source_indent)
|
88
|
+
broadcast(keyword, name, file_colon_line, source_indent)
|
89
|
+
end
|
90
|
+
|
91
|
+
def visit_steps(steps)
|
92
|
+
broadcast(steps) do
|
93
|
+
steps.accept(self)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def visit_step(step)
|
98
|
+
broadcast(step) do
|
99
|
+
step.accept(self)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
104
|
+
broadcast(keyword, step_match, multiline_arg, status, exception, source_indent, background) do
|
105
|
+
visit_step_name(keyword, step_match, status, source_indent, background)
|
106
|
+
visit_multiline_arg(multiline_arg) if multiline_arg
|
107
|
+
visit_exception(exception, status) if exception
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def visit_step_name(keyword, step_match, status, source_indent, background) #:nodoc:
|
112
|
+
broadcast(keyword, step_match, status, source_indent, background)
|
113
|
+
end
|
114
|
+
|
115
|
+
def visit_multiline_arg(multiline_arg) #:nodoc:
|
116
|
+
broadcast(multiline_arg) do
|
117
|
+
multiline_arg.accept(self)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def visit_exception(exception, status) #:nodoc:
|
122
|
+
broadcast(exception, status)
|
123
|
+
end
|
124
|
+
|
125
|
+
def visit_py_string(string)
|
126
|
+
broadcast(string)
|
127
|
+
end
|
128
|
+
|
129
|
+
def visit_table_row(table_row)
|
130
|
+
broadcast(table_row) do
|
131
|
+
table_row.accept(self)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def visit_table_cell(table_cell)
|
136
|
+
broadcast(table_cell) do
|
137
|
+
table_cell.accept(self)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def visit_table_cell_value(value, status)
|
142
|
+
broadcast(value, status)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Print +announcement+. This method can be called from within StepDefinitions.
|
146
|
+
def announce(announcement)
|
147
|
+
broadcast(announcement)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def broadcast(*args, &block)
|
153
|
+
message = extract_method_name_from(caller)
|
154
|
+
message.gsub!('visit_', '')
|
155
|
+
|
156
|
+
if block_given?
|
157
|
+
send_to_all("before_#{message}", *args)
|
158
|
+
yield if block_given?
|
159
|
+
send_to_all("after_#{message}", *args)
|
160
|
+
else
|
161
|
+
send_to_all(message, *args)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def send_to_all(message, *args)
|
166
|
+
@listeners.each do |listener|
|
167
|
+
if listener.respond_to?(message)
|
168
|
+
listener.__send__(message, *args)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def extract_method_name_from(call_stack)
|
174
|
+
call_stack[0].match(/in `(.*)'/).captures[0]
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -9,13 +9,17 @@ module Cucumber
|
|
9
9
|
'pdf' => ['Cucumber::Formatter::Pdf', "Generates a PDF report. You need to have the\n" +
|
10
10
|
"#{' ' * 51}prawn gem installed. Will pick up logo from\n" +
|
11
11
|
"#{' ' * 51}features/support/logo.png if present."],
|
12
|
-
'profile' => ['Cucumber::Formatter::Profile', 'Prints the 10 slowest steps at the end.'],
|
13
12
|
'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
|
14
13
|
'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
|
15
|
-
'usage' => ['Cucumber::Formatter::Usage',
|
14
|
+
'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" +
|
15
|
+
"#{' ' * 51}The slowest step definitions (with duration) are\n" +
|
16
|
+
"#{' ' * 51}listed first. If --dry-run is used the duration\n" +
|
17
|
+
"#{' ' * 51}is not shown, and step definitions are sorted by\n" +
|
18
|
+
"#{' ' * 51}filename instead."],
|
19
|
+
'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" +
|
20
|
+
"the usage formatter, except that steps are not printed."],
|
16
21
|
'junit' => ['Cucumber::Formatter::Junit', 'Generates a report similar to Ant+JUnit.'],
|
17
|
-
'tag_cloud' => ['Cucumber::Formatter::TagCloud', 'Prints a tag cloud of tag usage.']
|
18
|
-
'steps' => ['Cucumber::Formatter::Steps', 'Prints location of step definitions.']
|
22
|
+
'tag_cloud' => ['Cucumber::Formatter::TagCloud', 'Prints a tag cloud of tag usage.']
|
19
23
|
}
|
20
24
|
max = BUILTIN_FORMATS.keys.map{|s| s.length}.max
|
21
25
|
FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
|
@@ -171,9 +175,9 @@ module Cucumber
|
|
171
175
|
end
|
172
176
|
opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.",
|
173
177
|
"This also omits the loading of your support/env.rb file if it exists.",
|
174
|
-
"Implies --
|
178
|
+
"Implies --no-snippets.") do
|
175
179
|
@options[:dry_run] = true
|
176
|
-
@
|
180
|
+
@options[:snippets] = false
|
177
181
|
end
|
178
182
|
opts.on("-a", "--autoformat DIRECTORY",
|
179
183
|
"Reformats (pretty prints) feature files and write them to DIRECTORY.",
|
@@ -257,12 +261,16 @@ module Cucumber
|
|
257
261
|
attr_reader :options, :profiles, :expanded_args
|
258
262
|
protected :options, :profiles, :expanded_args
|
259
263
|
|
260
|
-
def non_stdout_formats
|
261
|
-
@options[:formats].select {|format, output| output != @out_stream }
|
262
|
-
end
|
263
|
-
|
264
264
|
private
|
265
265
|
|
266
|
+
def non_stdout_formats
|
267
|
+
@options[:formats].select {|format, output| output != @out_stream }
|
268
|
+
end
|
269
|
+
|
270
|
+
def stdout_formats
|
271
|
+
@options[:formats].select {|format, output| output == @out_stream }
|
272
|
+
end
|
273
|
+
|
266
274
|
def extract_environment_variables
|
267
275
|
@args.delete_if do |arg|
|
268
276
|
if arg =~ /^(\w+)=(.*)$/
|
@@ -337,7 +345,8 @@ module Cucumber
|
|
337
345
|
if @options[:formats].empty?
|
338
346
|
@options[:formats] = other_options[:formats]
|
339
347
|
else
|
340
|
-
@options[:formats] += other_options
|
348
|
+
@options[:formats] += other_options[:formats]
|
349
|
+
@options[:formats] = stdout_formats[0..0] + non_stdout_formats
|
341
350
|
end
|
342
351
|
|
343
352
|
self
|
@@ -2,82 +2,138 @@ require 'cucumber/formatter/progress'
|
|
2
2
|
|
3
3
|
module Cucumber
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
class Usage
|
5
|
+
class Usage < Progress
|
7
6
|
include Console
|
8
7
|
|
8
|
+
class StepDefKey
|
9
|
+
attr_reader :regexp_source, :file_colon_line
|
10
|
+
attr_accessor :mean_duration, :status
|
11
|
+
|
12
|
+
def initialize(regexp_source, file_colon_line)
|
13
|
+
@regexp_source, @file_colon_line = regexp_source, file_colon_line
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(o)
|
17
|
+
regexp_source == o.regexp_source && file_colon_line == o.file_colon_line
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
regexp_source.hash + 17*file_colon_line.hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
9
25
|
def initialize(step_mother, io, options)
|
26
|
+
@step_mother = step_mother
|
10
27
|
@io = io
|
11
28
|
@options = options
|
12
|
-
@
|
13
|
-
@all_step_definitions = step_mother.step_definitions.dup
|
14
|
-
@locations = []
|
15
|
-
end
|
16
|
-
|
17
|
-
def after_features(features)
|
18
|
-
print_summary(features)
|
29
|
+
@stepdef_to_match = Hash.new{|h,stepdef_key| h[stepdef_key] = []}
|
19
30
|
end
|
20
31
|
|
21
32
|
def before_step(step)
|
22
33
|
@step = step
|
23
34
|
end
|
24
35
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@
|
36
|
+
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
37
|
+
@step_duration = Time.now
|
38
|
+
end
|
39
|
+
|
40
|
+
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
41
|
+
duration = Time.now - @step_duration
|
42
|
+
if step_match.name.nil? # nil if it's from a scenario outline
|
43
|
+
stepdef_key = StepDefKey.new(step_match.step_definition.regexp_source, step_match.step_definition.file_colon_line)
|
44
|
+
|
45
|
+
@stepdef_to_match[stepdef_key] << {
|
46
|
+
:keyword => keyword,
|
47
|
+
:step_match => step_match,
|
48
|
+
:status => status,
|
49
|
+
:file_colon_line => @step.file_colon_line,
|
50
|
+
:duration => duration
|
51
|
+
}
|
35
52
|
end
|
53
|
+
super
|
36
54
|
end
|
37
55
|
|
38
56
|
def print_summary(features)
|
39
|
-
|
40
|
-
|
41
|
-
sorted_defs.each do |step_definition|
|
42
|
-
step_matches_and_descriptions = @step_definitions[step_definition].sort_by do |step_match_and_description|
|
43
|
-
step_match = step_match_and_description[0]
|
44
|
-
step_match.step_definition.regexp_source
|
45
|
-
end
|
57
|
+
add_unused_stepdefs
|
58
|
+
aggregate_info
|
46
59
|
|
47
|
-
|
60
|
+
if @options[:dry_run]
|
61
|
+
keys = @stepdef_to_match.keys.sort {|a,b| a.regexp_source <=> b.regexp_source}
|
62
|
+
else
|
63
|
+
keys = @stepdef_to_match.keys.sort {|a,b| a.mean_duration <=> b.mean_duration}.reverse
|
64
|
+
end
|
65
|
+
|
66
|
+
keys.each do |stepdef_key|
|
67
|
+
print_step_definition(stepdef_key)
|
48
68
|
|
49
|
-
|
50
|
-
|
69
|
+
if @stepdef_to_match[stepdef_key].any?
|
70
|
+
print_steps(stepdef_key)
|
71
|
+
else
|
72
|
+
@io.puts(" " + format_string("NOT MATCHED BY ANY STEPS", :failed))
|
51
73
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
74
|
+
end
|
75
|
+
@io.puts
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_step_definition(stepdef_key)
|
80
|
+
@io.print format_string(sprintf("%.7f", stepdef_key.mean_duration), :skipped) + " " unless @options[:dry_run]
|
81
|
+
@io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
|
82
|
+
if @options[:source]
|
83
|
+
indent = max_length - stepdef_key.regexp_source.jlength
|
84
|
+
line_comment = " # #{stepdef_key.file_colon_line}".indent(indent)
|
85
|
+
@io.print(format_string(line_comment, :comment))
|
86
|
+
end
|
87
|
+
@io.puts
|
88
|
+
end
|
89
|
+
|
90
|
+
def print_steps(stepdef_key)
|
91
|
+
@stepdef_to_match[stepdef_key].each do |step|
|
92
|
+
@io.print " "
|
93
|
+
@io.print format_string(sprintf("%.7f", step[:duration]), :skipped) + " " unless @options[:dry_run]
|
94
|
+
@io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
|
95
|
+
if @options[:source]
|
96
|
+
indent = max_length - (step[:keyword].jlength + step[:step_match].format_args.jlength)
|
97
|
+
line_comment = " # #{step[:file_colon_line]}".indent(indent)
|
98
|
+
@io.print(format_string(line_comment, :comment))
|
63
99
|
end
|
64
|
-
|
100
|
+
@io.puts
|
65
101
|
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def max_length
|
105
|
+
[max_stepdef_length, max_step_length].compact.max
|
106
|
+
end
|
66
107
|
|
67
|
-
|
108
|
+
def max_stepdef_length
|
109
|
+
@stepdef_to_match.keys.flatten.map{|key| key.regexp_source.jlength}.max
|
68
110
|
end
|
69
111
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
112
|
+
def max_step_length
|
113
|
+
@stepdef_to_match.values.flatten.map do |step|
|
114
|
+
step[:keyword].jlength + step[:step_match].format_args.jlength
|
115
|
+
end.max
|
116
|
+
end
|
73
117
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
118
|
+
def aggregate_info
|
119
|
+
@stepdef_to_match.each do |key, steps|
|
120
|
+
if steps.empty?
|
121
|
+
key.status = :skipped
|
122
|
+
key.mean_duration = 0
|
123
|
+
else
|
124
|
+
key.status = Ast::StepInvocation.worst_status(steps.map{|step| step[:status]})
|
125
|
+
total_duration = steps.inject(0) {|sum, step| step[:duration] + sum}
|
126
|
+
key.mean_duration = total_duration / steps.length
|
78
127
|
end
|
79
128
|
end
|
80
129
|
end
|
130
|
+
|
131
|
+
def add_unused_stepdefs
|
132
|
+
@step_mother.unmatched_step_definitions.each do |step_definition|
|
133
|
+
stepdef_key = StepDefKey.new(step_definition.regexp_source, step_definition.file_colon_line)
|
134
|
+
@stepdef_to_match[stepdef_key] = []
|
135
|
+
end
|
136
|
+
end
|
81
137
|
end
|
82
138
|
end
|
83
|
-
end
|
139
|
+
end
|