cucumber 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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}"}