cucumber 0.3.5 → 0.3.6

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.
@@ -1,3 +1,21 @@
1
+ == 0.3.6
2
+
3
+ Kanban! With this release you can tag features or scenarios that are work in progress
4
+ with a tag and use the new --wip switch.
5
+
6
+ Another handy feature in this release is that you can package your own formatters in RubyGems.
7
+
8
+ === New features
9
+ * New --wip switch. See http://www.jroller.com/perryn/entry/bdd_on_a_multi_disciplined (Perryn Fowler)
10
+ * Added a AfterStep hook (Luke Melia)
11
+ * New aliases for Vietnamese (Ngoc Dao)
12
+ * Automatic require of custom formatters. --require is no longer needed to load them, and they can be in Ruby gems. (Aslak Hellesøy)
13
+ * Lazy loading of built-in formatters. Should improve startup time a little bit.
14
+
15
+ === Bugfixes
16
+ * Gracefully handle exceptions in After block (#330 Matt Wynne)
17
+ * Feature with only Background doesn't run hooks (#314, #329 Aslak Hellesøy)
18
+
1
19
  == 0.3.5 2009-05-14
2
20
 
3
21
  Let's make a new release today because two annoying bugs are fixed.
@@ -190,7 +190,6 @@ examples/self_test/features/sample.feature
190
190
  examples/self_test/features/search_sample.feature
191
191
  examples/self_test/features/step_definitions/sample_steps.rb
192
192
  examples/self_test/features/support/env.rb
193
- examples/self_test/features/support/tag_count_formatter.rb
194
193
  examples/self_test/features/tons_of_cukes.feature
195
194
  examples/self_test/features/undefined_multiline_args.feature
196
195
  examples/sinatra/Rakefile
@@ -238,6 +237,8 @@ examples/watir/Rakefile
238
237
  examples/watir/features/search.feature
239
238
  examples/watir/features/step_definitons/search_steps.rb
240
239
  examples/watir/features/support/env.rb
240
+ features/after_block_exceptions.feature
241
+ features/after_step_block_exceptions.feature
241
242
  features/background.feature
242
243
  features/cucumber_cli.feature
243
244
  features/cucumber_cli_diff_disabled.feature
@@ -253,6 +254,7 @@ features/step_definitions/cucumber_steps.rb
253
254
  features/step_definitions/extra_steps.rb
254
255
  features/support/env.rb
255
256
  features/usage.feature
257
+ features/work_in_progress.feature
256
258
  gem_tasks/deployment.rake
257
259
  gem_tasks/environment.rake
258
260
  gem_tasks/features.rake
@@ -293,7 +295,6 @@ lib/cucumber/core_ext/exception.rb
293
295
  lib/cucumber/core_ext/instance_exec.rb
294
296
  lib/cucumber/core_ext/proc.rb
295
297
  lib/cucumber/core_ext/string.rb
296
- lib/cucumber/formatter.rb
297
298
  lib/cucumber/formatter/ansicolor.rb
298
299
  lib/cucumber/formatter/color_io.rb
299
300
  lib/cucumber/formatter/console.rb
@@ -305,6 +306,7 @@ lib/cucumber/formatter/pretty.rb
305
306
  lib/cucumber/formatter/profile.rb
306
307
  lib/cucumber/formatter/progress.rb
307
308
  lib/cucumber/formatter/rerun.rb
309
+ lib/cucumber/formatter/tag_cloud.rb
308
310
  lib/cucumber/formatter/unicode.rb
309
311
  lib/cucumber/formatter/usage.rb
310
312
  lib/cucumber/formatters/unicode.rb
@@ -0,0 +1,97 @@
1
+ Feature: After Block Exceptions
2
+ In order to use custom assertions at the end of each scenario
3
+ As a developer
4
+ I want exceptions raised in After blocks to be handled gracefully and reported by the formatters
5
+
6
+ Background:
7
+ Given a standard Cucumber project directory structure
8
+ And a file named "features/step_definitions/steps.rb" with:
9
+ """
10
+ Given /^this step does something naughty$/ do
11
+ @naughty = true
12
+ end
13
+
14
+ Given /^this step works$/ do
15
+ end
16
+ """
17
+ And a file named "features/support/env.rb" with:
18
+ """
19
+ class NaughtyScenarioException < Exception; end
20
+ After do
21
+ if @naughty
22
+ raise NaughtyScenarioException.new("This scenario has been very very naughty")
23
+ end
24
+ end
25
+ """
26
+
27
+ Scenario: Handle Exception in standard scenario step and carry on
28
+ Given a file named "features/naughty_step_in_scenario.feature" with:
29
+ """
30
+ Feature: Sample
31
+
32
+ Scenario: Naughty Step
33
+ Given this step does something naughty
34
+
35
+ Scenario: Success
36
+ Given this step works
37
+ """
38
+ When I run cucumber features
39
+ Then it should fail with
40
+ """
41
+ Feature: Sample
42
+
43
+ Scenario: Naughty Step # features/naughty_step_in_scenario.feature:3
44
+ Given this step does something naughty # features/step_definitions/steps.rb:1
45
+ This scenario has been very very naughty (NaughtyScenarioException)
46
+ ./features/support/env.rb:4:in `After'
47
+
48
+ Scenario: Success # features/naughty_step_in_scenario.feature:6
49
+ Given this step works # features/step_definitions/steps.rb:5
50
+
51
+ 2 scenarios (1 failed, 1 passed)
52
+ 2 steps (2 passed)
53
+
54
+ """
55
+
56
+ Scenario: Handle Exception in scenario outline table row and carry on
57
+ Given a file named "features/naughty_step_in_scenario_outline.feature" with:
58
+ """
59
+ Feature: Sample
60
+
61
+ Scenario Outline: Naughty Step
62
+ Given this step <Might Work>
63
+
64
+ Examples:
65
+ | Might Work |
66
+ | works |
67
+ | does something naughty |
68
+ | works |
69
+
70
+ Scenario: Success
71
+ Given this step works
72
+
73
+ """
74
+ When I run cucumber features
75
+ Then it should fail with
76
+ """
77
+ Feature: Sample
78
+
79
+ Scenario Outline: Naughty Step # features/naughty_step_in_scenario_outline.feature:3
80
+ Given this step <Might Work> # features/step_definitions/steps.rb:5
81
+
82
+ Examples:
83
+ | Might Work |
84
+ | works |
85
+ | does something naughty |
86
+ This scenario has been very very naughty (NaughtyScenarioException)
87
+ ./features/support/env.rb:4:in `After'
88
+ | works |
89
+
90
+ Scenario: Success # features/naughty_step_in_scenario_outline.feature:12
91
+ Given this step works # features/step_definitions/steps.rb:5
92
+
93
+ 4 scenarios (1 failed, 3 passed)
94
+ 4 steps (4 passed)
95
+
96
+ """
97
+
@@ -0,0 +1,99 @@
1
+ Feature: AfterStep Block Exceptions
2
+ In order to use custom assertions at the end of each step
3
+ As a developer
4
+ I want exceptions raised in AfterStep blocks to be handled gracefully and reported by the formatters
5
+
6
+ Background:
7
+ Given a standard Cucumber project directory structure
8
+ And a file named "features/step_definitions/steps.rb" with:
9
+ """
10
+ Given /^this step does something naughty$/ do
11
+ @naughty = true
12
+ end
13
+
14
+ Given /^this step works$/ do
15
+ end
16
+ """
17
+ And a file named "features/support/env.rb" with:
18
+ """
19
+ class NaughtyStepException < Exception; end
20
+ AfterStep do
21
+ if @naughty
22
+ raise NaughtyStepException.new("This step has been very very naughty")
23
+ end
24
+ end
25
+ """
26
+
27
+ Scenario: Handle Exception in standard scenario step and carry on
28
+ Given a file named "features/naughty_step_in_scenario.feature" with:
29
+ """
30
+ Feature: Sample
31
+
32
+ Scenario: Naughty Step
33
+ Given this step does something naughty
34
+
35
+ Scenario: Success
36
+ Given this step works
37
+ """
38
+ When I run cucumber features
39
+ Then it should fail with
40
+ """
41
+ Feature: Sample
42
+
43
+ Scenario: Naughty Step # features/naughty_step_in_scenario.feature:3
44
+ Given this step does something naughty # features/step_definitions/steps.rb:1
45
+ This step has been very very naughty (NaughtyStepException)
46
+ ./features/support/env.rb:4:in `AfterStep'
47
+ features/naughty_step_in_scenario.feature:4:in `Given this step does something naughty'
48
+
49
+ Scenario: Success # features/naughty_step_in_scenario.feature:6
50
+ Given this step works # features/step_definitions/steps.rb:5
51
+
52
+ 2 scenarios (1 failed, 1 passed)
53
+ 2 steps (1 failed, 1 passed)
54
+
55
+ """
56
+
57
+ Scenario: Handle Exception in scenario outline table row and carry on
58
+ Given a file named "features/naughty_step_in_scenario_outline.feature" with:
59
+ """
60
+ Feature: Sample
61
+
62
+ Scenario Outline: Naughty Step
63
+ Given this step <Might Work>
64
+
65
+ Examples:
66
+ | Might Work |
67
+ | works |
68
+ | does something naughty |
69
+ | works |
70
+
71
+ Scenario: Success
72
+ Given this step works
73
+
74
+ """
75
+ When I run cucumber features
76
+ Then it should fail with
77
+ """
78
+ Feature: Sample
79
+
80
+ Scenario Outline: Naughty Step # features/naughty_step_in_scenario_outline.feature:3
81
+ Given this step <Might Work> # features/step_definitions/steps.rb:5
82
+
83
+ Examples:
84
+ | Might Work |
85
+ | works |
86
+ | does something naughty |
87
+ This step has been very very naughty (NaughtyStepException)
88
+ ./features/support/env.rb:4:in `AfterStep'
89
+ features/naughty_step_in_scenario_outline.feature:4:in `Given this step <Might Work>'
90
+ | works |
91
+
92
+ Scenario: Success # features/naughty_step_in_scenario_outline.feature:12
93
+ Given this step works # features/step_definitions/steps.rb:5
94
+
95
+ 4 scenarios (1 failed, 3 passed)
96
+ 4 steps (1 failed, 3 passed)
97
+
98
+ """
99
+
@@ -253,5 +253,50 @@ Feature: backgrounds
253
253
 
254
254
  """
255
255
 
256
- @josephwilk
257
- Scenario: run a scenario showing explicit background steps --explicit-background
256
+ Scenario: https://rspec.lighthouseapp.com/projects/16211/tickets/329
257
+ Given a standard Cucumber project directory structure
258
+ And a file named "features/only_background_and_hooks.feature" with:
259
+ """
260
+ Feature: woo yeah
261
+
262
+ Background:
263
+ Given whatever
264
+
265
+ """
266
+ And a file named "features/only_background_and_hooks_steps.rb" with:
267
+ """
268
+ require 'spec/expectations'
269
+
270
+ Before do
271
+ $before = true
272
+ end
273
+
274
+ After do
275
+ $after = true
276
+ end
277
+
278
+ Given /^whatever$/ do
279
+ $before.should == true
280
+ $step = true
281
+ end
282
+
283
+ at_exit do
284
+ $before.should == true
285
+ $step.should == true
286
+ $after.should == true
287
+ end
288
+ """
289
+ When I run cucumber features/only_background_and_hooks.feature
290
+ Then it should pass
291
+ And the output should be
292
+ """
293
+ Feature: woo yeah
294
+
295
+ Background: # features/only_background_and_hooks.feature:3
296
+ Given whatever # features/only_background_and_hooks_steps.rb:11
297
+
298
+ 0 scenarios
299
+ 1 step (1 passed)
300
+
301
+ """
302
+ And STDERR should be empty
@@ -1,7 +1,7 @@
1
1
  Feature: Custom Formatter
2
2
 
3
3
  Scenario: count tags
4
- When I run cucumber --format Tag::Count features
4
+ When I run cucumber --format Cucumber::Formatter::TagCloud features
5
5
  Then it should fail with
6
6
  """
7
7
  | after_file | background_tagged_before_on_outline | four | lots | one | three | two |
@@ -62,6 +62,10 @@ Then /^the output should not contain$/ do |text|
62
62
  last_stdout.should_not include(text)
63
63
  end
64
64
 
65
+ Then /^the output should be$/ do |text|
66
+ last_stdout.should == text
67
+ end
68
+
65
69
  # http://diffxml.sourceforge.net/
66
70
  Then /^"(.*)" should contain XML$/ do |file, xml|
67
71
  t = Tempfile.new('cucumber-junit')
@@ -86,6 +90,10 @@ Then /^STDERR should match$/ do |text|
86
90
  last_stderr.should =~ /#{text}/
87
91
  end
88
92
 
93
+ Then /^STDERR should be empty$/ do
94
+ last_stderr.should == ""
95
+ end
96
+
89
97
  Then /^"(.*)" should exist$/ do |file|
90
98
  File.exists?(file).should be_true
91
99
  FileUtils.rm(file)
@@ -0,0 +1,146 @@
1
+ Feature: Cucumber --work-in-progress switch
2
+ In order to ensure that feature scenarios do not pass until they are expected to
3
+ Developers should be able to run cucumber in a mode that
4
+ - will fail if any scenario passes completely
5
+ - will not fail otherwise
6
+
7
+ Background: A passing and a pending feature
8
+ Given a standard Cucumber project directory structure
9
+ Given a file named "features/wip.feature" with:
10
+ """
11
+ Feature: WIP
12
+ @failing
13
+ Scenario: Failing
14
+ Given a failing step
15
+
16
+ @undefined
17
+ Scenario: Undefined
18
+ Given an undefined step
19
+
20
+ @pending
21
+ Scenario: Pending
22
+ Given a pending step
23
+
24
+ @passing
25
+ Scenario: Passing
26
+ Given a passing step
27
+ """
28
+ And a file named "features/passing_outline.feature" with:
29
+ """
30
+ Feature: Not WIP
31
+ Scenario Outline: Passing
32
+ Given a <what> step
33
+
34
+ Examples:
35
+ | what |
36
+ | passing |
37
+ """
38
+ And a file named "features/step_definitions/steps.rb" with:
39
+ """
40
+ Given /^a failing step$/ do
41
+ raise "I fail"
42
+ end
43
+
44
+ Given /^a passing step$/ do
45
+ end
46
+
47
+ Given /^a pending step$/ do
48
+ pending
49
+ end
50
+ """
51
+
52
+ Scenario: Pass with Failing Scenarios
53
+ When I run cucumber -q -w -t @failing features/wip.feature
54
+ Then it should pass with
55
+ """
56
+ Feature: WIP
57
+
58
+ @failing
59
+ Scenario: Failing
60
+ Given a failing step
61
+ I fail (RuntimeError)
62
+ ./features/step_definitions/steps.rb:2:in `/^a failing step$/'
63
+ features/wip.feature:4:in `Given a failing step'
64
+
65
+ 1 scenario (1 failed)
66
+ 1 step (1 failed)
67
+
68
+ """
69
+
70
+ Scenario: Pass with Undefined Scenarios
71
+ When I run cucumber -q -w -t @undefined features/wip.feature
72
+ Then it should pass with
73
+ """
74
+ Feature: WIP
75
+
76
+ @undefined
77
+ Scenario: Undefined
78
+ Given an undefined step
79
+
80
+ 1 scenario (1 undefined)
81
+ 1 step (1 undefined)
82
+
83
+ """
84
+
85
+ Scenario: Pass with Undefined Scenarios
86
+ When I run cucumber -q -w -t @pending features/wip.feature
87
+ Then it should pass with
88
+ """
89
+ Feature: WIP
90
+
91
+ @pending
92
+ Scenario: Pending
93
+ Given a pending step
94
+ TODO (Cucumber::Pending)
95
+ ./features/step_definitions/steps.rb:9:in `/^a pending step$/'
96
+ features/wip.feature:12:in `Given a pending step'
97
+
98
+ 1 scenario (1 pending)
99
+ 1 step (1 pending)
100
+
101
+ """
102
+
103
+ Scenario: Fail with Passing Scenarios
104
+ When I run cucumber -q -w -t @passing features/wip.feature
105
+ Then it should fail with
106
+ """
107
+ Feature: WIP
108
+
109
+ @passing
110
+ Scenario: Passing
111
+ Given a passing step
112
+
113
+ 1 scenario (1 passed)
114
+ 1 step (1 passed)
115
+
116
+ The --wip switch was used, so I didn't expect anything to pass. These scenarios passed:
117
+ (::) passed scenarios (::)
118
+
119
+ features/wip.feature:15:in `Scenario: Passing'
120
+
121
+
122
+ """
123
+
124
+ Scenario: Fail with Passing Scenario Outline
125
+ When I run cucumber -q -w features/passing_outline.feature
126
+ Then it should fail with
127
+ """
128
+ Feature: Not WIP
129
+
130
+ Scenario Outline: Passing
131
+ Given a <what> step
132
+
133
+ Examples:
134
+ | what |
135
+ | passing |
136
+
137
+ 1 scenario (1 passed)
138
+ 1 step (1 passed)
139
+
140
+ The --wip switch was used, so I didn't expect anything to pass. These scenarios passed:
141
+ (::) passed scenarios (::)
142
+
143
+ features/passing_outline.feature:7:in `| passing |'
144
+
145
+
146
+ """
@@ -14,6 +14,7 @@ module Cucumber
14
14
  KEYWORD_KEYS = %w{name native encoding feature background scenario scenario_outline examples given when then but}
15
15
 
16
16
  class << self
17
+ # The currently active language
17
18
  attr_reader :lang
18
19
 
19
20
  def load_language(lang) #:nodoc:
@@ -28,15 +28,15 @@ module Cucumber
28
28
  visitor.step_mother.before(hook_context)
29
29
  visitor.visit_steps(@step_invocations)
30
30
  @failed = @step_invocations.detect{|step_invocation| step_invocation.exception}
31
- visitor.step_mother.after(hook_context) if @failed
31
+ visitor.step_mother.after(hook_context) if @failed || @feature_elements.empty?
32
32
  end
33
33
 
34
34
  def accept_hook?(hook)
35
35
  if hook_context != self
36
36
  hook_context.accept_hook?(hook)
37
37
  else
38
- # We have no scenarios
39
- false
38
+ # We have no scenarios, just ask our feature
39
+ @feature.accept_hook?(hook)
40
40
  end
41
41
  end
42
42
 
@@ -36,7 +36,9 @@ module Cucumber
36
36
  end
37
37
 
38
38
  class ExampleCells < Cells
39
+
39
40
  def create_step_invocations!(scenario_outline)
41
+ @scenario_outline = scenario_outline
40
42
  @step_invocations = scenario_outline.step_invocations(self)
41
43
  end
42
44
 
@@ -62,6 +64,8 @@ module Cucumber
62
64
  @cells.each do |cell|
63
65
  visitor.visit_table_cell(cell)
64
66
  end
67
+
68
+ visitor.visit_exception(@scenario_exception, :failed) if @scenario_exception
65
69
  end
66
70
  end
67
71
  end
@@ -69,22 +73,36 @@ module Cucumber
69
73
  def accept_hook?(hook)
70
74
  @table.accept_hook?(hook)
71
75
  end
72
-
76
+
77
+ def exception
78
+ @exception || @scenario_exception
79
+ end
80
+
81
+ def fail!(exception)
82
+ @scenario_exception = exception
83
+ end
84
+
73
85
  # Returns true if one or more steps failed
74
86
  def failed?
75
- @step_invocations.failed?
87
+ @step_invocations.failed? || !!@scenario_exception
76
88
  end
77
89
 
78
90
  # Returns true if all steps passed
79
91
  def passed?
80
- @step_invocations.passed?
92
+ !failed?
81
93
  end
82
94
 
83
95
  # Returns the status
84
96
  def status
97
+ return :failed if @scenario_exception
85
98
  @step_invocations.status
86
99
  end
87
100
 
101
+ def backtrace_line
102
+ name = "| #{@cells.collect{|c| c.value }.join(' | ')} |"
103
+ @scenario_outline.backtrace_line(name, line)
104
+ end
105
+
88
106
  private
89
107
 
90
108
  def header?
@@ -28,25 +28,31 @@ module Cucumber
28
28
  visitor.step_mother.before_and_after(self, skip) do
29
29
  visitor.visit_steps(@steps)
30
30
  end
31
+ visitor.visit_exception(@exception, :failed) if @exception
31
32
  end
32
33
 
33
34
  # Returns true if one or more steps failed
34
35
  def failed?
35
- @steps.failed?
36
+ @steps.failed? || !!@exception
37
+ end
38
+
39
+ def fail!(exception)
40
+ @exception = exception
36
41
  end
37
42
 
38
43
  # Returns true if all steps passed
39
44
  def passed?
40
- @steps.passed?
45
+ !failed?
41
46
  end
42
47
 
43
48
  # Returns the first exception (if any)
44
49
  def exception
45
- @steps.exception
50
+ @exception || @steps.exception
46
51
  end
47
52
 
48
53
  # Returns the status
49
54
  def status
55
+ return :failed if @exception
50
56
  @steps.status
51
57
  end
52
58
 
@@ -30,6 +30,7 @@ module Cucumber
30
30
  begin
31
31
  step_mother.current_world.__cucumber_current_step = self if step_mother.current_world # Nil in Pure Java
32
32
  @step_match.invoke(step_mother.current_world, @multiline_arg)
33
+ step_mother.after_step
33
34
  status!(:passed)
34
35
  rescue Pending => e
35
36
  failed(e, false)
@@ -3,7 +3,15 @@ module Cucumber
3
3
  class YmlLoadError < StandardError; end
4
4
 
5
5
  class Configuration
6
- FORMATS = %w{pretty profile progress rerun junit}
6
+ BUILTIN_FORMATS = {
7
+ 'html' => 'Cucumber::Formatter::Html',
8
+ 'pretty' => 'Cucumber::Formatter::Pretty',
9
+ 'profile' => 'Cucumber::Formatter::Profile',
10
+ 'progress' => 'Cucumber::Formatter::Progress',
11
+ 'rerun' => 'Cucumber::Formatter::Rerun',
12
+ 'usage' => 'Cucumber::Formatter::Usage',
13
+ 'junit' => 'Cucumber::Formatter::Junit'
14
+ }
7
15
  DEFAULT_FORMAT = 'pretty'
8
16
 
9
17
  attr_reader :paths
@@ -57,12 +65,15 @@ module Cucumber
57
65
  end
58
66
  opts.on("-f FORMAT", "--format FORMAT",
59
67
  "How to format features (Default: #{DEFAULT_FORMAT})",
60
- "Available formats: #{FORMATS.join(", ")}",
61
- "You can also provide your own formatter classes as long",
62
- "as they have been previously required using --require or",
63
- "if they are in the folder structure such that cucumber",
64
- "will require them automatically.",
65
- "This option can be specified multiple times.") do |v|
68
+ "Available formats: #{BUILTIN_FORMATS.keys.sort.join(", ")}",
69
+ "FORMAT can also be the fully qualified class name of",
70
+ "your own custom formatter. If the class isn't loaded,",
71
+ "Cucumber will attempt to require a file with a relative",
72
+ "file name that is the underscore name of the class name.",
73
+ "Example: --format Foo::BarZap -> Cucumber will look for",
74
+ "foo/bar_zap.rb. You can place the file with this relative",
75
+ "path underneath your features/support directory or anywhere",
76
+ "on Ruby's LOAD_PATH, for example in a Ruby gem.") do |v|
66
77
  @options[:formats][v] = @out_stream
