cucumber 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/History.txt +44 -1
  2. data/Manifest.txt +11 -1
  3. data/bin/cucumber +11 -1
  4. data/examples/cs/Rakefile +1 -1
  5. data/examples/cs/features/addition.feature +5 -5
  6. data/examples/cs/features/step_definitons/calculator_steps.rb +2 -2
  7. data/examples/dos_line_endings/Rakefile +1 -1
  8. data/examples/i18n/ar/Rakefile +1 -1
  9. data/examples/i18n/bg/Rakefile +1 -1
  10. data/examples/i18n/cat/Rakefile +6 -0
  11. data/examples/i18n/cat/features/step_definitons/calculator_steps.rb +21 -0
  12. data/examples/i18n/cat/features/suma.feature +16 -0
  13. data/examples/i18n/cat/lib/calculadora.rb +16 -0
  14. data/examples/i18n/da/Rakefile +1 -1
  15. data/examples/i18n/de/Rakefile +1 -1
  16. data/examples/i18n/en-lol/Rakefile +1 -1
  17. data/examples/i18n/en/Rakefile +1 -1
  18. data/examples/i18n/es/Rakefile +1 -1
  19. data/examples/i18n/et/Rakefile +1 -1
  20. data/examples/i18n/fi/Rakefile +1 -1
  21. data/examples/i18n/fr/Rakefile +1 -1
  22. data/examples/i18n/he/Rakefile +1 -1
  23. data/examples/i18n/hu/Rakefile +1 -1
  24. data/examples/i18n/id/Rakefile +1 -1
  25. data/examples/i18n/it/Rakefile +1 -1
  26. data/examples/i18n/ja/Rakefile +1 -1
  27. data/examples/i18n/ko/Rakefile +1 -1
  28. data/examples/i18n/lt/Rakefile +1 -1
  29. data/examples/i18n/lt/features/addition.feature +2 -3
  30. data/examples/i18n/lv/Rakefile +1 -1
  31. data/examples/i18n/no/Rakefile +1 -1
  32. data/examples/i18n/pt/Rakefile +1 -1
  33. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +3 -7
  34. data/examples/i18n/pt/features/support/env.rb +6 -0
  35. data/examples/i18n/ro/Rakefile +1 -1
  36. data/examples/i18n/ru/Rakefile +1 -1
  37. data/examples/i18n/se/Rakefile +1 -1
  38. data/examples/i18n/sk/Rakefile +1 -1
  39. data/examples/i18n/zh-CN/Rakefile +1 -1
  40. data/examples/i18n/zh-TW/Rakefile +1 -1
  41. data/examples/java/README.textile +2 -6
  42. data/examples/java/build.xml +26 -0
  43. data/examples/java/features/step_definitons/hello_steps.rb +0 -2
  44. data/examples/junit/features/one_passing_one_failing.feature +8 -0
  45. data/examples/junit/features/pending.feature +5 -0
  46. data/examples/junit/features/step_definitions/steps.rb +11 -0
  47. data/examples/selenium/Rakefile +1 -1
  48. data/examples/selenium_webrat/Rakefile +1 -1
  49. data/examples/sinatra/Rakefile +1 -1
  50. data/examples/test_unit/Rakefile +1 -1
  51. data/examples/tickets/Rakefile +3 -3
  52. data/examples/tickets/features/177/1.feature +29 -29
  53. data/examples/tickets/features/177/2.feature +20 -20
  54. data/examples/watir/Rakefile +1 -1
  55. data/features/junit_formatter.feature +59 -0
  56. data/features/rake_task.feature +4 -4
  57. data/features/step_definitions/cucumber_steps.rb +25 -2
  58. data/gem_tasks/features.rake +5 -1
  59. data/lib/cucumber.rb +2 -2
  60. data/lib/cucumber/cli/configuration.rb +28 -32
  61. data/lib/cucumber/cli/main.rb +0 -2
  62. data/lib/cucumber/core_ext/proc.rb +9 -13
  63. data/lib/cucumber/formatter.rb +1 -1
  64. data/lib/cucumber/formatter/html.rb +1 -0
  65. data/lib/cucumber/formatter/junit.rb +78 -0
  66. data/lib/cucumber/languages.yml +36 -4
  67. data/lib/cucumber/parser/feature.rb +10 -6
  68. data/lib/cucumber/parser/feature.tt +18 -14
  69. data/lib/cucumber/rake/task.rb +92 -24
  70. data/lib/cucumber/step_mother.rb +3 -0
  71. data/lib/cucumber/version.rb +1 -1
  72. data/rails_generators/cucumber/templates/cucumber.rake +2 -1
  73. data/spec/cucumber/cli/configuration_spec.rb +9 -18
  74. metadata +13 -3
  75. data/examples/java/Rakefile +0 -12
