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.
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