67
78
  @active_format = v
68
79
  end
@@ -132,6 +143,9 @@ module Cucumber
132
143
  opts.on("-S", "--strict", "Fail if there are any undefined steps.") do
133
144
  @options[:strict] = true
134
145
  end
146
+ opts.on("-w", "--wip", "Fail if there are any passing scenarios.") do
147
+ @options[:wip] = true
148
+ end
135
149
  opts.on("-v", "--verbose", "Show the files and features loaded.") do
136
150
  @options[:verbose] = true
137
151
  end
@@ -156,6 +170,8 @@ module Cucumber
156
170
  @options[:snippets] = true if !@quiet && @options[:snippets].nil?
157
171
  @options[:source] = true if !@quiet && @options[:source].nil?
158
172
 
173
+ raise("You can't use both --strict and --wip") if @options[:strict] && @options[:wip]
174
+
159
175
  # Whatever is left after option parsing is the FILE arguments
160
176
  @paths += args
161
177
  end
@@ -168,6 +184,10 @@ module Cucumber
168
184
  @options[:strict]
169
185
  end
170
186
 
187
+ def wip?
188
+ @options[:wip]
189
+ end
190
+
171
191
  def guess?
172
192
  @options[:guess]
173
193
  end
@@ -223,14 +243,8 @@ module Cucumber
223
243
  end
