cucumber 2.0.0.beta.1 → 2.0.0.beta.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 514eb451f10f568e1402ba020ee3ee6256032dca
4
- data.tar.gz: 1e21fe76f4e75993ef5effa867cddc1f0ef823ef
3
+ metadata.gz: ec39cd25b8f17917a20fd5af809119a9ecad0671
4
+ data.tar.gz: 43aced438654c8263f00185586f45c42daeedab0
5
5
  SHA512:
6
- metadata.gz: f6a83f29f65355640bd4df83bd8bcac5b086445318c76cf3250e7ae2db1aacdfe4b6753546f983111014b1536f7944176e959c9783cba9baa8a3305a3c745947
7
- data.tar.gz: 996994c7adba08dc88ca282cd1e7de86095940ae3780fc032b3a58d7c44635674802989f2aaaf13cf99a1d84837c1d3ee3d85fc8c77d964eb227acab0f54a130
6
+ metadata.gz: 8b7126a89b0f2e88fc81dfbd3aebf4998928f8f49033ba67a453a74525234b1d73f811aba9346bafe2c0274fbc6cc8b4deb45c5a0a4411e54cbf47c3e5ece726
7
+ data.tar.gz: 4c81bc4dd772677dc1888a4da72eaf8401093440274faac071beb52f9dc320e481e6b34735486aec2e3c3b914d9096e3b273591265e379082447efa0f288a9d1
data/History.md CHANGED
@@ -1,4 +1,14 @@
1
- ## [v2.0.0.pre (unreleased)](https://github.com/cucumber/cucumber/compare/v1.3.8...master)
1
+ ## [In Git](https://github.com/cucumber/cucumber/compare/v.2.0.0.beta.1...master)
2
+
3
+ ### Bugfixes
4
+
5
+ * Better reporting of exceptions in Before / After hooks ([723](https://github.com/cucumber/cucumber/pull/723) @mattwynne)
6
+
7
+ ### New features
8
+
9
+ * Support embedding images directly in HTML and JSON reports ([696](https://github.com/cucumber/cucumber/pull/696),[695](https://github.com/cucumber/cucumber/pull/695/files) @brasmusson)
10
+
11
+ ## [v2.0.0.beta.1 ](https://github.com/cucumber/cucumber/compare/v1.3.8...v.2.0.0.beta.1)
2
12
 
3
13
  Version 2.0 contains a major internal redesign, extracting the core logic of
4
14
  parsing and executing tests into a [separate gem](https://github.com/cucumber/cucumber-ruby-core).
@@ -13,6 +23,7 @@ all.
13
23
  * Better snippet comment ([579](https://github.com/cucumber/cucumber/pull/579) Jeff Nyman)
14
24
  * Random scenario ordering with `--order random`
15
25
  * Embed plain text ([712](https://github.com/cucumber/cucumber/pull/712) @bingwei
26
+ * Support the cucumber-reporting tools also when using Scenario Outlines ([700](https://github.com/cucumber/cucumber/pull/700) @brasmusson
16
27
 
17
28
  ### Features removed
18
29
 
@@ -20,6 +31,7 @@ all.
20
31
  * Remove support for i18n nested step methods (Matt Wynne)
21
32
  * Remove experimental .js and .py support (Matt Wynne)
22
33
  * Remove Ruby 1.8.7 support, including RCov
34
+ * Spork support
23
35
 
24
36
  ### API Changes
25
37
 
data/cucumber.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.platform = Gem::Platform::RUBY
15
15
  s.required_ruby_version = ">= 1.9.3"
16
16
 
17
- s.add_dependency 'cucumber-core', '~> 1.0.0.beta.1'
17
+ s.add_dependency 'cucumber-core', '~> 1.0.0.beta.2'
18
18
  s.add_dependency 'builder', '>= 2.1.2'
19
19
  s.add_dependency 'diff-lcs', '>= 1.1.3'
20
20
  s.add_dependency 'gherkin', '~> 2.12'
@@ -27,6 +27,11 @@ Feature: JSON output formatter
27
27
  Given /^I print from step definition/ do
28
28
  puts "from step definition"
29
29
  end
30
+
31
+ Given /^I embed data directly/ do
32
+ data = "YWJj"
33
+ embed data, "mime-type;base64"
34
+ end
30
35
  """
31
36
  And a file named "features/embed.feature" with:
32
37
  """
@@ -70,6 +75,14 @@ Feature: JSON output formatter
70
75
  And I print from step definition
71
76
 
72
77
  """
78
+ And a file named "features/embed_data_directly.feature" with:
79
+ """
80
+ Feature: An embed data directly feature
81
+
82
+ Scenario:
83
+ Given I embed data directly
84
+
85
+ """
73
86
 
74
87
  # Need to investigate why this won't pass in-process. error_message doesn't get det?
75
88
  @spawn
@@ -586,3 +599,51 @@ Feature: JSON output formatter
586
599
  ]
587
600
 
588
601
  """
602
+
603
+ @spawn
604
+ Scenario: embedding data directly
605
+ When I run `cucumber -b --format json features/embed_data_directly.feature`
606
+ Then it should pass with JSON:
607
+ """
608
+ [
609
+ {
610
+ "uri": "features/embed_data_directly.feature",
611
+ "id": "an-embed-data-directly-feature",
612
+ "keyword": "Feature",
613
+ "name": "An embed data directly feature",
614
+ "line": 1,
615
+ "description": "",
616
+ "elements": [
617
+ {
618
+ "id": "an-embed-data-directly-feature;",
619
+ "keyword": "Scenario",
620
+ "name": "",
621
+ "line": 3,
622
+ "description": "",
623
+ "type": "scenario",
624
+ "steps": [
625
+ {
626
+ "keyword": "Given ",
627
+ "name": "I embed data directly",
628
+ "line": 4,
629
+ "embeddings": [
630
+ {
631
+ "mime_type": "mime-type",
632
+ "data": "YWJj"
633
+ }
634
+ ],
635
+ "match": {
636
+ "location": "features/step_definitions/json_steps.rb:10"
637
+ },
638
+ "result": {
639
+ "status": "passed",
640
+ "duration": 1
641
+ }
642
+ }
643
+ ]
644
+ }
645
+ ]
646
+ }
647
+ ]
648
+
649
+ """
@@ -2,56 +2,56 @@ Feature: Cucumber command line
2
2
  In order to find out what step definitions need to be implemented
3
3
  Developers should always see what step definition is missing
4
4
 
5
- @wip-new-core
6
5
  @spawn
7
6
  Scenario: Get info at arbitrary levels of nesting
8
7
  Given a file named "features/call_undefined_step_from_step_def.feature" with:
9
8
  """
10
9
  Feature: Calling undefined step
11
10
 
11
+ Scenario: Call from feature
12
+ Given this directly called step does not exist
13
+
12
14
  Scenario: Call directly
13
- Given a step definition that calls an undefined step
15
+ Given a step that calls an undefined step
14
16
 
15
17
  Scenario: Call via another
16
- Given call step "a step definition that calls an undefined step"
18
+ Given a step that calls a step that calls an undefined step
17
19
  """
18
20
  And a file named "features/step_definitions/steps.rb" with:
19
21
  """
20
- Given /^a step definition that calls an undefined step$/ do
22
+ Given /^a step that calls an undefined step$/ do
21
23
  step 'this does not exist'
22
24
  end
23
25
 
24
- Given /^call step "(.*)"$/ do |step_name| x=1
25
- step step_name
26
+ Given /^a step that calls a step that calls an undefined step$/ do
27
+ step 'a step that calls an undefined step'
26
28
  end
27
29
  """
28
- When I run `cucumber features/call_undefined_step_from_step_def.feature`
29
- Then it should pass with:
30
+ When I run `cucumber --strict -q features/call_undefined_step_from_step_def.feature`
31
+ Then it should fail with exactly:
30
32
  """
31
33
  Feature: Calling undefined step
32
34
 
33
- Scenario: Call directly # features/call_undefined_step_from_step_def.feature:3
34
- Given a step definition that calls an undefined step # features/step_definitions/steps.rb:1
35
- Undefined step: "this does not exist" (Cucumber::Undefined)
36
- ./features/step_definitions/steps.rb:2:in `/^a step definition that calls an undefined step$/'
37
- features/call_undefined_step_from_step_def.feature:4:in `Given a step definition that calls an undefined step'
35
+ Scenario: Call from feature
36
+ Given this directly called step does not exist
37
+ Undefined step: "this directly called step does not exist" (Cucumber::Undefined)
38
+ features/call_undefined_step_from_step_def.feature:4:in `Given this directly called step does not exist'
38
39
 
39
- Scenario: Call via another # features/call_undefined_step_from_step_def.feature:6
40
- Given call step "a step definition that calls an undefined step" # features/step_definitions/steps.rb:5
40
+ Scenario: Call directly
41
+ Given a step that calls an undefined step
41
42
  Undefined step: "this does not exist" (Cucumber::Undefined)
42
- ./features/step_definitions/steps.rb:2:in `/^a step definition that calls an undefined step$/'
43
- ./features/step_definitions/steps.rb:6:in `/^call step "(.*)"$/'
44
- features/call_undefined_step_from_step_def.feature:7:in `Given call step "a step definition that calls an undefined step"'
43
+ ./features/step_definitions/steps.rb:2:in `/^a step that calls an undefined step$/'
44
+ features/call_undefined_step_from_step_def.feature:7:in `Given a step that calls an undefined step'
45
45
 
46
- 2 scenarios (2 undefined)
47
- 2 steps (2 undefined)
48
- """
49
- And the output should contain:
50
- """
51
- You can implement step definitions for undefined steps with these snippets:
46
+ Scenario: Call via another
47
+ Given a step that calls a step that calls an undefined step
48
+ Undefined step: "this does not exist" (Cucumber::Undefined)
49
+ ./features/step_definitions/steps.rb:2:in `/^a step that calls an undefined step$/'
50
+ ./features/step_definitions/steps.rb:6:in `/^a step that calls a step that calls an undefined step$/'
51
+ features/call_undefined_step_from_step_def.feature:10:in `Given a step that calls a step that calls an undefined step'
52
52
 
53
- Given(/^this does not exist$/) do
54
- pending # Write code here that turns the phrase above into concrete actions
55
- end
53
+ 3 scenarios (3 undefined)
54
+ 3 steps (3 undefined)
55
+ 0m0.012s
56
56
 
57
57
  """
@@ -0,0 +1,13 @@
1
+ require 'cucumber/multiline_argument'
2
+
3
+ module Cucumber
4
+ module Ast
5
+ def self.const_missing(const_name)
6
+ if const_name == :Table
7
+ warn "`Cucumber::Ast::Table` has been deprecated. Use `Cucumber::MultilineArgument::DataTable` instead."
8
+ return Cucumber::MultilineArgument::DataTable
9
+ end
10
+ raise "`Cucumber::Ast` no longer exists. These classes have moved into the `Cucumber::Core::Ast` namespace, but may not have the same API."
11
+ end
12
+ end
13
+ end
@@ -2,20 +2,21 @@ require 'cucumber/core/test/result'
2
2
 
3
3
  module Cucumber
4
4
  # Raised when there is no matching StepDefinition for a step.
5
- class Undefined < StandardError
6
- attr_reader :step_name
5
+ class Undefined < Core::Test::Result::Undefined
6
+ def self.from(result, step_name)
7
+ if result.is_a?(self)
8
+ return result.with_message(with_prefix(result.message))
9
+ end
7
10
 
8
- def initialize(step_name)
9
- super %{Undefined step: "#{step_name}"}
10
- @step_name = step_name
11
+ begin
12
+ raise self.new(with_prefix(step_name))
13
+ rescue => exception
14
+ return exception
15
+ end
11
16
  end
12
17
 
13
- def nested!
14
- @nested = true
15
- end
16
-
17
- def nested?
18
- @nested
18
+ def self.with_prefix(step_name)
19
+ %(Undefined step: "#{step_name}")
19
20
  end
20
21
  end
21
22
 
@@ -146,15 +146,20 @@ module Cucumber
146
146
  end
147
147
 
148
148
  def embed(file, mime_type, label)
149
- # Only embed if file exists
150
- if File.exists?(file)
149
+ if File.file?(file)
151
150
  data = File.open(file, 'rb') { |f| f.read }
152
-
153
- if defined?(JRUBY_VERSION)
154
- data = data.to_java_bytes
151
+ else
152
+ if mime_type =~ /;base64$/
153
+ mime_type = mime_type[0..-8]
154
+ data = Base64.decode64(file)
155
+ else
156
+ data = file
155
157
  end
156
- @gf.embedding(mime_type, data)
157
158
  end
159
+ if defined?(JRUBY_VERSION)
160
+ data = data.to_java_bytes
161
+ end
162
+ @gf.embedding(mime_type, data)
158
163
  end
159
164
 
160
165
  def puts(message)
@@ -37,6 +37,10 @@ module Cucumber
37
37
  def embed(src, mime_type, label)
38
38
  case(mime_type)
39
39
  when /^image\/(png|gif|jpg|jpeg)/
40
+ unless File.file?(src) or src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
41
+ type = mime_type =~ /;base[0-9]+$/ ? mime_type : mime_type + ";base64"
42
+ src = "data:" + type + "," + src
43
+ end
40
44
  embed_image(src, label)
41
45
  when /^text\/plain/
42
46
  embed_text(src, label)
@@ -25,6 +25,10 @@ module Cucumber
25
25
  def before_feature(feature_element)
26
26
  @lines = []
27
27
  @file = feature_element.file
28
+ # See https://github.com/cucumber/cucumber/issues/629
29
+ if @file.include?(' ')
30
+ warn("Filenames with spaces like '#{@file}' cause unexpected behaviour from the rerun formatter.")
31
+ end
28
32
  end
29
33
 
30
34
  def after_feature(*)
@@ -84,7 +84,7 @@ module Cucumber
84
84
  end
85
85
  end
86
86
 
87
- # adapts our test_case to look like the Cucumber Runtime's Scenario
87
+ # adapts our test_case to look like the Cucumber Runtime's old Scenario
88
88
  class TestCase
89
89
  def initialize(test_case, feature)
90
90
  @test_case = test_case
@@ -95,6 +95,10 @@ module Cucumber
95
95
  hook.tag_expressions.all? { |expression| @test_case.match_tags?(expression) }
96
96
  end
97
97
 
98
+ def failed?
99
+ warn("Calling failed? on a scenario is not currently supported in Cucumber 2.0. Please see https://github.com/cucumber/cucumber/issues/726")
100
+ end
101
+
98
102
  def language
99
103
  @test_case.language
100
104
  end
@@ -108,10 +112,14 @@ module Cucumber
108
112
  end
109
113
 
110
114
  def source_tags
111
- warn('deprecated: call #tags instead')
115
+ #warn('deprecated: call #tags instead')
112
116
  tags
113
117
  end
114
118
 
119
+ def source_tag_names
120
+ tags.map &:name
121
+ end
122
+
115
123
  def tags
116
124
  @test_case.tags
117
125
  end
@@ -113,3 +113,4 @@ module Cucumber
113
113
  end
114
114
  end
115
115
 
116
+ require 'cucumber/ast'
@@ -4,7 +4,7 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.0.0.beta.1'
7
+ VERSION = '2.0.0.beta.2'
8
8
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
9
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
10
  JRUBY = defined?(JRUBY_VERSION)
@@ -39,7 +39,7 @@ module Cucumber
39
39
  # TODO: this argument parsing should move up out of core
40
40
  location = Core::Ast::Location.new(*caller[0].split(':')[0..1])
41
41
  core_multiline_arg = Core::Ast::MultilineArgument.from(raw_multiline_arg, location)
42
- @__cucumber_runtime.invoke(name, MultilineArgument.from(core_multiline_arg))
42
+ @__cucumber_runtime.invoke(name, MultilineArgument.from(core_multiline_arg), location)
43
43
  end
44
44
 
45
45
  # Run a snippet of Gherkin
@@ -34,21 +34,12 @@ module Cucumber
34
34
  extend Forwardable
35
35
 
36
36
  def_delegators :formatter,
37
- :embed,
38
37
  :ask
39
- :puts
40
38
 
41
- def before_test_case(test_case)
42
- printer.before_test_case(test_case)
43
- end
44
-
45
- def before_test_step(test_step)
46
- printer.before_test_step(test_step)
47
- end
48
-
49
- def after_test_step(test_step, result)
50
- printer.after_test_step(test_step, result)
51
- end
39
+ def_delegators :printer,
40
+ :before_test_case,
41
+ :before_test_step,
42
+ :after_test_step
52
43
 
53
44
  def after_test_case(test_case, result)
54
45
  record_test_case_result(test_case, result)
@@ -168,9 +159,16 @@ module Cucumber
168
159
 
169
160
  require 'ostruct'
170
161
  class StepSource < OpenStruct
171
-
172
162
  def build_step_invocation(indent, runtime, messages, embeddings)
173
- step_result.step_invocation(step_match(runtime), step, indent, background, runtime.configuration, messages, embeddings)
163
+ step_result.step_invocation(
164
+ step_match(runtime),
165
+ step,
166
+ indent,
167
+ background,
168
+ runtime.configuration,
169
+ messages,
170
+ embeddings
171
+ )
174
172
  end
175
173
 
176
174
  private
@@ -180,7 +178,6 @@ module Cucumber
180
178
  rescue Cucumber::Undefined
181
179
  NoStepMatch.new(step, step.name)
182
180
  end
183
-
184
181
  end
185
182
 
186
183
  end
@@ -207,7 +204,7 @@ module Cucumber
207
204
  attr_reader :current_test_step_source
208
205
 
209
206
  def before_test_case(test_case)
210
- @before_hook_result = Legacy::Ast::Node.new
207
+ @before_hook_results = Legacy::Ast::NodeCollection.new
211
208
  end
212
209
 
213
210
  def before_test_step(test_step)
@@ -232,11 +229,14 @@ module Cucumber
232
229
  end
233
230
 
234
231
  def before_hook(location, result)
235
- @before_hook_result = Legacy::Ast::BeforeHookResult.new(LegacyResultBuilder.new(result))
232
+ @before_hook_results << Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result))
236
233
  end
237
234
 
238
235
  def after_hook(location, result)
239
- @child.after_hook LegacyResultBuilder.new(result)
236
+ # if the scenario has no steps, we can hit this before we've created the scenario printer
237
+ # ideally we should call switch_step_container in before_step_step
238
+ switch_step_container if !@child
239
+ @child.after_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result))
240
240
  end
241
241
 
242
242
  def after_step_hook(hook, result)
@@ -272,8 +272,8 @@ module Cucumber
272
272
 
273
273
  private
274
274
 
275
- attr_reader :before_hook_result
276
- private :before_hook_result
275
+ attr_reader :before_hook_results
276
+ private :before_hook_results
277
277
 
278
278
  def switch_step_container
279
279
  switch_to_child select_step_container(current_test_step_source), current_test_step_source
@@ -282,17 +282,17 @@ module Cucumber
282
282
  def select_step_container(source)
283
283
  if source.background
284
284
  if same_background_as_previous_test_case?(source)
285
- HiddenBackgroundPrinter.new(formatter, runtime, source.background)
285
+ HiddenBackgroundPrinter.new(formatter, source.background)
286
286
  else
287
- BackgroundPrinter.new(formatter, runtime, source.background, before_hook_result)
287
+ BackgroundPrinter.new(formatter, source.background, before_hook_results)
288
288
  end
289
289
  elsif source.scenario
290
- ScenarioPrinter.new(formatter, runtime, source.scenario, before_hook_result)
290
+ ScenarioPrinter.new(formatter, source.scenario, before_hook_results)
291
291
  elsif source.scenario_outline
292
292
  if same_scenario_outline_as_previous_test_case?(source) and @previous_outline_child
293
293
  @previous_outline_child
294
294
  else
295
- ScenarioOutlinePrinter.new(formatter, runtime, source.scenario_outline)
295
+ ScenarioOutlinePrinter.new(formatter, runtime.configuration, source.scenario_outline)
296
296
  end
297
297
  else
298
298
  raise 'unknown step container'
@@ -313,7 +313,7 @@ module Cucumber
313
313
 
314
314
  if current_test_step_source.scenario_outline
315
315
  @child.examples_table(current_test_step_source.examples_table)
316
- @child.examples_table_row(current_test_step_source.examples_table_row, before_hook_result)
316
+ @child.examples_table_row(current_test_step_source.examples_table_row, before_hook_results)
317
317
  end
318
318
 
319
319
  if @failed_hidden_background_step
@@ -386,12 +386,33 @@ module Cucumber
386
386
 
387
387
  end
388
388
 
389
- BackgroundPrinter = Struct.new(:formatter, :runtime, :node, :before_hook_result) do
389
+ module PrintsAfterHooks
390
+ def after_hook_results
391
+ @after_hook_results ||= Legacy::Ast::NodeCollection.new
392
+ end
393
+
394
+ def after_hook(result)
395
+ after_hook_results << result
396
+ end
397
+ end
398
+
399
+ # Basic printer used by default
400
+ class AfterHookPrinter
401
+ include Cucumber.initializer(:formatter)
402
+
403
+ include PrintsAfterHooks
404
+
405
+ def after
406
+ after_hook_results.accept(formatter)
407
+ end
408
+ end
409
+
410
+ BackgroundPrinter = Struct.new(:formatter, :node, :before_hook_results) do
390
411
 
391
412
  def before
392
413
  formatter.before_background node
393
414
  formatter.background_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
394
- before_hook_result.accept(formatter)
415
+ before_hook_results.accept(formatter)
395
416
  self
396
417
  end
397
418
 
@@ -400,7 +421,7 @@ module Cucumber
400
421
  end
401
422
 
402
423
  def step_invocation(step_invocation, source)
403
- @child ||= StepsPrinter.new(formatter, runtime).before
424
+ @child ||= StepsPrinter.new(formatter).before
404
425
  @child.step_invocation step_invocation
405
426
  if source.step_result.status == :failed
406
427
  @failed = true
@@ -426,7 +447,7 @@ module Cucumber
426
447
 
427
448
  # Printer to handle background steps for anything but the first scenario in a
428
449
  # feature. These steps should not be printed.
429
- class HiddenBackgroundPrinter < Struct.new(:formatter, :runtime, :node)
450
+ class HiddenBackgroundPrinter < Struct.new(:formatter, :node)
430
451
  def get_failed_step_source
431
452
  return @source_of_failed_step
432
453
  end
@@ -446,26 +467,23 @@ module Cucumber
446
467
  def after_test_case(*);end
447
468
  end
448
469
 
449
- ScenarioPrinter = Struct.new(:formatter, :runtime, :node, :before_hook_result) do
470
+ ScenarioPrinter = Struct.new(:formatter, :node, :before_hook_results) do
471
+ include PrintsAfterHooks
450
472
 
451
473
  def before
452
474
  formatter.before_feature_element(node)
453
475
  Legacy::Ast::Tags.new(node.tags).accept(formatter)
454
476
  formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
455
- before_hook_result.accept(formatter)
477
+ before_hook_results.accept(formatter)
456
478
  self
457
479
  end
458
480
 
459
481
  def step_invocation(step_invocation, source)
460
- @child ||= StepsPrinter.new(formatter, runtime).before
482
+ @child ||= StepsPrinter.new(formatter).before
461
483
  @child.step_invocation step_invocation
462
484
  @last_step_result = source.step_result
463
485
  end
464
486
 
465
- def after_hook(result)
466
- @after_hook_result = result
467
- end
468
-
469
487
  def after_step_hook(result)
470
488
  result.describe_exception_to formatter
471
489
  end
@@ -480,7 +498,7 @@ module Cucumber
480
498
  # TODO - the last step result might not accurately reflect the
481
499
  # overall scenario result.
482
500
  scenario = last_step_result.scenario(node.name, node.location)
483
- @after_hook_result.describe_exception_to(formatter) if @after_hook_result
501
+ after_hook_results.accept(formatter)
484
502
  formatter.after_feature_element(scenario)
485
503
  @done = true
486
504
  self
@@ -497,7 +515,7 @@ module Cucumber
497
515
  end
498
516
  end
499
517
 
500
- StepsPrinter = Struct.new(:formatter, :runtime) do
518
+ StepsPrinter = Struct.new(:formatter) do
501
519
  def before
502
520
  formatter.before_steps(nil)
503
521
  self
@@ -522,7 +540,7 @@ module Cucumber
522
540
 
523
541
  end
524
542
 
525
- ScenarioOutlinePrinter = Struct.new(:formatter, :runtime, :node) do
543
+ ScenarioOutlinePrinter = Struct.new(:formatter, :configuration, :node) do
526
544
  extend Forwardable
527
545
  def_delegators :@child, :after_hook, :after_step_hook
528
546
 
@@ -530,14 +548,10 @@ module Cucumber
530
548
  formatter.before_feature_element(node)
531
549
  Legacy::Ast::Tags.new(node.tags).accept(formatter)
532
550
  formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
533
- OutlineStepsPrinter.new(formatter, runtime, indent).print(node)
551
+ OutlineStepsPrinter.new(formatter, configuration, indent).print(node)
534
552
  self
535
553
  end
536
554
 
537
- def after_hook(result)
538
- @child.after_hook(result)
539
- end
540
-
541
555
  def step_invocation(step_invocation, source)
542
556
  node, result = source.step, source.step_result
543
557
  @last_step_result = result
@@ -545,12 +559,12 @@ module Cucumber
545
559
  end
546
560
 
547
561
  def examples_table(examples_table)
548
- @child ||= ExamplesArrayPrinter.new(formatter, runtime).before
562
+ @child ||= ExamplesArrayPrinter.new(formatter, configuration).before
549
563
  @child.examples_table(examples_table)
550
564
  end
551
565
 
552
- def examples_table_row(node, before_hook_result)
553
- @child.examples_table_row(node, before_hook_result)
566
+ def examples_table_row(node, before_hook_results)
567
+ @child.examples_table_row(node, before_hook_results)
554
568
  end
555
569
 
556
570
  def after_test_case
@@ -577,7 +591,7 @@ module Cucumber
577
591
  end
578
592
  end
579
593
 
580
- OutlineStepsPrinter = Struct.new(:formatter, :runtime, :indent, :outline) do
594
+ OutlineStepsPrinter = Struct.new(:formatter, :configuration, :indent, :outline) do
581
595
  def print(node)
582
596
  node.describe_to self
583
597
  steps_printer.after
@@ -590,7 +604,7 @@ module Cucumber
590
604
  def outline_step(step)
591
605
  step_match = NoStepMatch.new(step, step.name)
592
606
  step_invocation = LegacyResultBuilder.new(Core::Test::Result::Skipped.new).
593
- step_invocation(step_match, step, indent, background = nil, runtime.configuration, messages = [], embeddings = [])
607
+ step_invocation(step_match, step, indent, background = nil, configuration, messages = [], embeddings = [])
594
608
  steps_printer.step_invocation step_invocation
595
609
  end
596
610
 
@@ -603,7 +617,7 @@ module Cucumber
603
617
  end
604
618
  end
605
619
 
606
- ExamplesArrayPrinter = Struct.new(:formatter, :runtime) do
620
+ ExamplesArrayPrinter = Struct.new(:formatter, :configuration) do
607
621
  extend Forwardable
608
622
  def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case, :examples_table_row
609
623
 
@@ -615,7 +629,7 @@ module Cucumber
615
629
  def examples_table(examples_table)
616
630
  return if examples_table == @current
617
631
  @child.after if @child
618
- @child = ExamplesTablePrinter.new(formatter, runtime, examples_table).before
632
+ @child = ExamplesTablePrinter.new(formatter, configuration, examples_table).before
619
633
  @current = examples_table
620
634
  end
621
635
 
@@ -626,7 +640,7 @@ module Cucumber
626
640
  end
627
641
  end
628
642
 
629
- ExamplesTablePrinter = Struct.new(:formatter, :runtime, :node) do
643
+ ExamplesTablePrinter = Struct.new(:formatter, :configuration, :node) do
630
644
  extend Forwardable
631
645
  def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case
632
646
 
@@ -634,20 +648,20 @@ module Cucumber
634
648
  formatter.before_examples(node)
635
649
  formatter.examples_name(node.keyword, node.legacy_conflated_name_and_description)
636
650
  formatter.before_outline_table(legacy_table)
637
- if !runtime.configuration.expand?
638
- HeaderTableRowPrinter.new(formatter, runtime, ExampleTableRow.new(node.header), Legacy::Ast::Node.new).before.after
651
+ if !configuration.expand?
652
+ HeaderTableRowPrinter.new(formatter, ExampleTableRow.new(node.header), Legacy::Ast::Node.new).before.after
639
653
  end
640
654
  self
641
655
  end
642
656
 
643
- def examples_table_row(examples_table_row, before_hook_result)
657
+ def examples_table_row(examples_table_row, before_hook_results)
644
658
  return if examples_table_row == @current
645
659
  @child.after if @child
646
660
  row = ExampleTableRow.new(examples_table_row)
647
- if !runtime.configuration.expand?
648
- @child = TableRowPrinter.new(formatter, runtime, row, before_hook_result).before
661
+ if !configuration.expand?
662
+ @child = TableRowPrinter.new(formatter, row, before_hook_results).before
649
663
  else
650
- @child = ExpandTableRowPrinter.new(formatter, runtime, row, before_hook_result).before
664
+ @child = ExpandTableRowPrinter.new(formatter, row, before_hook_results).before
651
665
  end
652
666
  @current = examples_table_row
653
667
  end
@@ -708,10 +722,8 @@ module Cucumber
708
722
  end
709
723
  end
710
724
 
711
- class TableRowPrinterBase < Struct.new(:formatter, :runtime, :node, :before_hook_result)
712
- def after_hook(result)
713
- @after_hook_result = result
714
- end
725
+ class TableRowPrinterBase < Struct.new(:formatter, :node, :before_hook_results)
726
+ include PrintsAfterHooks
715
727
 
716
728
  def after_step_hook(result)
717
729
  @after_step_hook_result = result
@@ -757,7 +769,7 @@ module Cucumber
757
769
 
758
770
  class TableRowPrinter < TableRowPrinterBase
759
771
  def before
760
- before_hook_result.accept(formatter)
772
+ before_hook_results.accept(formatter)
761
773
  formatter.before_table_row(node)
762
774
  self
763
775
  end
@@ -780,7 +792,7 @@ module Cucumber
780
792
  end
781
793
  formatter.after_table_row(legacy_table_row)
782
794
  @after_step_hook_result.describe_exception_to formatter if @after_step_hook_result
783
- @after_hook_result.describe_exception_to(formatter) if @after_hook_result
795
+ after_hook_results.accept(formatter)
784
796
  @done = true
785
797
  self
786
798
  end
@@ -794,7 +806,7 @@ module Cucumber
794
806
 
795
807
  class ExpandTableRowPrinter < TableRowPrinterBase
796
808
  def before
797
- before_hook_result.accept(formatter)
809
+ before_hook_results.accept(formatter)
798
810
  self
799
811
  end
800
812
 
@@ -815,7 +827,7 @@ module Cucumber
815
827
  return if @done
816
828
  @child.after if @child
817
829
  @after_step_hook_result.describe_exception_to formatter if @after_step_hook_result
818
- @after_hook_result.describe_exception_to(formatter) if @after_hook_result
830
+ after_hook_results.accept(formatter)
819
831
  @done = true
820
832
  self
821
833
  end
@@ -889,7 +901,8 @@ module Cucumber
889
901
  class LegacyResultBuilder
890
902
  attr_reader :status
891
903
  def initialize(result)
892
- result.describe_to(self)
904
+ @result = result
905
+ @result.describe_to(self)
893
906
  end
894
907
 
895
908
  def passed
@@ -945,12 +958,8 @@ module Cucumber
945
958
  def step_exception(step, configuration)
946
959
  return filtered_step_exception(step) if @exception
947
960
  return nil unless @status == :undefined && configuration.strict?
948
- begin
949
- raise Cucumber::Undefined.new(step.name)
950
- rescue => exception
951
- @exception = exception
952
- filtered_step_exception(step)
953
- end
961
+ @exception = Cucumber::Undefined.from(@result, step.name)
962
+ filtered_step_exception(step)
954
963
  end
955
964
 
956
965
  def filtered_exception
@@ -1023,6 +1032,20 @@ module Cucumber
1023
1032
  private :node
1024
1033
  end
1025
1034
 
1035
+ class NodeCollection
1036
+ def initialize
1037
+ @children = []
1038
+ end
1039
+
1040
+ def accept(formatter)
1041
+ @children.each { |child| child.accept(formatter) }
1042
+ end
1043
+
1044
+ def <<(child)
1045
+ @children << child
1046
+ end
1047
+ end
1048
+
1026
1049
  Comments = Struct.new(:comments) do
1027
1050
  def accept(formatter)
1028
1051
  return if comments.empty?
@@ -1033,7 +1056,7 @@ module Cucumber
1033
1056
  end
1034
1057
  end
1035
1058
 
1036
- class BeforeHookResult
1059
+ class HookResult
1037
1060
  def initialize(result)
1038
1061
  @result = result
1039
1062
  @already_accepted = false
@@ -22,7 +22,7 @@ module Cucumber
22
22
  def step(step)
23
23
  location = Cucumber::Core::Ast::Location.new(*caller[0].split(':')[0..1])
24
24
  core_multiline_arg = Core::Ast::MultilineArgument.from(step.doc_string || step.rows, location)
25
- @support_code.invoke(step.name, MultilineArgument.from(core_multiline_arg))
25
+ @support_code.invoke(step.name, MultilineArgument.from(core_multiline_arg), location)
26
26
  end
27
27
 
28
28
  def eof
@@ -55,15 +55,8 @@ module Cucumber
55
55
  parser.parse(steps_text, file, line.to_i)
56
56
  end
57
57
 
58
- def invoke(step_name, multiline_argument)
59
- file, line = *caller[2].split(':')[0..1]
60
- location = Core::Ast::Location.new(file, line)
61
- begin
62
- step_match(step_name).invoke(multiline_argument)
63
- rescue Exception => e
64
- e.nested! if Undefined === e
65
- raise e
66
- end
58
+ def invoke(step_name, multiline_argument, location=nil)
59
+ step_match(step_name).invoke(multiline_argument)
67
60
  end
68
61
 
69
62
  # Loads and registers programming language implementation.
@@ -115,16 +108,6 @@ module Cucumber
115
108
  end
116
109
  end
117
110
 
118
- def around(scenario, block)
119
- @programming_languages.reverse.inject(block) do |blk, programming_language|
120
- proc do
121
- programming_language.around(scenario) do
122
- blk.call(scenario)
123
- end
124
- end
125
- end.call
126
- end
127
-
128
111
  def step_definitions
129
112
  @programming_languages.map do |programming_language|
130
113
  programming_language.step_definitions
@@ -3,6 +3,7 @@ require 'cucumber/formatter/spec_helper'
3
3
  require 'cucumber/formatter/html'
4
4
  require 'nokogiri'
5
5
  require 'cucumber/rb_support/rb_language'
6
+ require 'rspec/mocks'
6
7
 
7
8
  module Cucumber
8
9
  module Formatter
@@ -245,7 +246,10 @@ module Cucumber
245
246
 
246
247
  describe "with a step that embeds a snapshot" do
247
248
  define_steps do
248
- Given(/snap/) { embed('snapshot.jpeg', 'image/jpeg') }
249
+ Given(/snap/) {
250
+ RSpec::Mocks.allow_message(File, :file?) { true }
251
+ embed('snapshot.jpeg', 'image/jpeg')
252
+ }
249
253
  end
250
254
 
251
255
  define_feature(<<-FEATURE)
@@ -271,6 +275,34 @@ module Cucumber
271
275
  it { expect(@doc.at('a#text_0')['href'].to_s).to eq "log.txt" }
272
276
  end
273
277
 
278
+ describe "with a step that embeds a snapshot content manually" do
279
+ define_steps do
280
+ Given(/snap/) { embed('data:image/png;base64,YWJj', 'image/png') }
281
+ end
282
+
283
+ define_feature(<<-FEATURE)
284
+ Feature:
285
+ Scenario:
286
+ Given snap
287
+ FEATURE
288
+
289
+ it { expect(@doc.css('.embed img').first.attributes['src'].to_s).to eq "data:image/png;base64,YWJj" }
290
+ end
291
+
292
+ describe "with a step that embeds a snapshot content" do
293
+ define_steps do
294
+ Given(/snap/) { embed('YWJj', 'image/png;base64') }
295
+ end
296
+
297
+ define_feature(<<-FEATURE)
298
+ Feature:
299
+ Scenario:
300
+ Given snap
301
+ FEATURE
302
+
303
+ it { expect(@doc.css('.embed img').first.attributes['src'].to_s).to eq "data:image/png;base64,YWJj" }
304
+ end
305
+
274
306
  describe "with an undefined Given step then an undefined And step" do
275
307
  define_feature(<<-FEATURE)
276
308
  Feature:
@@ -0,0 +1,51 @@
1
+ require_relative "../../lib/cucumber/mappings"
2
+ require 'cucumber/core'
3
+ require 'cucumber/core/gherkin/writer'
4
+
5
+ module Cucumber
6
+ class Mappings
7
+ describe Scenario do
8
+ include Core::Gherkin::Writer
9
+ include Core
10
+ let(:ruby) { double.as_null_object }
11
+ let(:runtime) do
12
+ double(
13
+ load_programming_language: ruby,
14
+ step_match: double
15
+ )
16
+ end
17
+ let(:mappings) { Mappings.new(runtime) }
18
+ let(:report) { double.as_null_object }
19
+
20
+ it "responds to #source_tag_names" do
21
+ gherkin_docs = [
22
+ gherkin do
23
+ feature 'test', tags: '@foo @bar' do
24
+ scenario 'test', tags: '@baz' do
25
+ step 'passing'
26
+ end
27
+ end
28
+ end
29
+ ]
30
+
31
+ # TODO: the complexity of this stubbing shows we need to clean up the interface
32
+ scenario_spy = nil
33
+ allow(ruby).to receive(:hooks_for) do |phase, scenario|
34
+ if phase == :before
35
+ hook = double
36
+ expect(hook).to receive(:invoke) do |phase, scenario|
37
+ scenario_spy = scenario
38
+ end
39
+ [hook]
40
+ else
41
+ []
42
+ end
43
+ end
44
+
45
+ execute gherkin_docs, mappings, report
46
+
47
+ expect(scenario_spy.source_tag_names).to eq %w(@foo @bar @baz)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -89,7 +89,7 @@ module Cucumber
89
89
 
90
90
  expect(-> {
91
91
  run_step "Outside"
92
- }).to raise_error(Cucumber::Undefined, 'Undefined step: "Inside"')
92
+ }).to raise_error(Cucumber::Undefined)
93
93
  end
94
94
 
95
95
  it "allows forced pending" do
@@ -1439,7 +1439,7 @@ module Cucumber
1439
1439
  end
1440
1440
  end
1441
1441
 
1442
- context 'with exception in before hooks' do
1442
+ context 'with exception in a single before hook' do
1443
1443
  it 'prints the exception after the scenario name' do
1444
1444
  define_steps do
1445
1445
  Before do
@@ -1590,6 +1590,47 @@ module Cucumber
1590
1590
  end
1591
1591
  end
1592
1592
 
1593
+ context 'with exception in the first of several before hooks' do
1594
+ # This proves that the second before hook's result doesn't overwrite
1595
+ # the result of the first one.
1596
+ it 'prints the exception after the scenario name' do
1597
+ define_steps do
1598
+ Before { raise 'an exception' }
1599
+ Before { }
1600
+ end
1601
+ execute_gherkin do
1602
+ feature do
1603
+ scenario do
1604
+ step 'passing'
1605
+ end
1606
+ end
1607
+ end
1608
+
1609
+ expect( formatter.messages ).to eq([
1610
+ :before_features,
1611
+ :before_feature,
1612
+ :before_tags,
1613
+ :after_tags,
1614
+ :feature_name,
1615
+ :before_feature_element,
1616
+ :before_tags,
1617
+ :after_tags,
1618
+ :scenario_name,
1619
+ :exception,
1620
+ :before_steps,
1621
+ :before_step,
1622
+ :before_step_result,
1623
+ :step_name,
1624
+ :after_step_result,
1625
+ :after_step,
1626
+ :after_steps,
1627
+ :after_feature_element,
1628
+ :after_feature,
1629
+ :after_features
1630
+ ])
1631
+ end
1632
+ end
1633
+
1593
1634
  context 'with exception in after hooks' do
1594
1635
  it 'prints the exception after the steps' do
1595
1636
  define_steps do
@@ -1688,6 +1729,75 @@ module Cucumber
1688
1729
  ])
1689
1730
  end
1690
1731
  end
1732
+
1733
+ context 'with exception in the first of several after hooks' do
1734
+ it 'prints the exception after the steps' do
1735
+ define_steps do
1736
+ After { raise 'an exception' }
1737
+ After { }
1738
+ end
1739
+ execute_gherkin do
1740
+ feature do
1741
+ scenario do
1742
+ step 'passing'
1743
+ end
1744
+ end
1745
+ end
1746
+
1747
+ expect( formatter.messages ).to eq([
1748
+ :before_features,
1749
+ :before_feature,
1750
+ :before_tags,
1751
+ :after_tags,
1752
+ :feature_name,
1753
+ :before_feature_element,
1754
+ :before_tags,
1755
+ :after_tags,
1756
+ :scenario_name,
1757
+ :before_steps,
1758
+ :before_step,
1759
+ :before_step_result,
1760
+ :step_name,
1761
+ :after_step_result,
1762
+ :after_step,
1763
+ :after_steps,
1764
+ :exception,
1765
+ :after_feature_element,
1766
+ :after_feature,
1767
+ :after_features
1768
+ ])
1769
+ end
1770
+ end
1771
+
1772
+ context 'with an exception in an after hook but no steps' do
1773
+ it 'prints the exception after the steps' do
1774
+ define_steps do
1775
+ After { fail }
1776
+ end
1777
+ execute_gherkin do
1778
+ feature do
1779
+ scenario do
1780
+ end
1781
+ end
1782
+ end
1783
+
1784
+ expect( formatter.messages ).to eq([
1785
+ :before_features,
1786
+ :before_feature,
1787
+ :before_tags,
1788
+ :after_tags,
1789
+ :feature_name,
1790
+ :before_feature_element,
1791
+ :before_tags,
1792
+ :after_tags,
1793
+ :scenario_name,
1794
+ :exception,
1795
+ :after_feature_element,
1796
+ :after_feature,
1797
+ :after_features
1798
+ ])
1799
+ end
1800
+ end
1691
1801
  end
1692
1802
 
1693
1803
  it 'passes an object responding to failed? with the after_feature_element message' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta.1
4
+ version: 2.0.0.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aslak Hellesøy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-22 00:00:00.000000000 Z
11
+ date: 2014-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.0.beta.1
19
+ version: 1.0.0.beta.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.0.0.beta.1
26
+ version: 1.0.0.beta.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -647,6 +647,7 @@ files:
647
647
  - lib/autotest/cucumber_rspec2.rb
648
648
  - lib/autotest/discover.rb
649
649
  - lib/cucumber.rb
650
+ - lib/cucumber/ast.rb
650
651
  - lib/cucumber/cli/configuration.rb
651
652
  - lib/cucumber/cli/main.rb
652
653
  - lib/cucumber/cli/options.rb
@@ -741,6 +742,7 @@ files:
741
742
  - spec/cucumber/formatter/pretty_spec.rb
742
743
  - spec/cucumber/formatter/progress_spec.rb
743
744
  - spec/cucumber/formatter/spec_helper.rb
745
+ - spec/cucumber/mappings_spec.rb
744
746
  - spec/cucumber/rake/forked_spec.rb
745
747
  - spec/cucumber/rb_support/rb_language_spec.rb
746
748
  - spec/cucumber/rb_support/rb_step_definition_spec.rb
@@ -787,10 +789,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
787
789
  version: 1.3.1
788
790
  requirements: []
789
791
  rubyforge_project:
790
- rubygems_version: 2.2.2
792
+ rubygems_version: 2.0.14
791
793
  signing_key:
792
794
  specification_version: 4
793
- summary: cucumber-2.0.0.beta.1
795
+ summary: cucumber-2.0.0.beta.2
794
796
  test_files:
795
797
  - features/docs/api/list_step_defs_as_json.feature
796
798
  - features/docs/api/run_cli_main_with_existing_runtime.feature
@@ -886,6 +888,7 @@ test_files:
886
888
  - spec/cucumber/formatter/pretty_spec.rb
887
889
  - spec/cucumber/formatter/progress_spec.rb
888
890
  - spec/cucumber/formatter/spec_helper.rb
891
+ - spec/cucumber/mappings_spec.rb
889
892
  - spec/cucumber/rake/forked_spec.rb
890
893
  - spec/cucumber/rb_support/rb_language_spec.rb
891
894
  - spec/cucumber/rb_support/rb_step_definition_spec.rb