cucumber 0.3.9 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/History.txt +40 -0
  2. data/Manifest.txt +10 -0
  3. data/cucumber.yml +1 -1
  4. data/examples/i18n/pl/Rakefile +6 -0
  5. data/examples/i18n/pl/features/addition.feature +16 -0
  6. data/examples/i18n/pl/features/division.feature +9 -0
  7. data/examples/i18n/pl/features/step_definitons/calculator_steps.rb +24 -0
  8. data/examples/i18n/pl/features/support/env.rb +6 -0
  9. data/examples/i18n/pl/lib/calculator.rb +14 -0
  10. data/examples/self_test/features/sample.feature +2 -0
  11. data/features/cucumber_cli.feature +24 -6
  12. data/features/drb_server_integration.feature +92 -0
  13. data/features/html_formatter/a.html +926 -437
  14. data/features/junit_formatter.feature +11 -2
  15. data/features/step_definitions/cucumber_steps.rb +15 -2
  16. data/features/support/env.rb +35 -1
  17. data/features/usage.feature +2 -2
  18. data/lib/cucumber/ast/background.rb +1 -1
  19. data/lib/cucumber/ast/comment.rb +4 -0
  20. data/lib/cucumber/ast/feature.rb +2 -1
  21. data/lib/cucumber/ast/outline_table.rb +2 -2
  22. data/lib/cucumber/ast/scenario.rb +1 -1
  23. data/lib/cucumber/ast/scenario_outline.rb +11 -4
  24. data/lib/cucumber/ast/step_invocation.rb +17 -12
  25. data/lib/cucumber/ast/visitor.rb +4 -0
  26. data/lib/cucumber/cli/configuration.rb +32 -8
  27. data/lib/cucumber/cli/drb_client.rb +21 -0
  28. data/lib/cucumber/cli/main.rb +9 -0
  29. data/lib/cucumber/formatter/color_io.rb +2 -2
  30. data/lib/cucumber/formatter/console.rb +1 -3
  31. data/lib/cucumber/formatter/html.rb +63 -12
  32. data/lib/cucumber/formatter/junit.rb +2 -2
  33. data/lib/cucumber/formatter/pretty.rb +2 -1
  34. data/lib/cucumber/formatter/rerun.rb +1 -0
  35. data/lib/cucumber/formatter/tag_cloud.rb +1 -0
  36. data/lib/cucumber/rails/rspec.rb +5 -3
  37. data/lib/cucumber/rake/task.rb +1 -1
  38. data/lib/cucumber/step_match.rb +6 -2
  39. data/lib/cucumber/step_mother.rb +11 -8
  40. data/lib/cucumber/version.rb +1 -1
  41. data/rails_generators/cucumber/cucumber_generator.rb +12 -2
  42. data/rails_generators/cucumber/templates/cucumber.rake +1 -1
  43. data/rails_generators/cucumber/templates/cucumber_environment.rb +6 -4
  44. data/rails_generators/cucumber/templates/env.rb +10 -3
  45. data/rails_generators/cucumber/templates/spork_env.rb +36 -0
  46. data/rails_generators/cucumber/templates/webrat_steps.rb +8 -0
  47. data/spec/cucumber/cli/configuration_spec.rb +56 -1
  48. data/spec/cucumber/cli/drb_client_spec.rb +42 -0
  49. data/spec/cucumber/cli/main_spec.rb +64 -19
  50. metadata +12 -2