224
244
 
225
245
  def formatter_class(format)
226
- case format
227
- when 'html' then Formatter::Html
228
- when 'pretty' then Formatter::Pretty
229
- when 'profile' then Formatter::Profile
230
- when 'progress' then Formatter::Progress
231
- when 'rerun' then Formatter::Rerun
232
- when 'usage' then Formatter::Usage
233
- when 'junit' then Formatter::JUnit
246
+ if(builtin = BUILTIN_FORMATS[format])
247
+ constantize(builtin)
234
248
  else
235
249
  constantize(format)
236
250
  end
@@ -271,14 +285,28 @@ module Cucumber
271
285
  end
272
286
 
273
287
  def constantize(camel_cased_word)
274
- names = camel_cased_word.split('::')
275
- names.shift if names.empty? || names.first.empty?
288
+ begin
289
+ names = camel_cased_word.split('::')
290
+ names.shift if names.empty? || names.first.empty?
276
291
 
277
- constant = Object
278
- names.each do |name|
279
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
292
+ constant = Object
293
+ names.each do |name|
294
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
295
+ end
296
+ constant
297
+ rescue NameError
298
+ require underscore(camel_cased_word)
299
+ retry
280
300
  end
281
- constant
301
+ end
302
+
303
+ # Snagged from active_support
304
+ def underscore(camel_cased_word)
305
+ camel_cased_word.to_s.gsub(/::/, '/').
306
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
307
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
308
+ tr("-", "_").
309
+ downcase
282
310
  end