@@ -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 = "--format progress"
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
@@ -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
- list_languages
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.") do |v|
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
- out = File.open(out, Cucumber.file_mode('w'))
201
- at_exit do
202
- out.flush
203
- out.close
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
- exit_with_error("Error creating formatter: #{format}", e)
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
- return(exit_with_error <<-END_OF_ERROR)
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
- if !args_from_yml.is_a?(String)
292
- exit_with_error "Profiles must be defined as a String. The '#{profile}' profile was #{args_from_yml.inspect} (#{args_from_yml.class}).\n"
293
- elsif args_from_yml =~ /^\s*$/
294
- exit_with_error "The 'foo' profile in cucumber.yml was blank. Please define the command line arguments for the 'foo' profile in cucumber.yml.\n"
295
- else
296
- parse!(args_from_yml.split(' '))
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
- exit_with_error("No language with key #{lang}")
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 list_languages
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
@@ -42,8 +42,6 @@ module Cucumber
42
42
 
43
43
  failure = step_mother.steps(:failed).any? ||
44
44
  (configuration.strict? && step_mother.steps(:undefined).any?)
45
-
46
- Kernel.exit(failure ? 1 : 0)
47
45
  end
48
46
 
49
47
  def load_plain_text_features
@@ -2,15 +2,15 @@
2
2
  class Proc
3
3
  PROC_PATTERN = /[\d\w]+@(.*):(.*)>/
4
4
 
5
- if Proc.new{}.to_s =~ PROC_PATTERN
6
- def backtrace_line(name)
7
- "#{file_colon_line}:in `#{name}'"
8
- end
5
+ def to_comment_line
6
+ "# #{file_colon_line}"
7
+ end
9
8
 
10
- def to_comment_line
11
- "# #{file_colon_line}"
12
- end
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 backtrace_line
26
- nil
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
@@ -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}"}
@@ -27,6 +27,7 @@ module Cucumber
27
27
  )
28
28
  @builder.html(:xmlns => 'http://www.w3.org/1999/xhtml') do
29
29
  @builder.head do
30
+ @builder.meta(:content => 'text/html;charset=utf-8')
30
31
  @builder.title 'Cucumber'
31
32
  inline_css
32
33
  end
@@ -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
@@ -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: Sąvybė
303
+ feature: Savybė
304
+ background: Kontekstas
289
305
  scenario: Scenarijus
290
- examples: Pavyzdžių
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
- def has_tags?(tag_names)
490
- feature_tags = self.parent.tags
491
- feature_tags.has_tags?(tag_names)
492
- end
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
- end
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
- comment white background_keyword space* name:lines_to_keyword? (eol+ / eof) steps {
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
- def matches_name?(regexp_to_match)
82
- name.build =~ regexp_to_match
83
- end
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
- def build
91
- Ast::Background.new(
92
- comment.build,
93
- background_keyword.line,
94
- background_keyword.text_value,
95
- name.build,
96
- steps.build
97
- )
98
- end
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