cucumber 0.3.3 → 0.3.4
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 +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
|