283
311
 
284
312
  def parse_args_from_profile(profile)
@@ -295,10 +323,10 @@ Defined profiles in cucumber.yml:
295
323
 
296
324
  case(args_from_yml)
297
325
  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*$/
326
+ raise "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/
299
327
  args_from_yml = args_from_yml.split(' ')
300
328
  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?
329
+ raise "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml.empty?
302
330
  else
303
331
  raise "The '#{profile}' profile in cucumber.yml was a #{args_from_yml.class}. It must be a String or Array"
304
332
  end
@@ -2,7 +2,7 @@ require 'optparse'
2
2
  require 'cucumber'
3
3
  require 'ostruct'
4
4
  require 'cucumber/parser'
5
- require 'cucumber/formatter'
5
+ require 'cucumber/formatter/color_io'
6
6
  require 'cucumber/cli/language_help_formatter'
7
7
  require 'cucumber/cli/configuration'
8
8
 
@@ -40,8 +40,12 @@ module Cucumber
40
40
  step_mother.visitor = visitor # Needed to support World#announce
41
41
  visitor.visit_features(features)
42
42
 
43
- failure = step_mother.steps(:failed).any? ||
43
+ failure = if configuration.wip?
44
+ step_mother.scenarios(:passed).any?
45
+ else
46
+ step_mother.scenarios(:failed).any? ||
44
47
  (configuration.strict? && step_mother.steps(:undefined).any?)
