cucumber 1.2.5 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +14 -6
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +1 -0
  5. data/.yardopts +1 -0
  6. data/CONTRIBUTING.md +2 -2
  7. data/History.md +38 -2
  8. data/bin/cucumber +2 -11
  9. data/cucumber.gemspec +3 -3
  10. data/cucumber.yml +5 -1
  11. data/examples/test_unit/Gemfile +4 -0
  12. data/examples/test_unit/features/step_definitions/test_unit_steps.rb +1 -4
  13. data/examples/watir/README.textile +2 -2
  14. data/examples/watir/features/support/env.rb +10 -7
  15. data/features/.cucumber/stepdefs.json +747 -1354
  16. data/features/assertions.feature +6 -2
  17. data/features/background.feature +3 -0
  18. data/features/backtraces.feature +3 -3
  19. data/features/before_hook.feature +43 -0
  20. data/features/bootstrap.feature +14 -2
  21. data/features/custom_formatter.feature +1 -1
  22. data/features/drb_server_integration.feature +3 -3
  23. data/features/formatter_callbacks.feature +2 -2
  24. data/features/formatter_step_file_colon_line.feature +1 -1
  25. data/features/html_formatter.feature +52 -1
  26. data/features/json_formatter.feature +93 -7
  27. data/features/load_path.feature +14 -0
  28. data/features/nested_steps.feature +75 -3
  29. data/features/nested_steps_i18n.feature +36 -0
  30. data/features/pretty_formatter.feature +31 -0
  31. data/features/progress_formatter.feature +31 -0
  32. data/features/raketask.feature +51 -0
  33. data/features/rerun_formatter.feature +1 -1
  34. data/features/stats_formatters.feature +17 -14
  35. data/features/step_definitions/cucumber_steps.rb +6 -4
  36. data/features/support/env.rb +31 -4
  37. data/features/support/feature_factory.rb +17 -0
  38. data/features/tagged_hooks.feature +37 -195
  39. data/features/transforms.feature +15 -15
  40. data/gem_tasks/cucumber.rake +2 -0
  41. data/gem_tasks/yard.rake +10 -6
  42. data/legacy_features/README.md +14 -0
  43. data/legacy_features/language_help.feature +3 -1
  44. data/legacy_features/report_called_undefined_steps.feature +1 -0
  45. data/legacy_features/snippets_when_using_star_keyword.feature +1 -0
  46. data/legacy_features/support/env.rb +4 -0
  47. data/lib/cucumber/ast/background.rb +35 -35
  48. data/lib/cucumber/ast/empty_background.rb +33 -0
  49. data/lib/cucumber/ast/examples.rb +5 -2
  50. data/lib/cucumber/ast/feature.rb +24 -35
  51. data/lib/cucumber/ast/features.rb +4 -1
  52. data/lib/cucumber/ast/has_steps.rb +9 -17
  53. data/lib/cucumber/ast/location.rb +41 -0
  54. data/lib/cucumber/ast/scenario.rb +37 -50
  55. data/lib/cucumber/ast/scenario_outline.rb +62 -49
  56. data/lib/cucumber/ast/step.rb +23 -27
  57. data/lib/cucumber/ast/step_collection.rb +16 -0
  58. data/lib/cucumber/ast/step_invocation.rb +4 -1
  59. data/lib/cucumber/ast/tree_walker.rb +7 -0
  60. data/lib/cucumber/cli/configuration.rb +15 -3
  61. data/lib/cucumber/cli/main.rb +24 -11
  62. data/lib/cucumber/cli/options.rb +24 -16
  63. data/lib/cucumber/configuration.rb +4 -0
  64. data/lib/cucumber/core_ext/disable_mini_and_test_unit_autorun.rb +10 -34
  65. data/lib/cucumber/core_ext/instance_exec.rb +4 -1
  66. data/lib/cucumber/core_ext/proc.rb +2 -0
  67. data/lib/cucumber/feature_file.rb +5 -12
  68. data/lib/cucumber/formatter/console.rb +10 -0
  69. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +4 -4
  70. data/lib/cucumber/formatter/html.rb +7 -42
  71. data/lib/cucumber/formatter/interceptor.rb +4 -0
  72. data/lib/cucumber/formatter/json_pretty.rb +0 -4
  73. data/lib/cucumber/formatter/junit.rb +8 -2
  74. data/lib/cucumber/formatter/pretty.rb +5 -1
  75. data/lib/cucumber/formatter/progress.rb +4 -0
  76. data/lib/cucumber/formatter/unicode.rb +12 -25
  77. data/lib/cucumber/formatter/usage.rb +7 -2
  78. data/lib/cucumber/js_support/js_snippets.rb +1 -1
  79. data/lib/cucumber/load_path.rb +13 -0
  80. data/lib/cucumber/parser/gherkin_builder.rb +237 -81
  81. data/lib/cucumber/platform.rb +1 -1
  82. data/lib/cucumber/py_support/py_language.rb +1 -1
  83. data/lib/cucumber/rake/task.rb +5 -1
  84. data/lib/cucumber/rb_support/rb_language.rb +20 -19
  85. data/lib/cucumber/rb_support/rb_world.rb +63 -21
  86. data/lib/cucumber/rb_support/snippet.rb +108 -0
  87. data/lib/cucumber/runtime.rb +1 -0
  88. data/lib/cucumber/runtime/support_code.rb +2 -2
  89. data/lib/cucumber/unit.rb +11 -0
  90. data/lib/cucumber/wire_support/wire_language.rb +1 -1
  91. data/spec/cucumber/ast/background_spec.rb +13 -6
  92. data/spec/cucumber/ast/feature_factory.rb +20 -10
  93. data/spec/cucumber/ast/features_spec.rb +51 -0
  94. data/spec/cucumber/ast/scenario_outline_spec.rb +13 -7
  95. data/spec/cucumber/ast/step_spec.rb +6 -4
  96. data/spec/cucumber/cli/configuration_spec.rb +34 -1
  97. data/spec/cucumber/cli/main_spec.rb +36 -26
  98. data/spec/cucumber/cli/options_spec.rb +28 -19
  99. data/spec/cucumber/core_ext/proc_spec.rb +13 -1
  100. data/spec/cucumber/formatter/interceptor_spec.rb +8 -0
  101. data/spec/cucumber/formatter/junit_spec.rb +33 -0
  102. data/spec/cucumber/formatter/pretty_spec.rb +391 -0
  103. data/spec/cucumber/rb_support/rb_language_spec.rb +21 -50
  104. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +2 -4
  105. data/spec/cucumber/rb_support/snippet_spec.rb +128 -0
  106. data/spec/cucumber/step_match_spec.rb +2 -6
  107. metadata +62 -113
  108. data/.rvmrc +0 -1
  109. data/features/hooks.feature +0 -59
  110. data/legacy_features/call_steps_from_stepdefs.feature +0 -154
