cucumber 0.3.9 → 0.3.10

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