48
+ end
45
49
  end
46
50
 
47
51
  def load_plain_text_features
@@ -85,6 +85,15 @@ module Cucumber
85
85
  @io.flush
86
86
  end
87
87
 
88
+ def print_passing_wip(options)
89
+ return unless options[:wip]
90
+ passed = step_mother.scenarios(:passed)
91
+ if passed.any?
92
+ @io.puts "\nThe --wip switch was used, so I didn't expect anything to pass. These scenarios passed:"
93
+ print_elements(passed, :passed, "scenarios")
94
+ end
95
+ end
96
+
88
97
  def announce(announcement)
89
98
  @io.puts
90
99
  @io.puts(format_string(announcement, :tag))
@@ -7,7 +7,7 @@ end
7
7
 
8
8
  module Cucumber
9
9
  module Formatter
10
- class JUnit < Cucumber::Ast::Visitor
10
+ class Junit < Cucumber::Ast::Visitor
11
11
 
12
12
  def initialize(step_mother, io, options)
13
13
  super(step_mother)
@@ -177,6 +177,7 @@ module Cucumber
177
177
  def print_summary
178
178
  print_counts
179
179
  print_snippets(@options)
180
+ print_passing_wip(@options)
180
181
  end
181
182
 
182
183
  end
@@ -35,6 +35,7 @@ module Cucumber
35
35
  print_steps(:failed)
