aslakhellesoy-cucumber 0.3.5 → 0.3.6.1
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 +24 -0
- data/Manifest.txt +5 -2
- data/examples/i18n/fr/features/addition.feature +4 -4
- data/features/after_block_exceptions.feature +97 -0
- data/features/after_step_block_exceptions.feature +99 -0
- data/features/background.feature +47 -2
- data/features/custom_formatter.feature +1 -1
- data/features/step_definitions/cucumber_steps.rb +8 -0
- data/features/work_in_progress.feature +146 -0
- data/lib/cucumber.rb +5 -0
- data/lib/cucumber/ast/background.rb +3 -3
- data/lib/cucumber/ast/outline_table.rb +51 -4
- data/lib/cucumber/ast/scenario.rb +9 -3
- data/lib/cucumber/ast/scenario_outline.rb +4 -0
- data/lib/cucumber/ast/step.rb +3 -3
- data/lib/cucumber/ast/step_invocation.rb +13 -4
- data/lib/cucumber/cli/configuration.rb +54 -23
- data/lib/cucumber/cli/main.rb +6 -2
- data/lib/cucumber/formatter/console.rb +9 -0
- data/lib/cucumber/formatter/junit.rb +1 -1
- data/lib/cucumber/formatter/pretty.rb +7 -2
- data/lib/cucumber/formatter/progress.rb +1 -0
- data/lib/cucumber/formatter/tag_cloud.rb +27 -0
- data/lib/cucumber/languages.yml +60 -60
- data/lib/cucumber/rails/world.rb +16 -3
- data/lib/cucumber/rake/task.rb +2 -2
- data/lib/cucumber/step_match.rb +3 -2
- data/lib/cucumber/step_mother.rb +29 -4
- data/lib/cucumber/version.rb +2 -2
- data/rails_generators/cucumber/templates/cucumber.rake +1 -1
- data/rails_generators/feature/feature_generator.rb +1 -1
- data/spec/cucumber/cli/configuration_spec.rb +0 -3
- data/spec/cucumber/formatter/color_io_spec.rb +1 -0
- data/spec/cucumber/formatter/progress_spec.rb +1 -0
- metadata +6 -4
- data/examples/self_test/features/support/tag_count_formatter.rb +0 -25
- data/lib/cucumber/formatter.rb +0 -1
data/lib/cucumber.rb
CHANGED
@@ -14,6 +14,7 @@ module Cucumber
|
|
14
14
|
KEYWORD_KEYS = %w{name native encoding feature background scenario scenario_outline examples given when then but}
|
15
15
|
|
16
16
|
class << self
|
17
|
+
# The currently active language
|
17
18
|
attr_reader :lang
|
18
19
|
|
19
20
|
def load_language(lang) #:nodoc:
|
@@ -38,6 +39,10 @@ module Cucumber
|
|
38
39
|
def keyword_hash(lang=@lang)
|
39
40
|
LANGUAGES[lang]
|
40
41
|
end
|
42
|
+
|
43
|
+
def scenario_keyword
|
44
|
+
keyword_hash['scenario'].split('|')[0] + ':'
|
45
|
+
end
|
41
46
|
|
42
47
|
def alias_step_definitions(lang) #:nodoc:
|
43
48
|
keywords = %w{given when then and but}.map{|keyword| keyword_hash(lang)[keyword].split('|')}
|
@@ -28,15 +28,15 @@ module Cucumber
|
|
28
28
|
visitor.step_mother.before(hook_context)
|
29
29
|
visitor.visit_steps(@step_invocations)
|
30
30
|
@failed = @step_invocations.detect{|step_invocation| step_invocation.exception}
|
31
|
-
visitor.step_mother.after(hook_context) if @failed
|
31
|
+
visitor.step_mother.after(hook_context) if @failed || @feature_elements.empty?
|
32
32
|
end
|
33
33
|
|
34
34
|
def accept_hook?(hook)
|
35
35
|
if hook_context != self
|
36
36
|
hook_context.accept_hook?(hook)
|
37
37
|
else
|
38
|
-
# We have no scenarios
|
39
|
-
|
38
|
+
# We have no scenarios, just ask our feature
|
39
|
+
@feature.accept_hook?(hook)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -10,7 +10,11 @@ module Cucumber
|
|
10
10
|
|
11
11
|
def accept(visitor)
|
12
12
|
cells_rows.each_with_index do |row, n|
|
13
|
-
visitor.
|
13
|
+
if(visitor.options[:expand])
|
14
|
+
row.accept(visitor)
|
15
|
+
else
|
16
|
+
visitor.visit_table_row(row)
|
17
|
+
end
|
14
18
|
end
|
15
19
|
nil
|
16
20
|
end
|
@@ -35,8 +39,14 @@ module Cucumber
|
|
35
39
|
cells_rows[1..-1]
|
36
40
|
end
|
37
41
|
|
42
|
+
def visit_scenario_name(visitor, row)
|
43
|
+
@scenario_outline.visit_scenario_name(visitor, row)
|
44
|
+
end
|
45
|
+
|
38
46
|
class ExampleCells < Cells
|
47
|
+
|
39
48
|
def create_step_invocations!(scenario_outline)
|
49
|
+
@scenario_outline = scenario_outline
|
40
50
|
@step_invocations = scenario_outline.step_invocations(self)
|
41
51
|
end
|
42
52
|
|
@@ -47,6 +57,10 @@ module Cucumber
|
|
47
57
|
end
|
48
58
|
|
49
59
|
def accept(visitor)
|
60
|
+
visitor.options[:expand] ? accept_expand(visitor) : accept_plain(visitor)
|
61
|
+
end
|
62
|
+
|
63
|
+
def accept_plain(visitor)
|
50
64
|
if header?
|
51
65
|
@cells.each do |cell|
|
52
66
|
cell.status = :skipped_param
|
@@ -62,6 +76,22 @@ module Cucumber
|
|
62
76
|
@cells.each do |cell|
|
63
77
|
visitor.visit_table_cell(cell)
|
64
78
|
end
|
79
|
+
|
80
|
+
visitor.visit_exception(@scenario_exception, :failed) if @scenario_exception
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def accept_expand(visitor)
|
86
|
+
if header?
|
87
|
+
else
|
88
|
+
visitor.step_mother.before_and_after(self) do
|
89
|
+
@table.visit_scenario_name(visitor, self)
|
90
|
+
@step_invocations.each do |step_invocation|
|
91
|
+
step_invocation.invoke(visitor.step_mother, visitor.options)
|
92
|
+
@exception ||= step_invocation.exception
|
93
|
+
step_invocation.visit_step_result(visitor)
|
94
|
+
end
|
65
95
|
end
|
66
96
|
end
|
67
97
|
end
|
@@ -69,22 +99,39 @@ module Cucumber
|
|
69
99
|
def accept_hook?(hook)
|
70
100
|
@table.accept_hook?(hook)
|
71
101
|
end
|
72
|
-
|
102
|
+
|
103
|
+
def exception
|
104
|
+
@exception || @scenario_exception
|
105
|
+
end
|
106
|
+
|
107
|
+
def fail!(exception)
|
108
|
+
@scenario_exception = exception
|
109
|
+
end
|
110
|
+
|
73
111
|
# Returns true if one or more steps failed
|
74
112
|
def failed?
|
75
|
-
@step_invocations.failed?
|
113
|
+
@step_invocations.failed? || !!@scenario_exception
|
76
114
|
end
|
77
115
|
|
78
116
|
# Returns true if all steps passed
|
79
117
|
def passed?
|
80
|
-
|
118
|
+
!failed?
|
81
119
|
end
|
82
120
|
|
83
121
|
# Returns the status
|
84
122
|
def status
|
123
|
+
return :failed if @scenario_exception
|
85
124
|
@step_invocations.status
|
86
125
|
end
|
87
126
|
|
127
|
+
def backtrace_line
|
128
|
+
@scenario_outline.backtrace_line(name, line)
|
129
|
+
end
|
130
|
+
|
131
|
+
def name
|
132
|
+
"| #{@cells.collect{|c| c.value }.join(' | ')} |"
|
133
|
+
end
|
134
|
+
|
88
135
|
private
|
89
136
|
|
90
137
|
def header?
|
@@ -28,25 +28,31 @@ module Cucumber
|
|
28
28
|
visitor.step_mother.before_and_after(self, skip) do
|
29
29
|
visitor.visit_steps(@steps)
|
30
30
|
end
|
31
|
+
visitor.visit_exception(@exception, :failed) if @exception
|
31
32
|
end
|
32
33
|
|
33
34
|
# Returns true if one or more steps failed
|
34
35
|
def failed?
|
35
|
-
@steps.failed?
|
36
|
+
@steps.failed? || !!@exception
|
37
|
+
end
|
38
|
+
|
39
|
+
def fail!(exception)
|
40
|
+
@exception = exception
|
36
41
|
end
|
37
42
|
|
38
43
|
# Returns true if all steps passed
|
39
44
|
def passed?
|
40
|
-
|
45
|
+
!failed?
|
41
46
|
end
|
42
47
|
|
43
48
|
# Returns the first exception (if any)
|
44
49
|
def exception
|
45
|
-
@steps.exception
|
50
|
+
@exception || @steps.exception
|
46
51
|
end
|
47
52
|
|
48
53
|
# Returns the status
|
49
54
|
def status
|
55
|
+
return :failed if @exception
|
50
56
|
@steps.status
|
51
57
|
end
|
52
58
|
|
@@ -61,6 +61,10 @@ module Cucumber
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
def visit_scenario_name(visitor, row)
|
65
|
+
visitor.visit_scenario_name(Cucumber.scenario_keyword, row.name, file_colon_line(row.line), source_indent(first_line_length))
|
66
|
+
end
|
67
|
+
|
64
68
|
def to_sexp
|
65
69
|
sexp = [:scenario_outline, @keyword, @name]
|
66
70
|
comment = @comment.to_sexp
|
data/lib/cucumber/ast/step.rb
CHANGED
@@ -51,7 +51,7 @@ module Cucumber
|
|
51
51
|
step_match = visitor.step_mother.step_match(name, @name) rescue nil
|
52
52
|
return step_match if step_match
|
53
53
|
end
|
54
|
-
NoStepMatch.new(self)
|
54
|
+
NoStepMatch.new(self, @name)
|
55
55
|
end
|
56
56
|
|
57
57
|
def to_sexp
|
@@ -62,8 +62,8 @@ module Cucumber
|
|
62
62
|
@feature_element.source_indent(text_length)
|
63
63
|
end
|
64
64
|
|
65
|
-
def text_length
|
66
|
-
@keyword.jlength +
|
65
|
+
def text_length(name=@name)
|
66
|
+
@keyword.jlength + name.jlength + INDENT # Add indent as steps get indented more than scenarios
|
67
67
|
end
|
68
68
|
|
69
69
|
def backtrace_line
|
@@ -20,7 +20,11 @@ module Cucumber
|
|
20
20
|
|
21
21
|
def accept(visitor)
|
22
22
|
invoke(visitor.step_mother, visitor.options)
|
23
|
-
|
23
|
+
visit_step_result(visitor)
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_step_result(visitor)
|
27
|
+
visitor.visit_step_result(keyword, @step_match, @multiline_arg, @status, @exception, source_indent, @background)
|
24
28
|
end
|
25
29
|
|
26
30
|
def invoke(step_mother, options)
|
@@ -30,6 +34,7 @@ module Cucumber
|
|
30
34
|
begin
|
31
35
|
step_mother.current_world.__cucumber_current_step = self if step_mother.current_world # Nil in Pure Java
|
32
36
|
@step_match.invoke(step_mother.current_world, @multiline_arg)
|
37
|
+
step_mother.after_step
|
33
38
|
status!(:passed)
|
34
39
|
rescue Pending => e
|
35
40
|
failed(e, false)
|
@@ -51,11 +56,11 @@ module Cucumber
|
|
51
56
|
rescue Undefined => e
|
52
57
|
failed(e, true)
|
53
58
|
status!(:undefined)
|
54
|
-
@step_match = NoStepMatch.new(@step)
|
59
|
+
@step_match = NoStepMatch.new(@step, @name)
|
55
60
|
rescue Ambiguous => e
|
56
61
|
failed(e, false)
|
57
62
|
status!(:failed)
|
58
|
-
@step_match = NoStepMatch.new(@step)
|
63
|
+
@step_match = NoStepMatch.new(@step, @name)
|
59
64
|
end
|
60
65
|
step_mother.step_visited(self)
|
61
66
|
end
|
@@ -85,8 +90,12 @@ module Cucumber
|
|
85
90
|
end
|
86
91
|
end
|
87
92
|
|
93
|
+
def source_indent
|
94
|
+
@step.feature_element.source_indent(text_length)
|
95
|
+
end
|
96
|
+
|
88
97
|
def text_length
|
89
|
-
@step.text_length
|
98
|
+
@step.text_length(@name)
|
90
99
|
end
|
91
100
|
|
92
101
|
def keyword
|
@@ -3,7 +3,15 @@ module Cucumber
|
|
3
3
|
class YmlLoadError < StandardError; end
|
4
4
|
|
5
5
|
class Configuration
|
6
|
-
|
6
|
+
BUILTIN_FORMATS = {
|
7
|
+
'html' => 'Cucumber::Formatter::Html',
|
8
|
+
'pretty' => 'Cucumber::Formatter::Pretty',
|
9
|
+
'profile' => 'Cucumber::Formatter::Profile',
|
10
|
+
'progress' => 'Cucumber::Formatter::Progress',
|
11
|
+
'rerun' => 'Cucumber::Formatter::Rerun',
|
12
|
+
'usage' => 'Cucumber::Formatter::Usage',
|
13
|
+
'junit' => 'Cucumber::Formatter::Junit'
|
14
|
+
}
|
7
15
|
DEFAULT_FORMAT = 'pretty'
|
8
16
|
|
9
17
|
attr_reader :paths
|
@@ -57,12 +65,15 @@ module Cucumber
|
|
57
65
|
end
|
58
66
|
opts.on("-f FORMAT", "--format FORMAT",
|
59
67
|
"How to format features (Default: #{DEFAULT_FORMAT})",
|
60
|
-
"Available formats: #{
|
61
|
-
"
|
62
|
-
"
|
63
|
-
"
|
64
|
-
"
|
65
|
-
"
|
68
|
+
"Available formats: #{BUILTIN_FORMATS.keys.sort.join(", ")}",
|
69
|
+
"FORMAT can also be the fully qualified class name of",
|
70
|
+
"your own custom formatter. If the class isn't loaded,",
|
71
|
+
"Cucumber will attempt to require a file with a relative",
|
72
|
+
"file name that is the underscore name of the class name.",
|
73
|
+
"Example: --format Foo::BarZap -> Cucumber will look for",
|
74
|
+
"foo/bar_zap.rb. You can place the file with this relative",
|
75
|
+
"path underneath your features/support directory or anywhere",
|
76
|
+
"on Ruby's LOAD_PATH, for example in a Ruby gem.") do |v|
|
66
77
|
@options[:formats][v] = @out_stream
|
67
78
|
@active_format = v
|
68
79
|
end
|
@@ -132,12 +143,18 @@ module Cucumber
|
|
132
143
|
opts.on("-S", "--strict", "Fail if there are any undefined steps.") do
|
133
144
|
@options[:strict] = true
|
134
145
|
end
|
146
|
+
opts.on("-w", "--wip", "Fail if there are any passing scenarios.") do
|
147
|
+
@options[:wip] = true
|
148
|
+
end
|
135
149
|
opts.on("-v", "--verbose", "Show the files and features loaded.") do
|
136
150
|
@options[:verbose] = true
|
137
151
|
end
|
138
152
|
opts.on("-g", "--guess", "Guess best match for Ambiguous steps.") do
|
139
153
|
@options[:guess] = true
|
140
154
|
end
|
155
|
+
opts.on("-x", "--expand", "Expand Scenario Outline Tables in output.") do
|
156
|
+
@options[:expand] = true
|
157
|
+
end
|
141
158
|
opts.on("--no-diff", "Disable diff output on failing expectations.") do
|
142
159
|
@options[:diff_enabled] = false
|
143
160
|
end
|
@@ -156,6 +173,8 @@ module Cucumber
|
|
156
173
|
@options[:snippets] = true if !@quiet && @options[:snippets].nil?
|
157
174
|
@options[:source] = true if !@quiet && @options[:source].nil?
|
158
175
|
|
176
|
+
raise("You can't use both --strict and --wip") if @options[:strict] && @options[:wip]
|
177
|
+
|
159
178
|
# Whatever is left after option parsing is the FILE arguments
|
160
179
|
@paths += args
|
161
180
|
end
|
@@ -168,6 +187,10 @@ module Cucumber
|
|
168
187
|
@options[:strict]
|
169
188
|
end
|
170
189
|
|
190
|
+
def wip?
|
191
|
+
@options[:wip]
|
192
|
+
end
|
193
|
+
|
171
194
|
def guess?
|
172
195
|
@options[:guess]
|
173
196
|
end
|
@@ -223,14 +246,8 @@ module Cucumber
|
|
223
246
|
end
|
224
247
|
|
225
248
|
def formatter_class(format)
|
226
|
-
|
227
|
-
|
228
|
-
when 'pretty' then Formatter::Pretty
|
229
|
-
when 'profile' then Formatter::Profile
|
230
|
-
when 'progress' then Formatter::Progress
|
231
|
-
when 'rerun' then Formatter::Rerun
|
232
|
-
when 'usage' then Formatter::Usage
|
233
|
-
when 'junit' then Formatter::JUnit
|
249
|
+
if(builtin = BUILTIN_FORMATS[format])
|
250
|
+
constantize(builtin)
|
234
251
|
else
|
235
252
|
constantize(format)
|
236
253
|
end
|
@@ -271,14 +288,28 @@ module Cucumber
|
|
271
288
|
end
|
272
289
|
|
273
290
|
def constantize(camel_cased_word)
|
274
|
-
|
275
|
-
|
291
|
+
begin
|
292
|
+
names = camel_cased_word.split('::')
|
293
|
+
names.shift if names.empty? || names.first.empty?
|
276
294
|
|
277
|
-
|
278
|
-
|
279
|
-
|
295
|
+
constant = Object
|
296
|
+
names.each do |name|
|
297
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
298
|
+
end
|
299
|
+
constant
|
300
|
+
rescue NameError
|
301
|
+
require underscore(camel_cased_word)
|
302
|
+
retry
|
280
303
|
end
|
281
|
-
|
304
|
+
end
|
305
|
+
|
306
|
+
# Snagged from active_support
|
307
|
+
def underscore(camel_cased_word)
|
308
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
309
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
310
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
311
|
+
tr("-", "_").
|
312
|
+
downcase
|
282
313
|
end
|
283
314
|
|
284
315
|
def parse_args_from_profile(profile)
|
@@ -295,10 +326,10 @@ Defined profiles in cucumber.yml:
|
|
295
326
|
|
296
327
|
case(args_from_yml)
|
297
328
|
when String
|
298
|
-
raise "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '
|
329
|
+
raise "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/
|
299
330
|
args_from_yml = args_from_yml.split(' ')
|
300
331
|
when Array
|
301
|
-
raise "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '
|
332
|
+
raise "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml.empty?
|
302
333
|
else
|
303
334
|
raise "The '#{profile}' profile in cucumber.yml was a #{args_from_yml.class}. It must be a String or Array"
|
304
335
|
end
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -2,7 +2,7 @@ require 'optparse'
|
|
2
2
|
require 'cucumber'
|
3
3
|
require 'ostruct'
|
4
4
|
require 'cucumber/parser'
|
5
|
-
require 'cucumber/formatter'
|
5
|
+
require 'cucumber/formatter/color_io'
|
6
6
|
require 'cucumber/cli/language_help_formatter'
|
7
7
|
require 'cucumber/cli/configuration'
|
8
8
|
|
@@ -40,8 +40,12 @@ module Cucumber
|
|
40
40
|
step_mother.visitor = visitor # Needed to support World#announce
|
41
41
|
visitor.visit_features(features)
|
42
42
|
|
43
|
-
failure =
|
43
|
+
failure = if configuration.wip?
|
44
|
+
step_mother.scenarios(:passed).any?
|
45
|
+
else
|
46
|
+
step_mother.scenarios(:failed).any? ||
|
44
47
|
(configuration.strict? && step_mother.steps(:undefined).any?)
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
def load_plain_text_features
|
@@ -85,6 +85,15 @@ module Cucumber
|
|
85
85
|
@io.flush
|
86
86
|
end
|
87
87
|
|
88
|
+
def print_passing_wip(options)
|
89
|
+
return unless options[:wip]
|
90
|
+
passed = step_mother.scenarios(:passed)
|
91
|
+
if passed.any?
|
92
|
+
@io.puts "\nThe --wip switch was used, so I didn't expect anything to pass. These scenarios passed:"
|
93
|
+
print_elements(passed, :passed, "scenarios")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
88
97
|
def announce(announcement)
|
89
98
|
@io.puts
|
90
99
|
@io.puts(format_string(announcement, :tag))
|