turnip 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.md +128 -166
  2. data/examples/autoload_steps.feature +3 -2
  3. data/examples/errors.feature +8 -0
  4. data/examples/step_calling.feature +2 -0
  5. data/examples/steps/alignment_steps.rb +23 -0
  6. data/examples/{autoload_steps.rb → steps/autoload_steps.rb} +0 -0
  7. data/examples/steps/backtick_steps.rb +10 -0
  8. data/examples/steps/dragon_steps.rb +41 -0
  9. data/examples/{knight_steps.rb → steps/knight_steps.rb} +3 -1
  10. data/examples/{more_steps.rb → steps/more_steps.rb} +0 -0
  11. data/examples/{step_calling_steps.rb → steps/step_calling_steps.rb} +0 -0
  12. data/examples/{steps.rb → steps/steps.rb} +25 -1
  13. data/examples/steps_for.feature +2 -2
  14. data/examples/steps_with_variations.feature +17 -0
  15. data/lib/turnip.rb +19 -34
  16. data/lib/turnip/builder.rb +26 -24
  17. data/lib/turnip/define.rb +9 -0
  18. data/lib/turnip/dsl.rb +11 -17
  19. data/lib/turnip/execute.rb +15 -0
  20. data/lib/turnip/rspec.rb +80 -0
  21. data/lib/turnip/step_definition.rb +7 -32
  22. data/lib/turnip/version.rb +1 -1
  23. data/spec/builder_spec.rb +1 -40
  24. data/spec/define_and_execute.rb +38 -0
  25. data/spec/dsl_spec.rb +36 -19
  26. data/spec/integration_spec.rb +5 -1
  27. data/spec/spec_helper.rb +1 -3
  28. data/spec/step_definition_spec.rb +37 -51
  29. metadata +25 -55
  30. data/examples/alignment_steps.rb +0 -7
  31. data/examples/backtick_steps.rb +0 -4
  32. data/examples/dragon_steps.rb +0 -17
  33. data/examples/evil_steps.rb +0 -7
  34. data/examples/neutral_steps.rb +0 -7
  35. data/examples/red_dragon_steps.rb +0 -18
  36. data/lib/turnip/config.rb +0 -18
  37. data/lib/turnip/feature_file.rb +0 -20
  38. data/lib/turnip/loader.rb +0 -16
  39. data/lib/turnip/runner_dsl.rb +0 -9
  40. data/lib/turnip/scenario_context.rb +0 -41
  41. data/lib/turnip/scenario_runner.rb +0 -35
  42. data/lib/turnip/step_loader.rb +0 -27
  43. data/lib/turnip/step_module.rb +0 -89
  44. data/spec/feature_file_spec.rb +0 -18
  45. data/spec/runner_dsl_spec.rb +0 -23
  46. data/spec/scenario_context_spec.rb +0 -51
  47. data/spec/scenario_runner_spec.rb +0 -79
  48. data/spec/step_loader_spec.rb +0 -29
  49. data/spec/step_module_spec.rb +0 -106