36
36
  print_counts
37
37
  print_snippets(@options)
38
+ print_passing_wip(@options)
38
39
  end
39
40
 
40
41
  CHARS = {
@@ -0,0 +1,27 @@
1
+ module Cucumber
2
+ module Formatter
3
+ # Custom formatter that prints a tag cloud
4
+ class TagCloud < Cucumber::Ast::Visitor
5
+ def initialize(step_mother, io, options)
6
+ super(step_mother)
7
+ @io = io
8
+ @counts = Hash.new{|h,k| h[k] = 0}
9
+ end
10
+
11
+ def visit_features(features)
12
+ super
13
+ print_summary
14
+ end
15
+
16
+ def visit_tag_name(tag_name)
17
+ @counts[tag_name] += 1
18
+ end
19
+
20
+ def print_summary
21
+ matrix = @counts.to_a.sort{|paira, pairb| paira[0] <=> pairb[0]}.transpose
22
+ table = Cucumber::Ast::Table.new(matrix)
23
+ Cucumber::Formatter::Pretty.new(@step_mother, @io, {}).visit_multiline_arg(table)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -43,6 +43,36 @@
43
43
  and: و
44
44
  but: لكن
45
45
  space_after_keyword: true
46
+ "bg":
47
+ name: Bulgarian
48
+ native: български
49
+ encoding: UTF-8
50
+ feature: Функционалност
51
+ background: Предистория
52
+ scenario: Сценарий
53
+ scenario_outline: Рамка на сценарий
54
+ examples: Примери
55
+ given: Дадено
56
+ when: Когато
57
+ then: То
58
+ and: И
59
+ but: Но
60
+ space_after_keyword: true
61
+ "cat":
62
+ name: Catalan
63
+ native: català
64
+ encoding: UTF-8
65
+ background: Rerefons
66
+ feature: Característica
67
+ scenario: Escenari
68
+ scenario_outline: Esquema de l\'escenari
69
+ examples: Exemples
70
+ given: Donat
71
+ when: Quan
72
+ then: Aleshores
73
+ and: I
74
+ but: Però
75
+ space_after_keyword: true
46
76
  "cy":
47
77
  name: Welsh
48
78
  native: Cymraeg
@@ -161,21 +191,6 @@
161
191
  and: Y
162
192
  but: Pero
163
193
  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
179
194
  "et":
180
195
  name: Estonian
181
196
  native: eesti keel
@@ -296,6 +311,21 @@
296
311
  and: かつ
297
312
  but: しかし|但し
298
313
  space_after_keyword: false
314
+ "ko":
315
+ name: Korean
316
+ native: 한국어
317
+ encoding: UTF-8
318
+ background: 배경
319
+ feature: 기능
320
+ scenario: 시나리오
321
+ scenario_outline: 시나리오 개요
322
+ examples: 예
323
+ given: 조건
324
+ when: 만일
325
+ then: 그러면
326
+ and: 그리고
327
+ but: 하지만
328
+ space_after_keyword: false
299
329
  "lt":
300
330
  name: Lithuanian
301
331
  native: lietuvių kalba
@@ -455,6 +485,21 @@
455
485
  and: A
456
486
  but: Ale
457
487
  space_after_keyword: true
488
+ "vi":
489
+ name: Vietnamese
490
+ native: Tiếng Việt
491
+ encoding: UTF-8
492
+ feature: Tính năng
493
+ background: Bối cảnh
494
+ scenario: Tình huống|Kịch bản
495
+ scenario_outline: Khung tình huống|Khung kịch bản
496
+ examples: Dữ liệu
497
+ given: Biết|Cho
498
+ when: Khi
499
+ then: Thì
500
+ and: Và
501
+ but: Nhưng
502
+ space_after_keyword: true
458
503
  "zh-CN":
459
504
  name: Chinese simplified
460
505
  native: 简体中文
@@ -485,48 +530,3 @@
485
530
  and: 而且|並且
486
531
  but: 但是
487
532
  space_after_keyword: false
488
- "ko":
489
- name: Korean
490
- native: 한국어
491
- encoding: UTF-8
492
- background: 배경
493
- feature: 기능
494
- scenario: 시나리오
495
- scenario_outline: 시나리오 개요
496
- examples: 예
497
- given: 조건
498
- when: 만일
499
- then: 그러면
500
- and: 그리고
501
- but: 하지만
502
- space_after_keyword: false
503
- "bg":
504
- name: Bulgarian
505
- native: български
506
- encoding: UTF-8
507
- feature: Функционалност
508
- background: Предистория
509
- scenario: Сценарий
510
- scenario_outline: Рамка на сценарий
511
- examples: Примери
512
- given: Дадено
513
- when: Когато
514
- then: То
515
- and: И
516
- but: Но
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
@@ -68,13 +68,26 @@ module Cucumber #:nodoc:
68
68
 
69
69
  def self.bypass_rescue
70
70
  ActionController::Base.class_eval do
71
+ alias_method :rescue_action_without_bypass, :rescue_action
72
+
71
73
  def rescue_action(exception)
72
74
  raise exception
73
75
  end
74
76
  end
75
- ActionController::Dispatcher.class_eval do
76
- def self.failsafe_response(output, status, exception = nil)
77
- raise exception
77
+
78
+ begin
79
+ ActionController::Failsafe.class_eval do
80
+ alias_method :failsafe_response_without_bypass, :failsafe_response
81
+
82
+ def failsafe_response(exception)
83
+ raise exception
84
+ end
85
+ end
86
+ rescue NameError # Failsafe was introduced in Rails 2.3.2
87
+ ActionController::Dispatcher.class_eval do
88
+ def self.failsafe_response(output, status, exception = nil)
89
+ raise exception
90
+ end
78
91
  end
79
92
  end
80
93
  end
@@ -135,7 +135,8 @@ module Cucumber
135
135
  attr_accessor :fork
136
136
 
137
137
  # Define what profile to be used. When used with cucumber_opts it is simply appended to it. Will be ignored when CUCUMBER_OPTS is used.
138
- def profile=(profile)
138
+ attr_accessor :profile
139
+ def profile=(profile) #:nodoc:
139
140
  @profile = profile
140
141
  unless feature_list
141
142
  # TODO: remove once we completely remove these from the rake task.
@@ -143,7 +144,6 @@ module Cucumber
143
144
  @feature_list = [] # Don't use accessor to avoid deprecation warning.
144
145
  end
145
146
  end
146
- attr_reader :profile
147
147
 
148
148
  # Define Cucumber Rake task
149
149
  def initialize(task_name = "features", desc = "Run Features with Cucumber")
@@ -78,8 +78,16 @@ module Cucumber
78
78
  @tag_names.empty? || (@tag_names & tag_names).any?
79
79
  end
80
80
 
81
- def execute_in(world, scenario, location)
82
- world.cucumber_instance_exec(false, location, scenario, &@proc)
81
+ def execute_in(world, scenario, location, exception_fails_scenario = true)
82
+ begin
83
+ world.cucumber_instance_exec(false, location, scenario, &@proc)
84
+ rescue Exception => exception
85
+ if exception_fails_scenario
86
+ scenario.fail!(exception)
87
+ else
88
+ raise
89
+ end
90
+ end
83
91
  end
84
92
  end
85
93
 
@@ -143,6 +151,10 @@ module Cucumber
143
151
  register_hook(:after, tag_names, proc)
144
152
  end
145
153
 
154
+ def AfterStep(*tag_names, &proc)
155
+ register_hook(:after_step, tag_names, proc)
156
+ end
157
+
146
158
  def register_hook(phase, tags, proc)
147
159
  hook = Hook.new(tags, proc)
148
160
  hooks[phase] << hook
@@ -233,7 +245,9 @@ module Cucumber
233
245
 
234
246
  def before_and_after(scenario, skip=false)
235
247
  before(scenario) unless skip
236
- yield
248
+ @current_scenario = scenario
249
+ yield scenario
250
+ @current_scenario = nil
237
251
  after(scenario) unless skip
238
252
  scenario_visited(scenario)
239
253
  end
@@ -249,7 +263,11 @@ module Cucumber
249
263
  execute_after(scenario)
250
264
  nil_world!
251
265
  end
252
-
266
+
267
+ def after_step
268
+ execute_after_step(@current_scenario)
269
+ end
270
+
253
271
  private
254
272
 
255
273
  def max_step_definition_length
@@ -323,6 +341,13 @@ module Cucumber
323
341
  end
324
342
  end
325
343
 
344
+ def execute_after_step(scenario)
345
+ return if options[:dry_run]
346
+ hooks_for(:after_step, scenario).each do |hook|
347
+ hook.execute_in(@current_world, scenario, 'AfterStep', false)
348
+ end
349
+ end
350
+
326
351
  def scenario_visited(scenario)
327
352
  scenarios << scenario unless scenarios.index(scenario)
328
353
  end
@@ -2,7 +2,7 @@ module Cucumber #:nodoc:
2
2
  class VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 5
5
+ TINY = 6
6
6
  PATCH = nil # Set to nil for official release
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
@@ -1,4 +1,4 @@
1
- # This generator bootstraps a Rails project for use with Cucumber
1
+ # This generator generates a baic feature.
2
2
  class FeatureGenerator < Rails::Generator::NamedBase
3
3
  def manifest
4
4
  record do |m|
@@ -81,11 +81,8 @@ module Cli
81
81
  "/features/support/fooz.rb"
82
82
  ]
