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

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