@@ -0,0 +1,10 @@
1
+ step "I run :cmd" do |cmd|
2
+ step('I attack it') if cmd == 'killall monsters'
3
+ end
4
+
5
+ placeholder :cmd do
6
+ match /`([^`]*)`/ do |cmd|
7
+ cmd
8
+ end
9
+ end
10
+
@@ -0,0 +1,41 @@
1
+ require_relative "knight_steps"
2
+
3
+ module DragonSteps
4
+ include KnightSteps
5
+
6
+ attr_accessor :dragon
7
+
8
+ def dragon_attack
9
+ dragon * 10
10
+ end
11
+
12
+ step "there is a dragon" do
13
+ self.dragon = 1
14
+ end
15
+
16
+ step "the dragon attacks the knight" do
17
+ knight.attacked_for(dragon_attack)
18
+ end
19
+ end
20
+
21
+ module RedDragonSteps
22
+ include DragonSteps
23
+
24
+ attr_accessor :red_dragon
25
+
26
+ def dragon_attack
27
+ attack = super
28
+ if red_dragon
29
+ attack + 15
30
+ else
31
+ attack
32
+ end
33
+ end
34
+
35
+ step "the dragon breathes fire" do
36
+ self.red_dragon = 1
37
+ end
38
+ end
39
+
40
+ RSpec.configure { |c| c.include DragonSteps, :dragon => true }
41
+ RSpec.configure { |c| c.include RedDragonSteps, :red_dragon => true }
@@ -1,4 +1,4 @@
1
- steps_for :knight do
1
+ module KnightSteps
2
2
  attr_accessor :knight
3
3
 
4
4
  class Knight
@@ -27,3 +27,5 @@ steps_for :knight do
27
27
  knight.should_not be_alive
28
28
  end
29
29
  end
30
+
31
+ RSpec.configure { |c| c.include KnightSteps, :knight => true }
@@ -17,7 +17,7 @@ end
17
17
  step "this is ambiguous" do
18
18
  end
19
19
 
20
- step "this is ambiguous" do
20
+ step "this is :ambiguous" do
21
21
  end
22
22
 
23
23
  step "there is a monster called :name" do |name|
@@ -79,6 +79,30 @@ step "the song should have :count lines" do |count|
79
79
  @song.to_s.split("\n").length.should eq(count)
80
80
  end
81
81
 
82
+ step "it should be strong/tough" do
83
+ @monster.should >= 2
84
+ end
85
+
86
+ step "it should be (a) badass" do
87
+ @monster.should >= 2
88
+ end
89
+
90
+ step "it should be (a) badass" do
91
+ @monster.should >= 2
92
+ end
93
+
94
+ step "it should be terrible(st)" do
95
+ @monster.should >= 2
96
+ end
97
+
98
+ step "it (should) have/has :count (terrifying) hitpoint(s)" do |count|
99
+ @monster.should == count
100
+ end
101
+
102
+ step "raise error" do
103
+ raise "foobar"
104
+ end
105
+
82
106
  placeholder :count do
83
107
  match /\d+/ do |count|
84
108
  count.to_i
@@ -1,10 +1,10 @@
1
1
  Feature: Steps for a feature
2
2
  @evil
3
- Scenario:
3
+ Scenario: Evil
4
4
  Given the monster has an alignment
5
5
  Then that alignment should be "Evil"
6
6
 
7
7
  @neutral
8
- Scenario:
8
+ Scenario: Neutral
9
9
  Given the monster has an alignment
10
10
  Then that alignment should be "Neutral"
@@ -0,0 +1,17 @@
1
+ Feature: steps with variations, such as alternative words or optional words
2
+ Scenario: alternative words
3
+ Given there is a strong monster
4
+ Then it should be strong
5
+ And it should be tough
6
+ Scenario: optional words
7
+ Given there is a strong monster
8
+ Then it should be badass
9
+ And it should be a badass
10
+ Scenario: optional parts of words
11
+ Given there is a strong monster
12
+ Then it should be terrible
13
+ And it should be terriblest
14
+ Scenario: putting it all together
15
+ Given there is a strong monster
16
+ Then it should have 2 terrifying hitpoints
17
+ And it has 2 hitpoint
@@ -1,48 +1,33 @@
1
- require "gherkin"
2
- require "gherkin/formatter/tag_count_formatter"
3
-
4
1
  require "turnip/version"
5
2
  require "turnip/dsl"
6
-
7
- require 'rspec'
3
+ require "turnip/execute"
4
+ require "turnip/define"
5
+ require "turnip/builder"
6
+ require "turnip/step_definition"
7
+ require "turnip/placeholder"
8
+ require "turnip/table"
8
9
 
9
10
  module Turnip
10
- autoload :Config, 'turnip/config'
11
- autoload :FeatureFile, 'turnip/feature_file'
12
- autoload :Loader, 'turnip/loader'
13
- autoload :Builder, 'turnip/builder'
14
- autoload :StepDefinition, 'turnip/step_definition'
15
- autoload :Placeholder, 'turnip/placeholder'
16
- autoload :Table, 'turnip/table'
17
- autoload :StepLoader, 'turnip/step_loader'
18
- autoload :StepModule, 'turnip/step_module'
19
- autoload :ScenarioRunner, 'turnip/scenario_runner'
20
- autoload :RunnerDSL, 'turnip/runner_dsl'
21
- autoload :ScenarioContext, 'turnip/scenario_context'
11
+ class Pending < StandardError; end
12
+ class Ambiguous < StandardError; end
13
+
14
+ ##
15
+ #
16
+ # The global step module, adding steps here will make them available in all
17
+ # your tests.
18
+ #
19
+ module Steps
20
+ end
22
21
 
23
22
  class << self
24
23
  attr_accessor :type
25
-
26
- def run(feature_file)
27
- Turnip::Builder.build(feature_file).features.each do |feature|
28
- describe feature.name, feature.metadata_hash do
29
- feature.scenarios.each do |scenario|
30
- it scenario.name, scenario.metadata_hash do
31
- Turnip::ScenarioRunner.new(self).load(Turnip::ScenarioContext.new(feature, scenario)).run
32
- end
33
- end
34
- end
35
- end
36
- end
37
24
  end
38
25
  end
39
26
 
40
27
  Turnip.type = :turnip
41
28
 
42
- RSpec::Core::Configuration.send(:include, Turnip::Loader)
43
-
44
- RSpec.configure do |config|
45
- config.pattern << ",**/*.feature"
46
- end
29
+ Module.send(:include, Turnip::Define)
47
30
 
48
31
  self.extend Turnip::DSL
32
+
33
+ require "turnip/rspec"
@@ -1,13 +1,11 @@
1
+ require "gherkin"
2
+
1
3
  module Turnip
2
4
  class Builder
3
5
  module Tags
4
6
  def tags
5
7
  @raw.tags.map { |tag| tag.name.sub(/^@/, '') }
6
8
  end
7
-
8
- def active_tags
9
- tags.map(&:to_sym)
10
- end
11
9
 
12
10
  def tags_hash
13
11
  Hash[tags.map { |t| [t.to_sym, true] }]
@@ -36,13 +34,9 @@ module Turnip
36
34
  @scenarios = []
37
35
  @backgrounds = []
38
36
  end
39
-
40
- # Feature's active_tags automatically prepends the :global tag
41
- # as well as its feature_tag if defined
42
- def active_tags
43
- active_tags = [:global]
44
- active_tags << feature_tag.to_sym if feature_tag
45
- active_tags + super
37
+
38
+ def line
39
+ @raw.line
46
40
  end
47
41
 
48
42
  def metadata_hash
@@ -88,30 +82,36 @@ module Turnip
88
82
  Scenario.new(@raw).tap do |scenario|
89
83
  scenario.steps = steps.map do |step|
90
84
  new_description = step.description.gsub(/<([^>]*)>/) { |_| Hash[headers.zip(row)][$1] }
91
- Step.new(new_description, step.extra_arg)
85
+ Step.new(new_description, step.extra_args, step.line)
92
86
  end
93
87
  end
94
88
  end
95
89
  end
96
90
  end
97
91
 
98
- class Step < Struct.new(:description, :extra_arg)
92
+ class Step < Struct.new(:description, :extra_args, :line)
93
+ # 1.9.2 support hack
94
+ def split(*args)
95
+ self.to_s.split(*args)
96
+ end
97
+
98
+ def to_s
99
+ description
100
+ end
99
101
  end
100
102
 
101
103
  attr_reader :features
102
104
 
103
105
  class << self
104
106
  def build(feature_file)
105
- Turnip::Builder.new(feature_file).tap do |builder|
106
- formatter = Gherkin::Formatter::TagCountFormatter.new(builder, {})
107
- parser = Gherkin::Parser::Parser.new(formatter, true, "root", false)
108
- parser.parse(feature_file.content, nil, 0)
107
+ Turnip::Builder.new.tap do |builder|
108
+ parser = Gherkin::Parser::Parser.new(builder, true)
109
+ parser.parse(File.read(feature_file), nil, 0)
109
110
  end
110
111
  end
111
112
  end
112
113
 
113
- def initialize(feature_file)
114
- @feature_file = feature_file
114
+ def initialize
115
115
  @features = []
116
116
  end
117
117
 
@@ -122,8 +122,6 @@ module Turnip
122
122
 
123
123
  def feature(feature)
124
124
  @current_feature = Feature.new(feature)
125
- # Automatically add a tag based on the name of the feature to the Feature if configured to
126
- @current_feature.feature_tag = @feature_file.feature_name if Turnip::Config.autotag_features
127
125
  @features << @current_feature
128
126
  end
129
127
 
@@ -141,12 +139,16 @@ module Turnip
141
139
  end
142
140
 
143
141
  def step(step)
142
+ extra_args = []
144
143
  if step.doc_string
145
- extra_arg = step.doc_string.value
144
+ extra_args.push step.doc_string.value
146
145
  elsif step.rows
147
- extra_arg = Turnip::Table.new(step.rows.map { |row| row.cells(&:value) })
146
+ extra_args.push Turnip::Table.new(step.rows.map { |row| row.cells(&:value) })
148
147
  end
149
- @current_step_context.steps << Step.new(step.name, extra_arg)
148
+ @current_step_context.steps << Step.new(step.name, extra_args, step.line)
149
+ end
150
+
151
+ def uri(*)
150
152
  end
151
153
 
152
154
  def eof
@@ -0,0 +1,9 @@
1
+ module Turnip
2
+ module Define
3
+ def step(expression, &block)
4
+ step = Turnip::StepDefinition.new(expression, &block)
5
+ send(:define_method, "match: #{expression}") { |description| step.match(description) }
6
+ send(:define_method, expression, &block)
7
+ end
8
+ end
9
+ end
@@ -5,26 +5,20 @@ module Turnip
5
5
  end
6
6
 
7
7
  def step(description, &block)
8
- global_step_module_entry.step_module.steps << Turnip::StepDefinition.new(description, &block)
8
+ Turnip::Steps.step(description, &block)
9
9
  end
10
10
 
11
11
  def steps_for(tag, &block)
12
- Turnip::StepModule.steps_for(tag, &block)
13
- end
14
-
15
- private
16
-
17
- def global_step_module_entry
18
- @global_step_module_entry ||= begin
19
- anon = Module.new do
20
- def self.steps
21
- @steps ||= []
22
- end
23
- end
24
- entry = Turnip::StepModule::Entry.new([:global], anon, [])
25
- Turnip::StepModule.module_registry[:global] << entry
26
- entry
27
- end
12
+ if tag.to_s == "global"
13
+ warn "[Turnip] using steps_for(:global) is deprecated, add steps to Turnip::Steps instead"
14
+ Turnip::Steps.module_eval(&block)
15
+ else
16
+ Module.new do
17
+ singleton_class.send(:define_method, :tag) { tag }
18
+ module_eval(&block)
19
+ ::RSpec.configure { |c| c.include self, tag => true }
20
+ end
21
+ end
28
22
  end
29
23
  end
30
24
  end
@@ -0,0 +1,15 @@
1
+ module Turnip
2
+ module Execute
3
+ def step(description, *extra_args)
4
+ extra_args.concat(description.extra_args) if description.respond_to?(:extra_args)
5
+
6
+ matches = methods.map do |method|
7
+ next unless method.to_s.start_with?("match: ")
8
+ send(method.to_s, description.to_s)
9
+ end.compact
10
+ raise Turnip::Pending, description if matches.length == 0
11
+ raise Turnip::Ambiguous, description if matches.length > 1
12
+ send(matches.first.expression, *(matches.first.params + extra_args))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,80 @@
1
+ require "turnip"
2
+ require "rspec"
3
+
4
+ module Turnip
5
+ module RSpec
6
+
7
+ ##
8
+ #
9
+ # This module hooks Turnip into RSpec by duck punching the load Kernel
10
+ # method. If the file is a feature file, we run Turnip instead!
11
+ #
12
+ module Loader
13
+ def load(*a, &b)
14
+ if a.first.end_with?('.feature')
15
+ begin
16
+ require 'spec_helper'
17
+ rescue LoadError
18
+ end
19
+ Turnip::RSpec.run(a.first)
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+
26
+ ##
27
+ #
28
+ # This module provides an improved method to run steps inside RSpec, adding
29
+ # proper support for pending steps, as well as nicer backtraces.
30
+ #
31
+ module Execute
32
+ include Turnip::Execute
33
+
34
+ def run_step(feature_file, step)
35
+ begin
36
+ step(step)
37
+ rescue Turnip::Pending
38
+ pending("No such step: '#{step}'")
39
+ rescue StandardError => e
40
+ e.backtrace.push "#{feature_file}:#{step.line}:in `#{step.description}'"
41
+ raise e
42
+ end
43
+ end
44
+ end
45
+
46
+ class << self
47
+ def run(feature_file)
48
+ Turnip::Builder.build(feature_file).features.each do |feature|
49
+ describe feature.name, feature.metadata_hash do
50
+ before do
51
+ # This is kind of a hack, but it will make RSpec throw way nicer exceptions
52
+ example.metadata[:file_path] = feature_file
53
+
54
+ feature.backgrounds.map(&:steps).flatten.each do |step|
55
+ run_step(feature_file, step)
56
+ end
57
+ end
58
+ feature.scenarios.each do |scenario|
59
+ describe scenario.name, scenario.metadata_hash do
60
+ it scenario.steps.map(&:description).join(' -> ') do
61
+ scenario.steps.each do |step|
62
+ run_step(feature_file, step)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ ::RSpec::Core::Configuration.send(:include, Turnip::RSpec::Loader)
75
+
76
+ ::RSpec.configure do |config|
77
+ config.include Turnip::RSpec::Execute
78
+ config.include Turnip::Steps
79
+ config.pattern << ",**/*.feature"
80
+ end