83
83
  end
84
-
85
-
86
84
  end
87
85
 
88
-
89
86
  it "should expand args from YAML file" do
90
87
  given_cucumber_yml_defined_as({'bongo' => '--require from/yml'})
91
88
 
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'cucumber/formatter/color_io'
2
3
 
3
4
  module Cucumber
4
5
  module Formatter
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'cucumber/formatter/progress'
2
3
 
3
4
  module Cucumber
4
5
  module Formatter
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Aslak Helles\xC3\xB8y"
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-15 00:00:00 +02:00
12
+ date: 2009-05-20 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -267,7 +267,6 @@ files:
267
267
  - examples/self_test/features/search_sample.feature
268
268
  - examples/self_test/features/step_definitions/sample_steps.rb
269
269
  - examples/self_test/features/support/env.rb
270
- - examples/self_test/features/support/tag_count_formatter.rb
271
270
  - examples/self_test/features/tons_of_cukes.feature
272
271
  - examples/self_test/features/undefined_multiline_args.feature
273
272
  - examples/sinatra/Rakefile
@@ -315,6 +314,8 @@ files:
315
314
  - examples/watir/features/search.feature
316
315
  - examples/watir/features/step_definitons/search_steps.rb
317
316
  - examples/watir/features/support/env.rb
317
+ - features/after_block_exceptions.feature
318
+ - features/after_step_block_exceptions.feature
318
319
  - features/background.feature