@@ -58,5 +58,14 @@ Feature: JUnit output formatter
58
58
  """
59
59
  And "examples/junit/tmp/TEST-One_passing_scenario__one_failing_scenario.xml" should exist
60
60
  And "examples/junit/tmp/TEST-Pending_step.xml" should exist
61
-
62
-
61
+
62
+ Scenario: show correct error message if no --out is passed
63
+ When I run cucumber --format junit features
64
+ Then STDERR should not match
65
+ """
66
+ can't convert .* into String \(TypeError\)
67
+ """
68
+ And STDERR should match
69
+ """
70
+ You \*must\* specify \-\-out DIR for the junit formatter
71
+ """
@@ -31,6 +31,15 @@ Given /^the following profiles? (?:are|is) defined:$/ do |profiles|
31
31
  create_file('cucumber.yml', profiles)
32
32
  end
33
33
 
34
+ Given /^I am running "([^\"]*)" in the background$/ do |command|
35
+ run_in_background command
36
+ end
37
+
38
+ Given /^I am not running (?:.*) in the background$/ do
39
+ # no-op
40
+ end
41
+
42
+
34
43
  When /^I run cucumber (.*)$/ do |cucumber_opts|
35
44
  run "#{Cucumber::RUBY_BINARY} #{Cucumber::BINARY} --no-color #{cucumber_opts}"
36
45
  end
@@ -88,9 +97,9 @@ end
88
97
 
89
98
  Then /^"([^\"]*)" should have the same contents as "([^\"]*)"$/ do |actual_file, expected_file|
90
99
  actual = IO.read(actual_file)
91
- # Comment out to replace expected file. Use with care! Remember to update duration afterwards.
92
- # File.open(expected_file, "w"){|io| io.write(actual)}
93
100
  actual = replace_duration(actual, '0m30.005s')
101
+ # Comment out to replace expected file. Use with care! Remember to update duration afterwards.
102
+ # File.open(expected_file, "w") {|io| io.write(actual)}
94
103
  actual.should == IO.read(expected_file)
95
104
  end
96
105
 
@@ -98,6 +107,10 @@ Then /^STDERR should match$/ do |text|
98
107
  last_stderr.should =~ /#{text}/
99
108
  end
100
109
 
110
+ Then /^STDERR should not match$/ do |text|
111
+ last_stderr.should_not =~ /#{text}/
112
+ end
113
+
101
114
  Then /^STDERR should be empty$/ do
102
115
  last_stderr.should == ""
103
116
  end
@@ -3,6 +3,11 @@ require 'tempfile'
3
3
  require 'spec/expectations'
4
4
  require 'fileutils'
5
5
  require 'forwardable'
6
+ begin
7
+ require 'spork'
8
+ rescue Gem::LoadError => ex
9
+ warn "WARNING: #{ex.message} You need to have the spork gem installed to run the DRb feature properly!"
10
+ end
6
11
 
7
12
  class CucumberWorld
8
13
  extend Forwardable
@@ -52,6 +57,10 @@ class CucumberWorld
52
57
  end
53
58
  end
54
59
 
60
+ def background_jobs
61
+ @background_jobs ||= []
62
+ end
63
+
55
64
  def in_current_dir(&block)
56
65
  Dir.chdir(@current_dir, &block)
57
66
  end
@@ -66,6 +75,27 @@ class CucumberWorld
66
75
  @last_stderr = IO.read(stderr_file.path)
67
76
  end
68
77
 
78
+ def run_in_background(command)
79
+ pid = fork
80
+ in_current_dir do
81
+ if pid
82
+ background_jobs << pid
83
+ else
84
+ #STDOUT.close
85
+ #STDERR.close
86
+ exec command
87
+ end
88
+ end
89
+ end
90
+
91
+ def terminate_background_jobs
92
+ if @background_jobs
93
+ @background_jobs.each do |pid|
94
+ Process.kill(Signal.list['TERM'], pid)
95
+ end
96
+ end
97
+ end
98
+
69
99
  end
70
100
 
71
101
  World do
@@ -77,7 +107,11 @@ Before do
77
107
  FileUtils.mkdir CucumberWorld.working_dir
78
108
  end
79
109
 
110
+ After do
111
+ terminate_background_jobs
112
+ end
113
+
80
114
  Before('@diffxml') do
81
115
  `diffxml --version`
82
116
  raise "Please install diffxml from http://diffxml.sourceforge.net/" if $? != 0
83
- end
117
+ end
@@ -113,11 +113,11 @@ Feature: Cucumber command line
113
113
  Then the multiline string should be # features/background/multiline_args_background.feature:17
114
114
  Then the multiline string should be # features/background/multiline_args_background.feature:27
115
115
  /^passing$/ # features/step_definitions/sample_steps.rb:5
116
- Given passing # features/sample.feature:10
116
+ Given passing # features/sample.feature:12
117
117
  /^failing expectation$/ # features/step_definitions/sample_steps.rb:62
118
118
  Given failing expectation # features/failing_expectation.feature:4
119
119
  /^failing$/ # features/step_definitions/sample_steps.rb:8
120
- Given failing # features/sample.feature:16
120
+ Given failing # features/sample.feature:18
121
121
  (::) UNUSED (::)
122
122
  /^unused$/ # features/step_definitions/sample_steps.rb:66
123
123
  /^another unused$/ # features/step_definitions/sample_steps.rb:69
@@ -23,7 +23,7 @@ module Cucumber
23
23
  end
24
24
 
25
25
  def accept(visitor)
26
- visitor.visit_comment(@comment)
26
+ visitor.visit_comment(@comment) unless @comment.empty?
27
27
  visitor.visit_background_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
28
28
  visitor.step_mother.before(hook_context)
29
29
  visitor.visit_steps(@step_invocations)
@@ -12,6 +12,10 @@ module Cucumber
12
12
  @value = value
13
13
  end
14
14
 
15
+ def empty?
16
+ @value.nil? || @value == ""
17
+ end
18
+
15
19
  def accept(visitor)
16
20
  @value.split("\n").each do |line|
17
21
  visitor.visit_comment_line(line.strip)
@@ -4,6 +4,7 @@ module Cucumber
4
4
  class Feature
5
5
  attr_accessor :file
6
6
  attr_writer :features
7
+ attr_reader :name
7
8
 
8
9
  def initialize(background, comment, tags, name, feature_elements)
9
10
  @background, @comment, @tags, @name, @feature_elements = background, comment, tags, name, feature_elements
@@ -15,7 +16,7 @@ module Cucumber
15
16
  end
16
17
 
17
18
  def accept(visitor)
18
- visitor.visit_comment(@comment)
19
+ visitor.visit_comment(@comment) unless @comment.empty?
19
20
  visitor.visit_tags(@tags)
20
21
  visitor.visit_feature_name(@name)
21
22
  visitor.visit_background(@background) if @background
@@ -70,7 +70,7 @@ module Cucumber
70
70
  visitor.step_mother.before_and_after(self) do
71
71
  @step_invocations.each do |step_invocation|
72
72
  step_invocation.invoke(visitor.step_mother, visitor.options)
73
- @exception ||= step_invocation.exception
73
+ @exception ||= step_invocation.reported_exception
74
74
  end
75
75
 
76
76
  @cells.each do |cell|
@@ -89,7 +89,7 @@ module Cucumber
89
89
  @table.visit_scenario_name(visitor, self)
90
90
  @step_invocations.each do |step_invocation|
91
91
  step_invocation.invoke(visitor.step_mother, visitor.options)
92
- @exception ||= step_invocation.exception
92
+ @exception ||= step_invocation.reported_exception
93
93
  step_invocation.visit_step_result(visitor)
94
94
  end
95
95
  end
@@ -21,7 +21,7 @@ module Cucumber
21
21
  end
22
22
 
23
23
  def accept(visitor)
24
- visitor.visit_comment(@comment)
24
+ visitor.visit_comment(@comment) unless @comment.empty?
25
25
  visitor.visit_tags(@tags)
26
26
  visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
27
27
 
@@ -3,6 +3,14 @@ module Cucumber
3
3
  class ScenarioOutline
4
4
  include FeatureElement
5
5
 
6
+ module ExamplesArray
7
+ def accept(visitor)
8
+ each do |examples|
9
+ visitor.visit_examples(examples)
10
+ end
11
+ end
12
+ end
13
+
6
14
  # The +example_sections+ argument must be an Array where each element is another array representing
7
15
  # an Examples section. This array has 3 elements:
8
16
  #
@@ -23,20 +31,19 @@ module Cucumber
23
31
  examples_table = OutlineTable.new(examples_matrix, self)
24
32
  Examples.new(examples_line, examples_keyword, examples_name, examples_table)
25
33
  end
34
+ @examples_array.extend(ExamplesArray)
26
35
 
27
36
  @background.feature_elements << self if @background
28
37
  end
29
38
 
30
39
  def accept(visitor)
31
- visitor.visit_comment(@comment)
40
+ visitor.visit_comment(@comment) unless @comment.empty?
32
41
  visitor.visit_tags(@tags)
33
42
  visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
34
43
  visitor.visit_steps(@steps)
35
44
 
36
45
  skip_invoke! if @background && @background.failed?
37
- @examples_array.each do |examples|
38
- visitor.visit_examples(examples)
39
- end
46
+ visitor.visit_examples_array(@examples_array) unless @examples_array.empty?
40
47
  end
41
48
 
42
49
  def skip_invoke!
@@ -2,7 +2,7 @@ module Cucumber
2
2
  module Ast
3
3
  class StepInvocation
4
4
  attr_writer :step_collection, :background
5
- attr_reader :name, :matched_cells, :status
5
+ attr_reader :name, :matched_cells, :status, :reported_exception
6
6
  attr_accessor :exception
7
7
 
8
8
  def initialize(step, name, multiline_arg, matched_cells)
@@ -24,12 +24,12 @@ module Cucumber
24
24
  end
25
25
 
26
26
  def visit_step_result(visitor)
27
- visitor.visit_step_result(keyword, @step_match, @multiline_arg, @status, @exception, source_indent, @background)
27
+ visitor.visit_step_result(keyword, @step_match, @multiline_arg, @status, @reported_exception, source_indent, @background)
28
28
  end
29
29
 
30
30
  def invoke(step_mother, options)
31
31
  find_step_match!(step_mother)
32
- unless @skip_invoke || options[:dry_run] || exception || @step_collection.exception
32
+ unless @skip_invoke || options[:dry_run] || @exception || @step_collection.exception
33
33
  @skip_invoke = true
34
34
  begin
35
35
  step_mother.current_world.__cucumber_current_step = self if step_mother.current_world # Nil in Pure Java
@@ -37,13 +37,13 @@ module Cucumber
37
37
  step_mother.after_step
38
38
  status!(:passed)
39
39
  rescue Pending => e
40
- failed(e, false)
40
+ failed(options, e, false)
41
41
  status!(:pending)
42
42
  rescue Undefined => e
43
- failed(e, false)
43
+ failed(options, e, false)
44
44
  status!(:undefined)
45
45
  rescue Exception => e
46
- failed(e, false)
46
+ failed(options, e, false)
47
47
  status!(:failed)
48
48
  end
49
49
  end
@@ -54,21 +54,26 @@ module Cucumber
54
54
  begin
55
55
  @step_match = step_mother.step_match(@name)
56
56
  rescue Undefined => e
57
- failed(e, true)
57
+ failed(step_mother.options, e, true)
58
58
  status!(:undefined)
59
59
  @step_match = NoStepMatch.new(@step, @name)
60
60
  rescue Ambiguous => e
61
- failed(e, false)
61
+ failed(step_mother.options, e, false)
62
62
  status!(:failed)
63
63
  @step_match = NoStepMatch.new(@step, @name)
64
64
  end
65
65
  step_mother.step_visited(self)
66
66
  end
67
67
 
68
- def failed(exception, clear_backtrace)
69
- @exception = exception
70
- @exception.set_backtrace([]) if clear_backtrace
71
- @exception.backtrace << @step.backtrace_line unless @step.backtrace_line.nil?
68
+ def failed(options, e, clear_backtrace)
69
+ e.set_backtrace([]) if clear_backtrace
70
+ e.backtrace << @step.backtrace_line unless @step.backtrace_line.nil?
71
+ @exception = e
72
+ if(options[:strict] || !(Undefined === e) || e.nested?)
73
+ @reported_exception = e
74
+ else
75
+ @reported_exception = nil
76
+ end
72
77
  end
73
78
 
74
79
  def status!(status)
@@ -52,6 +52,10 @@ module Cucumber
52
52
  def visit_background_name(keyword, name, file_colon_line, source_indent)
53
53
  end
54
54
 
55
+ def visit_examples_array(examples_array)
56
+ examples_array.accept(self)
57
+ end
58
+
55
59
  def visit_examples(examples)
56
60
  examples.accept(self)
57
61
  end
@@ -13,6 +13,9 @@ module Cucumber
13
13
  'junit' => 'Cucumber::Formatter::Junit'
14
14
  }
15
15
  DEFAULT_FORMAT = 'pretty'
16
+ DRB_FLAG = '--drb'
17
+ PROFILE_SHORT_FLAG = '-p'
18
+ PROFILE_LONG_FLAG = '--profile'
16
19
 
17
20
  attr_reader :paths
18
21
  attr_reader :options
@@ -28,8 +31,10 @@ module Cucumber
28
31
  end
29
32
 
30
33
  def parse!(args)
31
- @args = args
32
- return parse_args_from_profile('default') if @args.empty?
34
+ @args = args.empty? ? args_from_profile('default') : args
35
+ expand_profiles_into_args
36
+ return if parse_drb
37
+
33
38
  @args.extend(::OptionParser::Arguable)
34
39
 
35
40
  @args.options do |opts|
@@ -100,8 +105,8 @@ module Cucumber
100
105
  opts.on("-e", "--exclude PATTERN", "Don't run feature files or require ruby files matching PATTERN") do |v|
101
106
  @options[:excludes] << Regexp.new(v)
102
107
  end
103
- opts.on("-p", "--profile PROFILE", "Pull commandline arguments from cucumber.yml.") do |v|
104
- parse_args_from_profile(v)
108
+ opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", "Pull commandline arguments from cucumber.yml.") do |v|
109
+ # Processing of this is done previsouly so that the DRb flag can be detected within profiles.
105
110
  end
106
111
  opts.on("-c", "--[no-]color",
107
112
  "Whether or not to use ANSI color in the output. Cucumber decides",
@@ -158,6 +163,9 @@ module Cucumber
158
163
  opts.on("--no-diff", "Disable diff output on failing expectations.") do
159
164
  @options[:diff_enabled] = false
160
165
  end
166
+ opts.on(DRB_FLAG, "Run features against a DRb server. (i.e. with the spork gem)") do
167
+ # Processing of this is done previsouly in order to short circuit args from being lost.
168
+ end
161
169
  opts.on_tail("--version", "Show version.") do
162
170
  @out_stream.puts VERSION::STRING
163
171
  Kernel.exit
@@ -176,7 +184,7 @@ module Cucumber
176
184
  raise("You can't use both --strict and --wip") if @options[:strict] && @options[:wip]
177
185
 
178
186
  # Whatever is left after option parsing is the FILE arguments
179
- @paths += args
187
+ @paths += @args
180
188
  end
181
189
 
182
190
  def verbose?
@@ -199,6 +207,10 @@ module Cucumber
199
207
  @options[:diff_enabled]
200
208
  end
201
209
 
210
+ def drb?
211
+ @drb
212
+ end
213
+
202
214
  def load_language
203
215
  if Cucumber.language_incomplete?(@options[:lang])
204
216
  list_keywords_and_exit(@options[:lang])
@@ -313,7 +325,15 @@ module Cucumber
313
325
  downcase
314
326
  end
315
327
 
316
- def parse_args_from_profile(profile)
328
+ def expand_profiles_into_args
329
+ while (profile_index = @args.index(PROFILE_SHORT_FLAG) || @args.index(PROFILE_LONG_FLAG)) do
330
+ @args.delete_at(profile_index)
331
+ @args[profile_index] = args_from_profile(@args[profile_index])
332
+ @args.flatten!
333
+ end
334
+ end
335
+
336
+ def args_from_profile(profile)
317
337
  unless cucumber_yml.has_key?(profile)
318
338
  raise(<<-END_OF_ERROR)
319
339
  Could not find profile: '#{profile}'
@@ -334,7 +354,7 @@ Defined profiles in cucumber.yml:
334
354
  else
335
355
  raise "The '#{profile}' profile in cucumber.yml was a #{args_from_yml.class}. It must be a String or Array"
336
356
  end
337
- parse!(args_from_yml)
357
+ args_from_yml
338
358
  end
339
359
 
340
360
  def cucumber_yml
@@ -346,7 +366,7 @@ Defined profiles in cucumber.yml:
346
366
  require 'yaml'
347
367
  begin
348
368
  @cucumber_yml = YAML::load(IO.read('cucumber.yml'))
349
- rescue Exception => e
369
+ rescue StandardError => e
350
370
  raise(YmlLoadError,"cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentation on correct profile usage.\n")
351
371
  end
352
372
 
@@ -370,6 +390,10 @@ Defined profiles in cucumber.yml:
370
390
  Kernel.exit
371
391
  end
372
392
 
393
+ def parse_drb
394
+ @drb = @args.delete(DRB_FLAG) ? true : false
395
+ end
396
+
373
397
  def default_options
374
398
  {
375
399
  :strict => false,
@@ -0,0 +1,21 @@
1
+ require "drb/drb"
2
+ # This code was taken from the RSpec project and slightly modified.
3
+
4
+ module Cucumber
5
+ module Cli
6
+ # Runs features on a DRB server, originally created with Spork compatibility in mind.
7
+ class DRbClient
8
+ def self.run(args, error_stream, out_stream)
9
+ begin
10
+ # See http://redmine.ruby-lang.org/issues/show/496 as to why we specify localhost:0
11
+ DRb.start_service("druby://localhost:0")
12
+ feature_server = DRbObject.new_with_uri("druby://127.0.0.1:8990")
13
+ feature_server.run(args, error_stream, out_stream)
14
+ true
15
+ rescue DRb::DRbConnError
16
+ false
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end