@@ -15,7 +15,9 @@ Feature: Transforms
15
15
  """
16
16
  And a file named "features/support/person.rb" with:
17
17
  """
18
- class Person < Struct.new(:age)
18
+ class Person
19
+ attr_accessor :age
20
+
19
21
  def to_s
20
22
  "I am #{age} years old"
21
23
  end
@@ -29,18 +31,17 @@ Feature: Transforms
29
31
  And a file named "features/step_definitions/steps.rb" with:
30
32
  """
31
33
  Transform(/a Person aged (\d+)/) do |age|
32
- Person.new(age.to_i)
34
+ person = Person.new
35
+ person.age = age.to_i
36
+ person
33
37
  end
34
38
 
35
39
  Given /^(a Person aged \d+) with blonde hair$/ do |person|
36
- puts "#{person} and I have blonde hair"
40
+ person.age.should == 15
37
41
  end
38
42
  """
39
- When I run cucumber "features/foo.feature"
40
- Then it should pass with:
41
- """
42
- I am 15 years old and I have blonde hair
43
- """
43
+ When I run `cucumber features/foo.feature`
44
+ Then it should pass
44
45
 
45
46
  Scenario: Re-use Transform's Regular Expression
46
47
  If you keep a reference to the transform, you can use it in your
@@ -49,15 +50,14 @@ Feature: Transforms
49
50
  And a file named "features/step_definitions/steps.rb" with:
50
51
  """
51
52
  A_PERSON = Transform(/a Person aged (\d+)/) do |age|
52
- Person.new(age.to_i)
53
+ person = Person.new
54
+ person.age = age.to_i
55
+ person
53
56
  end
54
57
 
55
58
  Given /^(#{A_PERSON}) with blonde hair$/ do |person|
56
- puts "#{person} and I have blonde hair"
59
+ person.age.should == 15
57
60
  end
58
61
  """
