cucumber 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +44 -1
- data/Manifest.txt +11 -1
- data/bin/cucumber +11 -1
- data/examples/cs/Rakefile +1 -1
- data/examples/cs/features/addition.feature +5 -5
- data/examples/cs/features/step_definitons/calculator_steps.rb +2 -2
- data/examples/dos_line_endings/Rakefile +1 -1
- data/examples/i18n/ar/Rakefile +1 -1
- data/examples/i18n/bg/Rakefile +1 -1
- data/examples/i18n/cat/Rakefile +6 -0
- data/examples/i18n/cat/features/step_definitons/calculator_steps.rb +21 -0
- data/examples/i18n/cat/features/suma.feature +16 -0
- data/examples/i18n/cat/lib/calculadora.rb +16 -0
- data/examples/i18n/da/Rakefile +1 -1
- data/examples/i18n/de/Rakefile +1 -1
- data/examples/i18n/en-lol/Rakefile +1 -1
- data/examples/i18n/en/Rakefile +1 -1
- data/examples/i18n/es/Rakefile +1 -1
- data/examples/i18n/et/Rakefile +1 -1
- data/examples/i18n/fi/Rakefile +1 -1
- data/examples/i18n/fr/Rakefile +1 -1
- data/examples/i18n/he/Rakefile +1 -1
- data/examples/i18n/hu/Rakefile +1 -1
- data/examples/i18n/id/Rakefile +1 -1
- data/examples/i18n/it/Rakefile +1 -1
- data/examples/i18n/ja/Rakefile +1 -1
- data/examples/i18n/ko/Rakefile +1 -1
- data/examples/i18n/lt/Rakefile +1 -1
- data/examples/i18n/lt/features/addition.feature +2 -3
- data/examples/i18n/lv/Rakefile +1 -1
- data/examples/i18n/no/Rakefile +1 -1
- data/examples/i18n/pt/Rakefile +1 -1
- data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +3 -7
- data/examples/i18n/pt/features/support/env.rb +6 -0
- data/examples/i18n/ro/Rakefile +1 -1
- data/examples/i18n/ru/Rakefile +1 -1
- data/examples/i18n/se/Rakefile +1 -1
- data/examples/i18n/sk/Rakefile +1 -1
- data/examples/i18n/zh-CN/Rakefile +1 -1
- data/examples/i18n/zh-TW/Rakefile +1 -1
- data/examples/java/README.textile +2 -6
- data/examples/java/build.xml +26 -0
- data/examples/java/features/step_definitons/hello_steps.rb +0 -2
- data/examples/junit/features/one_passing_one_failing.feature +8 -0
- data/examples/junit/features/pending.feature +5 -0
- data/examples/junit/features/step_definitions/steps.rb +11 -0
- data/examples/selenium/Rakefile +1 -1
- data/examples/selenium_webrat/Rakefile +1 -1
- data/examples/sinatra/Rakefile +1 -1
- data/examples/test_unit/Rakefile +1 -1
- data/examples/tickets/Rakefile +3 -3
- data/examples/tickets/features/177/1.feature +29 -29
- data/examples/tickets/features/177/2.feature +20 -20
- data/examples/watir/Rakefile +1 -1
- data/features/junit_formatter.feature +59 -0
- data/features/rake_task.feature +4 -4
- data/features/step_definitions/cucumber_steps.rb +25 -2
- data/gem_tasks/features.rake +5 -1
- data/lib/cucumber.rb +2 -2
- data/lib/cucumber/cli/configuration.rb +28 -32
- data/lib/cucumber/cli/main.rb +0 -2
- data/lib/cucumber/core_ext/proc.rb +9 -13
- data/lib/cucumber/formatter.rb +1 -1
- data/lib/cucumber/formatter/html.rb +1 -0
- data/lib/cucumber/formatter/junit.rb +78 -0
- data/lib/cucumber/languages.yml +36 -4
- data/lib/cucumber/parser/feature.rb +10 -6
- data/lib/cucumber/parser/feature.tt +18 -14
- data/lib/cucumber/rake/task.rb +92 -24
- data/lib/cucumber/step_mother.rb +3 -0
- data/lib/cucumber/version.rb +1 -1
- data/rails_generators/cucumber/templates/cucumber.rake +2 -1
- data/spec/cucumber/cli/configuration_spec.rb +9 -18
- metadata +13 -3
- data/examples/java/Rakefile +0 -12
data/gem_tasks/features.rake
CHANGED
@@ -2,5 +2,9 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
2
2
|
require 'cucumber/rake/task'
|
3
3
|
|
4
4
|
Cucumber::Rake::Task.new do |t|
|
5
|
-
t.cucumber_opts =
|
5
|
+
t.cucumber_opts = %w{--format progress}
|
6
|
+
end
|
7
|
+
|
8
|
+
Cucumber::Rake::Task.new('pretty') do |t|
|
9
|
+
t.cucumber_opts = %w{--format pretty}
|
6
10
|
end
|
data/lib/cucumber.rb
CHANGED
@@ -40,8 +40,8 @@ module Cucumber
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def alias_step_definitions(lang) #:nodoc:
|
43
|
-
keywords = %w{given when then and but}.map{|keyword| keyword_hash(lang)[keyword]}
|
44
|
-
alias_steps(keywords)
|
43
|
+
keywords = %w{given when then and but}.map{|keyword| keyword_hash(lang)[keyword].split('|')}
|
44
|
+
alias_steps(keywords.flatten)
|
45
45
|
end
|
46
46
|
|
47
47
|
# Sets up additional method aliases for Given, When and Then.
|
@@ -3,7 +3,7 @@ module Cucumber
|
|
3
3
|
class YmlLoadError < StandardError; end
|
4
4
|
|
5
5
|
class Configuration
|
6
|
-
FORMATS = %w{pretty profile progress rerun}
|
6
|
+
FORMATS = %w{pretty profile progress rerun junit}
|
7
7
|
DEFAULT_FORMAT = 'pretty'
|
8
8
|
|
9
9
|
attr_reader :paths
|
@@ -15,6 +15,7 @@ module Cucumber
|
|
15
15
|
|
16
16
|
@paths = []
|
17
17
|
@options = default_options
|
18
|
+
|
18
19
|
@active_format = DEFAULT_FORMAT
|
19
20
|
end
|
20
21
|
|
@@ -47,7 +48,7 @@ module Cucumber
|
|
47
48
|
%{Run with "--language help" to see all languages},
|
48
49
|
%{Run with "--language LANG help" to list keywords for LANG}) do |v|
|
49
50
|
if v == 'help'
|
50
|
-
|
51
|
+
list_languages_and_exit
|
51
52
|
elsif args==['help']
|
52
53
|
list_keywords_and_exit(v)
|
53
54
|
else
|
@@ -65,10 +66,11 @@ module Cucumber
|
|
65
66
|
@options[:formats][v] = @out_stream
|
66
67
|
@active_format = v
|
67
68
|
end
|
68
|
-
opts.on("-o", "--out FILE",
|
69
|
-
"Write output to a file instead of STDOUT. This option",
|
69
|
+
opts.on("-o", "--out [FILE|DIR]",
|
70
|
+
"Write output to a file/directory instead of STDOUT. This option",
|
70
71
|
"applies to the previously specified --format, or the",
|
71
|
-
"default format if no format is specified.
|
72
|
+
"default format if no format is specified. Check the specific",
|
73
|
+
"formatter's docs to see whether to pass a file or a dir.") do |v|
|
72
74
|
@options[:formats][@active_format] = v
|
73
75
|
end
|
74
76
|
opts.on("-t TAGS", "--tags TAGS",
|
@@ -197,10 +199,12 @@ module Cucumber
|
|
197
199
|
return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat]
|
198
200
|
formatters = @options[:formats].map do |format, out|
|
199
201
|
if String === out # file name
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
202
|
+
unless File.directory?(out)
|
203
|
+
out = File.open(out, Cucumber.file_mode('w'))
|
204
|
+
at_exit do
|
205
|
+
out.flush
|
206
|
+
out.close
|
207
|
+
end
|
204
208
|
end
|
205
209
|
end
|
206
210
|
|
@@ -208,7 +212,8 @@ module Cucumber
|
|
208
212
|
formatter_class = formatter_class(format)
|
209
213
|
formatter_class.new(step_mother, out, @options)
|
210
214
|
rescue Exception => e
|
211
|
-
|
215
|
+
e.message << "\nError creating formatter: #{format}"
|
216
|
+
raise e
|
212
217
|
end
|
213
218
|
end
|
214
219
|
|
@@ -225,6 +230,7 @@ module Cucumber
|
|
225
230
|
when 'progress' then Formatter::Progress
|
226
231
|
when 'rerun' then Formatter::Rerun
|
227
232
|
when 'usage' then Formatter::Usage
|
233
|
+
when 'junit' then Formatter::JUnit
|
228
234
|
else
|
229
235
|
constantize(format)
|
230
236
|
end
|
@@ -244,7 +250,6 @@ module Cucumber
|
|
244
250
|
files
|
245
251
|
end
|
246
252
|
|
247
|
-
|
248
253
|
def feature_files
|
249
254
|
potential_feature_files = @paths.map do |path|
|
250
255
|
path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
|
@@ -278,7 +283,7 @@ module Cucumber
|
|
278
283
|
|
279
284
|
def parse_args_from_profile(profile)
|
280
285
|
unless cucumber_yml.has_key?(profile)
|
281
|
-
|
286
|
+
raise(<<-END_OF_ERROR)
|
282
287
|
Could not find profile: '#{profile}'
|
283
288
|
|
284
289
|
Defined profiles in cucumber.yml:
|
@@ -288,16 +293,16 @@ Defined profiles in cucumber.yml:
|
|
288
293
|
|
289
294
|
args_from_yml = cucumber_yml[profile] || ''
|
290
295
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
296
|
+
case(args_from_yml)
|
297
|
+
when String
|
298
|
+
raise "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the 'foo' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/
|
299
|
+
args_from_yml = args_from_yml.split(' ')
|
300
|
+
when Array
|
301
|
+
raise "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the 'foo' profile in cucumber.yml.\n" if args_from_yml.empty?
|
302
|
+
else
|
303
|
+
raise "The '#{profile}' profile in cucumber.yml was a #{args_from_yml.class}. It must be a String or Array"
|
297
304
|
end
|
298
|
-
|
299
|
-
rescue YmlLoadError => e
|
300
|
-
exit_with_error(e.message)
|
305
|
+
parse!(args_from_yml)
|
301
306
|
end
|
302
307
|
|
303
308
|
def cucumber_yml
|
@@ -322,13 +327,13 @@ Defined profiles in cucumber.yml:
|
|
322
327
|
|
323
328
|
def list_keywords_and_exit(lang)
|
324
329
|
unless Cucumber::LANGUAGES[lang]
|
325
|
-
|
330
|
+
raise("No language with key #{lang}")
|
326
331
|
end
|
327
332
|
LanguageHelpFormatter.list_keywords(@out_stream, lang)
|
328
333
|
Kernel.exit
|
329
334
|
end
|
330
335
|
|
331
|
-
def
|
336
|
+
def list_languages_and_exit
|
332
337
|
LanguageHelpFormatter.list_languages(@out_stream)
|
333
338
|
Kernel.exit
|
334
339
|
end
|
@@ -347,15 +352,6 @@ Defined profiles in cucumber.yml:
|
|
347
352
|
:diff_enabled => true
|
348
353
|
}
|
349
354
|
end
|
350
|
-
|
351
|
-
def exit_with_error(error_message, e=nil)
|
352
|
-
@error_stream.puts(error_message)
|
353
|
-
if e
|
354
|
-
@error_stream.puts("#{e.message} (#{e.class})")
|
355
|
-
@error_stream.puts(e.backtrace.join("\n"))
|
356
|
-
end
|
357
|
-
Kernel.exit 1
|
358
|
-
end
|
359
355
|
end
|
360
356
|
|
361
357
|
end
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
class Proc
|
3
3
|
PROC_PATTERN = /[\d\w]+@(.*):(.*)>/
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
5
|
+
def to_comment_line
|
6
|
+
"# #{file_colon_line}"
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
def backtrace_line(name)
|
10
|
+
"#{file_colon_line}:in `#{name}'"
|
11
|
+
end
|
13
12
|
|
13
|
+
if Proc.new{}.to_s =~ PROC_PATTERN
|
14
14
|
def file_colon_line
|
15
15
|
path, line = *to_s.match(PROC_PATTERN)[1..2]
|
16
16
|
path = File.expand_path(path)
|
@@ -22,12 +22,8 @@ class Proc
|
|
22
22
|
# This Ruby implementation doesn't implement Proc#to_s correctly
|
23
23
|
STDERR.puts "*** THIS RUBY IMPLEMENTATION DOESN'T REPORT FILE AND LINE FOR PROCS ***"
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_comment_line
|
30
|
-
""
|
25
|
+
def file_colon_line
|
26
|
+
"UNKNOWN:-1"
|
31
27
|
end
|
32
28
|
end
|
33
29
|
end
|
data/lib/cucumber/formatter.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
%w{color_io pretty progress profile rerun html usage}.each{|n| require "cucumber/formatter/#{n}"}
|
1
|
+
%w{color_io pretty progress profile rerun html usage junit}.each{|n| require "cucumber/formatter/#{n}"}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
begin
|
2
|
+
require 'builder'
|
3
|
+
rescue LoadError
|
4
|
+
gem 'builder'
|
5
|
+
require 'builder'
|
6
|
+
end
|
7
|
+
|
8
|
+
module Cucumber
|
9
|
+
module Formatter
|
10
|
+
class JUnit < Cucumber::Ast::Visitor
|
11
|
+
|
12
|
+
def initialize(step_mother, io, options)
|
13
|
+
super(step_mother)
|
14
|
+
@reportdir = io
|
15
|
+
raise "You *must* specify --out DIR for the junit formatter" unless @reportdir
|
16
|
+
raise "Use --out DIR (not --out FILE) for the junit formatter" if File === @reportdir
|
17
|
+
end
|
18
|
+
|
19
|
+
def visit_feature(feature)
|
20
|
+
@failures = @errors = @tests = 0
|
21
|
+
@builder = Builder::XmlMarkup.new( :indent => 2 )
|
22
|
+
super
|
23
|
+
|
24
|
+
@testsuite = Builder::XmlMarkup.new( :indent => 2 )
|
25
|
+
@testsuite.instruct!
|
26
|
+
@testsuite.testsuite(
|
27
|
+
:failures => @failures,
|
28
|
+
:errors => @errors,
|
29
|
+
:tests => @tests,
|
30
|
+
:name => @feature_name ) do
|
31
|
+
@testsuite << @builder.target!
|
32
|
+
end
|
33
|
+
|
34
|
+
File.open(@feature_filename, 'w') { |file| file.write(@testsuite.target!) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def visit_feature_name(name)
|
38
|
+
lines = name.split(/\r?\n/)
|
39
|
+
@feature_name = lines[0].sub(/Feature\:/, '').strip
|
40
|
+
@feature_filename = convert_to_file_name(@feature_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def visit_scenario_name(keyword, name, file_colon_line, source_indent)
|
44
|
+
@scenario = name
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_steps(steps)
|
48
|
+
@steps_failed = false
|
49
|
+
super
|
50
|
+
@failures += 1 if @steps_failed
|
51
|
+
@tests += 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
55
|
+
step_name = keyword + " " + step_match.format_args(lambda{|param| "*#{param}*"})
|
56
|
+
@builder.testcase(:classname => "#{@feature_name}.#{@scenario}", :name => step_name) do
|
57
|
+
if status != :passed
|
58
|
+
@builder.failure(:message => step_name) do
|
59
|
+
@builder.text!(format_exception(exception)) if exception
|
60
|
+
end
|
61
|
+
@steps_failed = true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def convert_to_file_name(feature_name)
|
69
|
+
File.join(@reportdir, "TEST-" + feature_name.gsub(/[^\w_\.]/, '_') + ".xml")
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_exception(exception)
|
73
|
+
(["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/cucumber/languages.yml
CHANGED
@@ -161,6 +161,21 @@
|
|
161
161
|
and: Y
|
162
162
|
but: Pero
|
163
163
|
space_after_keyword: true
|
164
|
+
"cat":
|
165
|
+
name: Catalan
|
166
|
+
native: català
|
167
|
+
encoding: UTF-8
|
168
|
+
background: Rerefons
|
169
|
+
feature: Característica
|
170
|
+
scenario: Escenari
|
171
|
+
scenario_outline: Esquema de l\'escenari
|
172
|
+
examples: Exemples
|
173
|
+
given: Donat
|
174
|
+
when: Quan
|
175
|
+
then: Aleshores
|
176
|
+
and: I
|
177
|
+
but: Però
|
178
|
+
space_after_keyword: true
|
164
179
|
"et":
|
165
180
|
name: Estonian
|
166
181
|
native: eesti keel
|
@@ -270,7 +285,7 @@
|
|
270
285
|
name: Japanese
|
271
286
|
native: 日本語
|
272
287
|
encoding: UTF-8
|
273
|
-
feature:
|
288
|
+
feature: フィーチャ|機能
|
274
289
|
background: 背景
|
275
290
|
scenario: シナリオ
|
276
291
|
scenario_outline: シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ
|
@@ -279,15 +294,17 @@
|
|
279
294
|
when: もし
|
280
295
|
then: ならば
|
281
296
|
and: かつ
|
282
|
-
but:
|
297
|
+
but: しかし|但し
|
283
298
|
space_after_keyword: false
|
284
299
|
"lt":
|
285
300
|
name: Lithuanian
|
286
301
|
native: lietuvių kalba
|
287
302
|
encoding: UTF-8
|
288
|
-
feature:
|
303
|
+
feature: Savybė
|
304
|
+
background: Kontekstas
|
289
305
|
scenario: Scenarijus
|
290
|
-
|
306
|
+
scenario_outline: Scenarijaus šablonas
|
307
|
+
examples: Pavyzdžiai|Scenarijai|Variantai
|
291
308
|
given: Duota
|
292
309
|
when: Kai
|
293
310
|
then: Tada
|
@@ -498,3 +515,18 @@
|
|
498
515
|
and: И
|
499
516
|
but: Но
|
500
517
|
space_after_keyword: true
|
518
|
+
"vi":
|
519
|
+
name: Vietnamese
|
520
|
+
native: Tiếng Việt
|
521
|
+
encoding: UTF-8
|
522
|
+
feature: Tính năng
|
523
|
+
background: Bối cảnh
|
524
|
+
scenario: Kịch bản
|
525
|
+
scenario_outline: Khung kịch bản
|
526
|
+
examples: Dữ liệu
|
527
|
+
given: Cho
|
528
|
+
when: Khi
|
529
|
+
then: Thì
|
530
|
+
and: Và
|
531
|
+
but: Nhưng
|
532
|
+
space_after_keyword: true
|
@@ -481,15 +481,19 @@ module Cucumber
|
|
481
481
|
end
|
482
482
|
|
483
483
|
module Background1
|
484
|
-
|
485
484
|
def matches_name?(regexp_to_match)
|
486
485
|
name.build =~ regexp_to_match
|
487
486
|
end
|
488
487
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
488
|
+
def at_line?(line)
|
489
|
+
background_keyword.line == line ||
|
490
|
+
steps.at_line?(line)
|
491
|
+
end
|
492
|
+
|
493
|
+
def has_tags?(tag_names)
|
494
|
+
feature_tags = self.parent.tags
|
495
|
+
feature_tags.has_tags?(tag_names)
|
496
|
+
end
|
493
497
|
|
494
498
|
def build
|
495
499
|
Ast::Background.new(
|
@@ -499,7 +503,7 @@ module Cucumber
|
|
499
503
|
name.build,
|
500
504
|
steps.build
|
501
505
|
)
|
502
|
-
|
506
|
+
end
|
503
507
|
end
|
504
508
|
|
505
509
|
def _nt_background
|
@@ -76,27 +76,31 @@ module Cucumber
|
|
76
76
|
end
|
77
77
|
|
78
78
|
rule background
|
79
|
-
|
79
|
+
comment white background_keyword space* name:lines_to_keyword? (eol+ / eof) steps {
|
80
|
+
def matches_name?(regexp_to_match)
|
81
|
+
name.build =~ regexp_to_match
|
82
|
+
end
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
def at_line?(line)
|
85
|
+
background_keyword.line == line ||
|
86
|
+
steps.at_line?(line)
|
87
|
+
end
|
84
88
|
|
85
89
|
def has_tags?(tag_names)
|
86
90
|
feature_tags = self.parent.tags
|
87
91
|
feature_tags.has_tags?(tag_names)
|
88
92
|
end
|
89
93
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
94
|
+
def build
|
95
|
+
Ast::Background.new(
|
96
|
+
comment.build,
|
97
|
+
background_keyword.line,
|
98
|
+
background_keyword.text_value,
|
99
|
+
name.build,
|
100
|
+
steps.build
|
101
|
+
)
|
102
|
+
end
|
103
|
+
}
|
100
104
|
end
|
101
105
|
|
102
106
|
rule feature_elements
|