319
320
  - features/cucumber_cli.feature
320
321
  - features/cucumber_cli_diff_disabled.feature
@@ -330,6 +331,7 @@ files:
330
331
  - features/step_definitions/extra_steps.rb
331
332
  - features/support/env.rb
332
333
  - features/usage.feature
334
+ - features/work_in_progress.feature
333
335
  - gem_tasks/deployment.rake
334
336
  - gem_tasks/environment.rake
335
337
  - gem_tasks/features.rake
@@ -370,7 +372,6 @@ files:
370
372
  - lib/cucumber/core_ext/instance_exec.rb
371
373
  - lib/cucumber/core_ext/proc.rb
372
374
  - lib/cucumber/core_ext/string.rb
373
- - lib/cucumber/formatter.rb
374
375
  - lib/cucumber/formatter/ansicolor.rb
375
376
  - lib/cucumber/formatter/color_io.rb
376
377
  - lib/cucumber/formatter/console.rb
@@ -382,6 +383,7 @@ files:
382
383
  - lib/cucumber/formatter/profile.rb
383
384
  - lib/cucumber/formatter/progress.rb
384
385
  - lib/cucumber/formatter/rerun.rb
386
+ - lib/cucumber/formatter/tag_cloud.rb
385
387
  - lib/cucumber/formatter/unicode.rb
386
388
  - lib/cucumber/formatter/usage.rb
387
389
  - lib/cucumber/formatters/unicode.rb
@@ -1,25 +0,0 @@
1
- module Tag
2
- # Custom formatter that reports occurrences of each tag
3
- class Count < Cucumber::Ast::Visitor
4
- def initialize(step_mother, io, options)
5
- super(step_mother)
6
- @io = io
7
- @counts = Hash.new{|h,k| h[k] = 0}
8
- end
9
-
10
- def visit_features(features)
11
- super
12
- print_summary
13
- end
14
-
15
- def visit_tag_name(tag_name)
16
- @counts[tag_name] += 1
17
- end
18
-
19
- def print_summary
20
- matrix = @counts.to_a.sort{|paira, pairb| paira[0] <=> pairb[0]}.transpose
21
- table = Cucumber::Ast::Table.new(matrix)
22
- Cucumber::Formatter::Pretty.new(@step_mother, @io, {}).visit_multiline_arg(table)
23
- end
24
- end
25
- end
@@ -1 +0,0 @@
1
- %w{color_io pretty progress profile rerun html usage junit}.each{|n| require "cucumber/formatter/#{n}"}