59
- When I run cucumber "features/foo.feature"
60
- Then it should pass with:
61
- """
62
- I am 15 years old and I have blonde hair
63
- """
62
+ When I run `cucumber features/foo.feature`
63
+ Then it should pass
@@ -7,6 +7,8 @@ class Cucumber::Rake::Task
7
7
  Cucumber::WINDOWS ? 'jruby_win' : 'jruby'
8
8
  elsif Cucumber::WINDOWS_MRI
9
9
  'windows_mri'
10
+ elsif Cucumber::RUBY_1_8_7
11
+ 'ruby_1_8_7'
10
12
  elsif Cucumber::RUBY_1_9
11
13
  'ruby_1_9'
12
14
  elsif Cucumber::RUBY_2_0
@@ -2,26 +2,30 @@ require 'yard'
2
2
  require 'yard/rake/yardoc_task'
3
3
  require File.expand_path(File.dirname(__FILE__) + '/../lib/cucumber/platform')
4
4
 
5
+ DOC_DIR = File.expand_path(File.dirname(__FILE__) + '/../doc')
5
6
  SITE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../cucumber.github.com')
6
7
  API_DIR = File.join(SITE_DIR, 'api', 'cucumber', 'ruby', 'yardoc')
7
8
  TEMPLATE_DIR = File.expand_path(File.join(File.dirname(__FILE__), 'yard'))
8
9
  YARD::Templates::Engine.register_template_path(TEMPLATE_DIR)
9
10
 
10
11
  namespace :api do
11
- task :dir do
12
+ YARD::Rake::YardocTask.new(:yard) do |yard|
13
+ yard.options = ["--out", DOC_DIR]
14
+ end
15
+
16
+ task :sync_with_git do
12
17
  unless File.directory?(SITE_DIR)
13
18
  raise "You need to git clone git@github.com:cucumber/cucumber.github.com.git #{SITE_DIR}"
14
19
  end
15
20
  Dir.chdir(SITE_DIR) do
16
21
  sh 'git pull -u'
17
- mkdir_p API_DIR
18
22
  end
19
23
  end
20
24
 
21
- YARD::Rake::YardocTask.new(:yard) do |yard|
22
- yard.options = ["--out", API_DIR]
25
+ task :copy_to_website do
26
+ rm_rf API_DIR
27
+ cp_r DOC_DIR, API_DIR
23
28
  end
24
- task :yard => :dir
25
29
 
26
30
  task :release do
27
31
  Dir.chdir(SITE_DIR) do
@@ -32,5 +36,5 @@ namespace :api do
32
36
  end
33
37
 
34
38
  desc "Generate YARD docs for Cucumber's API"
35
- task :doc => [:yard, :release]
39
+ task :doc => [:yard, :sync_with_git, :copy_to_website, :release]
36
40
  end
@@ -0,0 +1,14 @@
1
+ # Please do not add to the tests in this folder
2
+
3
+ This folder contains the acceptance tests that were written for Cucumber
4
+ before [Aruba](https://github.com/cucumber/aruba) existed.
5
+
6
+ These tests are currently run as part of the build, but should not be added to.
7
+
8
+ New acceptance tests should be added in the `features` directory. If you find a test
9
+ in here that you want to modify, please do the work to migrate it into the `features`
10
+ directory.
11
+
12
+ There is a ticket [#408](https://github.com/cucumber/cucumber/issues/408) to track
13
+ the effort to migrate all these tests into the `features` directory. We aim to have this
14
+ completed for the release of Cucumber 2.0
@@ -2,7 +2,7 @@
2
2
  Feature: Language help
3
3
  In order to figure out the keywords to use for a language
4
4
  I want to be able to get help on the language from the CLI
5
-
5
+
6
6
  Scenario: Get help for Portuguese language
7
7
  When I run cucumber --i18n pt help
8
8
  Then it should pass with
@@ -24,6 +24,7 @@ Feature: Language help
24
24
  | but (code) | "Mas" |
25
25
 
26
26
  """
27
+
27
28
  Scenario: List languages
28
29
  When I run cucumber --i18n help
29
30
  Then STDERR should be empty
@@ -51,6 +52,7 @@ Feature: Language help
51
52
  | fa | Persian | فارسی |
52
53
  | fi | Finnish | suomi |
53
54
  | fr | French | français |
55
+ | gl | Galician | galego |
54
56
  | he | Hebrew | עברית |
55
57
  | hi | Hindi | हिंदी |
56
58
  | hr | Croatian | hrvatski |
@@ -18,6 +18,7 @@ Feature: Cucumber command line
18
18
  Given call step "a step definition that calls an undefined step" # features/step_definitions/sample_steps.rb:23
19
19
  Undefined step: "this does not exist" (Cucumber::Undefined)
20
20
  ./features/step_definitions/sample_steps.rb:19:in `/^a step definition that calls an undefined step$/'
21
+ ./features/step_definitions/sample_steps.rb:24:in `/^call step "(.*)"$/'
21
22
  features/call_undefined_step_from_step_def.feature:7:in `Given call step "a step definition that calls an undefined step"'
22
23
 
23
24
  2 scenarios (2 undefined)
@@ -3,6 +3,7 @@ Feature: Use * keywords and still get snippets
3
3
  Given/When/Then, I should not get an exception
4
4
  when I have undefined steps
5
5
 
6
+ @wip-jruby
6
7
  Scenario: Use some *
7
8
  Given a standard Cucumber project directory structure
8
9
  And a file named "features/f.feature" with:
@@ -109,6 +109,10 @@ class CucumberWorld
109
109
  @last_exit_status = $?.exitstatus
110
110
  end
111
111
  @last_stderr = IO.read(stderr_file.path)
112
+ if Cucumber::JRUBY
113
+ # TODO: this actually a workaround for cucumber/gherkin#238
114
+ @last_stderr.gsub!(/^.*java_package_module_template.rb:\d+ warning: `eval' should not be aliased.*\n/, '')
115
+ end
112
116
  end
113
117
 
114
118
  def run_spork_in_background(port = nil)
@@ -1,52 +1,54 @@
1
1
  require 'cucumber/ast/has_steps'
2
2
  require 'cucumber/ast/names'
3
+ require 'cucumber/ast/location'
3
4
 
4
5
  module Cucumber
5
6
  module Ast
6
7
  class Background #:nodoc:
7
8
  include HasSteps
8
9
  include Names
9
- attr_reader :feature_elements
10
+ include HasLocation
11
+ attr_accessor :feature
10
12
 
11
- def initialize(comment, line, keyword, title, description, raw_steps)
12
- @comment, @line, @keyword, @title, @description, @raw_steps = comment, line, keyword, title, description, raw_steps
13
- @feature_elements = []
13
+ def initialize(language, location, comment, keyword, title, description, raw_steps)
14
+ @language, @location, @comment, @keyword, @title, @description, @raw_steps = language, location, comment, keyword, title, description, raw_steps
15
+ @failed = nil
16
+ @first_collection_created = false
17
+ attach_steps(@raw_steps)
14
18
  end
15
19
 
16
- def init
17
- return if @steps
18
- attach_steps(@raw_steps)
19
- @steps = StepCollection.new(@raw_steps)
20
- @step_invocations = @steps.step_invocations(true)
20
+ def feature_elements
21
+ feature.feature_elements
21
22
  end
22
23
 
23
- def step_collection(step_invocations)
24
- init
25
- unless((defined? @first_collection_created) and @first_collection_created)
26
- @first_collection_created = true
27
- @step_invocations.dup(step_invocations)
24
+ def step_invocations
25
+ @step_invocations ||= steps.step_invocations(true)
26
+ end
27
+
28
+ def step_collection(scenario_step_invocations)
29
+ if(@first_collection_created)
30
+ steps.step_invocations(true).dup(scenario_step_invocations)
28
31
  else
29
- @steps.step_invocations(true).dup(step_invocations)
32
+ @first_collection_created = true
33
+ step_invocations.dup(scenario_step_invocations)
30
34
  end
31
35
  end
32
36
 
33
37
  def accept(visitor)
34
38
  return if Cucumber.wants_to_quit
35
- init
36
39
  visitor.visit_comment(@comment) unless @comment.empty?
37
- visitor.visit_background_name(@keyword, name, file_colon_line(@line), source_indent(first_line_length))
40
+ visitor.visit_background_name(@keyword, name, file_colon_line, source_indent(first_line_length))
38
41
  with_visitor(hook_context, visitor) do
39
42
  visitor.runtime.before(hook_context)
40
43
  skip_invoke! if failed?
41
- visitor.visit_steps(@step_invocations)
42
- @failed = @step_invocations.detect{|step_invocation| step_invocation.exception || step_invocation.status != :passed }
43
- visitor.runtime.after(hook_context) if @failed || @feature_elements.empty?
44
+ visitor.visit_steps(step_invocations)
45
+ @failed = step_invocations.any? { |step_invocation| step_invocation.exception || step_invocation.status != :passed }
46
+ visitor.runtime.after(hook_context) if @failed || feature_elements.empty?
44
47
  end
45
48
  end
46
49
 
47
50
  def with_visitor(scenario, visitor)
48
51
  @current_visitor = visitor
49
- init
50
52
  if self != scenario && scenario.respond_to?(:with_visitor)
51
53
  scenario.with_visitor(visitor) do
52
54
  yield
@@ -57,39 +59,32 @@ module Cucumber
57
59
  end
58
60
 
59
61
  def accept_hook?(hook)
60
- init
61
62
  if hook_context != self
62
63
  hook_context.accept_hook?(hook)
63
64
  else
64
65
  # We have no scenarios, just ask our feature
65
- @feature.accept_hook?(hook)
66
+ feature.accept_hook?(hook)
66
67
  end
67
68
  end
68
69
 
69
70
  def skip_invoke!
70
- @step_invocations.each{|step_invocation| step_invocation.skip_invoke!}
71
+ step_invocations.each{|step_invocation| step_invocation.skip_invoke!}
71
72
  end
72
73
 
73
74
  def failed?
74
- if defined? @failed
75
- return @failed
76
- else
77
- return nil
78
- end
75
+ !!@failed
79
76
  end
80
77
 
81
78
  def hook_context
82
- @feature_elements.first || self
79
+ feature_elements.first || self
83
80
  end
84
81
 
85
82
  def to_sexp
86
- init
87
- sexp = [:background, @line, @keyword]
83
+ sexp = [:background, line, @keyword]
88
84
  sexp += [name] unless name.empty?
89
85
  comment = @comment.to_sexp
90
86
  sexp += [comment] if comment
91
- steps = @steps.to_sexp
92
- sexp += steps if steps.any?
87
+ sexp += steps.to_sexp if steps.any?
93
88
  sexp
94
89
  end
95
90
 
@@ -99,7 +94,6 @@ module Cucumber
99
94
  @current_visitor.visit_exception(@exception, :failed)
100
95
  end
101
96
 
102
-
103
97
  # Override this method, as there are situations where the background
104
98
  # wind up being the one called fore Before scenarios, and
105
99
  # backgrounds don't have tags.
@@ -111,6 +105,12 @@ module Cucumber
111
105
  source_tags.map { |tag| tag.name }
112
106
  end
113
107
 
108
+ private
109
+
110
+ def steps
111
+ @steps ||= StepCollection.new(@raw_steps)
112
+ end
113
+
114
114
  end
115
115
  end
116
116
  end
@@ -0,0 +1,33 @@
1
+ require 'cucumber/ast/step_collection'
2
+
3
+ module Cucumber
4
+ module Ast
5
+ class EmptyBackground
6
+ attr_writer :file
7
+ attr_accessor :feature
8
+
9
+ def failed?
10
+ false
11
+ end
12
+
13
+ def feature_elements
14
+ []
15
+ end
16
+
17
+ def step_collection(step_invocations)
18
+ StepCollection.new(step_invocations)
19
+ end
20
+
21
+ def step_invocations
22
+ []
23
+ end
24
+
25
+ def init
26
+ end
27
+
28
+ def accept(visitor)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -4,10 +4,13 @@ module Cucumber
4
4
  module Ast
5
5
  class Examples #:nodoc:
6
6
  include Names
7
+ include HasLocation
7
8
  attr_writer :outline_table
8
9
 
9
- def initialize(comment, line, keyword, title, description, outline_table)
10
- @comment, @keyword, @title, @description, @outline_table = comment, keyword, title, description, outline_table
10
+ def initialize(location, comment, keyword, title, description, outline_table)
11
+ @location, @comment, @keyword, @title, @description, @outline_table = location, comment, keyword, title, description, outline_table
12
+ raise ArgumentError unless @location.is_a?(Location)
13
+ raise ArgumentError unless @comment.is_a?(Comment)
11
14
  end
12
15
 
13
16
  attr_reader :gherkin_statement
@@ -1,17 +1,22 @@
1
1
  require 'cucumber/ast/names'
2
+ require 'cucumber/ast/location'
3
+ require 'cucumber/ast/location'
2
4
 
3
5
  module Cucumber
4
6
  module Ast
5
7
  # Represents the root node of a parsed feature.
6
8
  class Feature #:nodoc:
7
9
  include Names
10
+ include HasLocation
8
11
 
9
12
  attr_accessor :language
10
- attr_writer :features, :background
11
- attr_reader :file, :feature_elements
13
+ attr_reader :feature_elements
12
14
 
13
- def initialize(background, comment, tags, keyword, title, description, feature_elements)
15
+ def initialize(location, background, comment, tags, keyword, title, description, feature_elements)
14
16
  @background, @comment, @tags, @keyword, @title, @description, @feature_elements = background, comment, tags, keyword, title, description, feature_elements
17
+ @background.feature = self
18
+ @location = location
19
+ @feature_elements.each { |e| e.feature = self }
15
20
  end
16
21
 
17
22
  attr_reader :gherkin_statement
@@ -19,26 +24,16 @@ module Cucumber
19
24
  @gherkin_statement ||= statement
20
25
  end
21
26
 
22
- def init
23
- @background.feature = self if @background
24
- @background.init if @background
25
- @feature_elements.each do |feature_element|
26
- feature_element.init
27
- feature_element.feature = self
28
- end
29
- end
30
-
31
- def add_feature_element(feature_element)
32
- @feature_elements << feature_element
27
+ def step_count
28
+ units.inject(0) { |total, unit| total += unit.step_count }
33
29
  end
34
30
 
35
31
  def accept(visitor)
36
32
  return if Cucumber.wants_to_quit
37
- init
38
33
  visitor.visit_comment(@comment) unless @comment.empty?
39
34
  visitor.visit_tags(@tags)
40
35
  visitor.visit_feature_name(@keyword, indented_name)
41
- visitor.visit_background(@background) if @background
36
+ visitor.visit_background(@background) if !@background.is_a?(EmptyBackground)
42
37
  @feature_elements.each do |feature_element|
43
38
  visitor.visit_feature_element(feature_element)
44
39
  end
@@ -65,24 +60,8 @@ module Cucumber
65
60
  @tags.accept_hook?(hook)
66
61
  end
67
62
 
68
- def next_feature_element(feature_element, &proc)
69
- init
70
- index = @feature_elements.index(feature_element)
71
- next_one = @feature_elements[index+1]
72
- proc.call(next_one) if next_one
73
- end
74
-
75
63
  def backtrace_line(step_name, line)
76
- "#{file_colon_line(line)}:in `#{step_name}'"
77
- end
78
-
79
- def file=(file)
80
- file = file.gsub(/\//, '\\') if Cucumber::WINDOWS && file && !ENV['CUCUMBER_FORWARD_SLASH_PATHS']
81
- @file = file
82
- end
83
-
84
- def file_colon_line(line)
85
- "#{@file}:#{line}"
64
+ "#{location.on_line(line)}:in `#{step_name}'"
86
65
  end
87
66
 
88
67
  def short_name
@@ -95,8 +74,7 @@ module Cucumber
95
74
  end
96
75
 
97
76
  def to_sexp
98
- init
99
- sexp = [:feature, @file, name]
77
+ sexp = [:feature, file, name]
100
78
  comment = @comment.to_sexp
101
79
  sexp += [comment] if comment
102
80
  tags = @tags.to_sexp
@@ -105,6 +83,17 @@ module Cucumber
105
83
  sexp += @feature_elements.map{|fe| fe.to_sexp}
106
84
  sexp
107
85
  end
86
+
87
+ private
88
+
89
+ attr_reader :background
90
+
91
+ def units
92
+ @units ||= @feature_elements.map do |element|
93
+ element.to_units(background)
94
+ end.flatten
95
+ end
96
+
108
97
  end
109
98
  end
110
99
  end