turnip 0.2.0 → 0.3.0

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.
Files changed (46) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +5 -0
  3. data/README.md +196 -44
  4. data/Rakefile +8 -0
  5. data/examples/alignment_steps.rb +7 -0
  6. data/examples/autoload_steps.feature +5 -0
  7. data/examples/autoload_steps.rb +5 -0
  8. data/examples/dragon_steps.rb +17 -0
  9. data/examples/evil_steps.rb +7 -0
  10. data/examples/knight_steps.rb +29 -0
  11. data/examples/more_steps.rb +7 -0
  12. data/examples/neutral_steps.rb +7 -0
  13. data/examples/red_dragon_steps.rb +18 -0
  14. data/examples/step_calling.feature +14 -0
  15. data/examples/step_calling_steps.rb +23 -0
  16. data/examples/steps.rb +0 -22
  17. data/examples/steps_for_super.feature +16 -0
  18. data/lib/turnip.rb +12 -12
  19. data/lib/turnip/builder.rb +20 -4
  20. data/lib/turnip/config.rb +18 -0
  21. data/lib/turnip/dsl.rb +19 -13
  22. data/lib/turnip/feature_file.rb +20 -0
  23. data/lib/turnip/loader.rb +6 -3
  24. data/lib/turnip/placeholder.rb +1 -1
  25. data/lib/turnip/runner_dsl.rb +9 -0
  26. data/lib/turnip/scenario_context.rb +41 -0
  27. data/lib/turnip/scenario_runner.rb +35 -0
  28. data/lib/turnip/step_definition.rb +14 -30
  29. data/lib/turnip/step_loader.rb +27 -0
  30. data/lib/turnip/step_module.rb +89 -0
  31. data/lib/turnip/table.rb +12 -0
  32. data/lib/turnip/version.rb +1 -1
  33. data/spec/builder_spec.rb +41 -2
  34. data/spec/dsl_spec.rb +22 -34
  35. data/spec/feature_file_spec.rb +18 -0
  36. data/spec/integration_spec.rb +1 -1
  37. data/spec/runner_dsl_spec.rb +23 -0
  38. data/spec/scenario_context_spec.rb +51 -0
  39. data/spec/scenario_runner_spec.rb +79 -0
  40. data/spec/spec_helper.rb +1 -3
  41. data/spec/step_definition_spec.rb +22 -34
  42. data/spec/step_loader_spec.rb +29 -0
  43. data/spec/step_module_spec.rb +106 -0
  44. data/spec/table_spec.rb +8 -1
  45. data/turnip.gemspec +2 -1
  46. metadata +51 -7
@@ -0,0 +1,27 @@
1
+ module Turnip
2
+ module StepLoader
3
+ extend self
4
+
5
+ attr_accessor :steps_loaded
6
+
7
+ def load_steps
8
+ return if steps_loaded?
9
+ load_step_files
10
+ self.steps_loaded = true
11
+ end
12
+
13
+ def steps_loaded?
14
+ @steps_loaded
15
+ end
16
+
17
+ private
18
+
19
+ def load_step_files
20
+ Turnip::Config.step_dirs.each do |dir|
21
+ Pathname.glob(Pathname.new(dir) + '**' + "*steps.rb").each do |step_file|
22
+ load step_file, true
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,89 @@
1
+ require 'pathname'
2
+
3
+ module Turnip
4
+ module StepModule
5
+ module DSL
6
+ def placeholder(name, &block)
7
+ Turnip::Placeholder.add(name, &block)
8
+ end
9
+
10
+ def step(description, &block)
11
+ steps << Turnip::StepDefinition.new(description, &block)
12
+ end
13
+
14
+ def steps
15
+ @steps ||= []
16
+ end
17
+
18
+ def use_steps(*tags)
19
+ uses_steps.concat(tags)
20
+ end
21
+
22
+ def uses_steps
23
+ @uses_steps ||= []
24
+ end
25
+ end
26
+
27
+ class Entry < Struct.new(:for_tag, :step_module, :uses_steps)
28
+ def all_modules(already_visited = [])
29
+ if already_visited.include?(for_tag)
30
+ []
31
+ else
32
+ already_visited << for_tag
33
+ uses_modules(already_visited) << step_module
34
+ end
35
+ end
36
+
37
+ def uses_modules(already_visited)
38
+ uses_steps.map do |uses_tag|
39
+ StepModule.module_registry[uses_tag].map do |entry|
40
+ entry.all_modules(already_visited)
41
+ end
42
+ end.flatten.uniq
43
+ end
44
+ end
45
+
46
+ extend self
47
+
48
+ def all_steps_for(*taggings)
49
+ modules_for(*taggings).map do |step_module|
50
+ step_module.steps
51
+ end.flatten
52
+ end
53
+
54
+ def clear_module_registry
55
+ module_registry.clear
56
+ end
57
+
58
+ def modules_for(*taggings)
59
+ taggings.map do |tag|
60
+ module_registry[tag].map do |entry|
61
+ entry.all_modules
62
+ end
63
+ end.flatten.uniq
64
+ end
65
+
66
+ def module_registry
67
+ @module_registry ||= Hash.new { |hash, key| hash[key] = [] }
68
+ end
69
+
70
+ def registered?(module_name)
71
+ module_registry.has_key? module_name
72
+ end
73
+
74
+ def steps_for(tag, &block)
75
+ anon = step_module(&block)
76
+
77
+ entry = Entry.new(tag, anon, anon.uses_steps)
78
+
79
+ module_registry[tag] << entry
80
+ end
81
+
82
+ def step_module(&block)
83
+ anon = Module.new
84
+ anon.extend(Turnip::StepModule::DSL)
85
+ anon.module_eval(&block)
86
+ anon
87
+ end
88
+ end
89
+ end
@@ -20,9 +20,21 @@ module Turnip
20
20
  def hashes
21
21
  rows.map { |row| Hash[headers.zip(row)] }
22
22
  end
23
+
24
+ def rows_hash
25
+ return @rows_hash if @rows_hash
26
+ verify_table_width(2)
27
+ @rows_hash = self.class.new(raw.transpose).hashes[0]
28
+ end
23
29
 
24
30
  def each
25
31
  @raw.each { |row| yield(row) }
26
32
  end
33
+
34
+ private
35
+
36
+ def verify_table_width(width)
37
+ raise %{The table must have exactly #{width} columns} unless raw[0].size == width
38
+ end
27
39
  end
28
40
  end
@@ -1,3 +1,3 @@
1
1
  module Turnip
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe Turnip::Builder do
4
4
  context "with scenario outlines" do
5
- let(:gherkin) { File.read(File.expand_path('../examples/scenario_outline.feature', File.dirname(__FILE__))) }
6
- let(:builder) { Turnip::Builder.build(gherkin) }
5
+ let(:feature_file) { Turnip::FeatureFile.new(File.expand_path('../examples/scenario_outline.feature', File.dirname(__FILE__))) }
6
+ let(:builder) { Turnip::Builder.build(feature_file) }
7
7
  let(:feature) { builder.features.first }
8
8
 
9
9
 
@@ -27,4 +27,43 @@ describe Turnip::Builder do
27
27
  ])
28
28
  end
29
29
  end
30
+
31
+ describe "taggings" do
32
+ let(:feature_file) { Turnip::FeatureFile.new(File.expand_path('../examples/autoload_steps.feature', File.dirname(__FILE__))) }
33
+ let(:builder) { Turnip::Builder.build(feature_file) }
34
+ let(:feature) { builder.features.first }
35
+
36
+ context "for features" do
37
+ it 'should automatically include the :global tag for features' do
38
+ feature.active_tags.should include(:global)
39
+ end
40
+
41
+ it 'should automatically include the feature name tag for features' do
42
+ feature.active_tags.should include(:autoload_steps)
43
+ end
44
+ end
45
+
46
+ context "for scenarios" do
47
+ it 'should only include scenario tags' do
48
+ feature.scenarios.first.active_tags.should eq([:scenario_tag])
49
+ end
50
+ end
51
+
52
+ context "autotag features disabled" do
53
+ before(:each) { Turnip::Config.autotag_features = false }
54
+ after(:each) { Turnip::Config.autotag_features = true }
55
+
56
+ let(:feature_file) { Turnip::FeatureFile.new(File.expand_path('../examples/autoload_steps.feature', File.dirname(__FILE__))) }
57
+ let(:builder) { Turnip::Builder.build(feature_file) }
58
+ let(:feature) { builder.features.first }
59
+
60
+ it 'should automatically include the :global tag for features' do
61
+ feature.active_tags.should include(:global)
62
+ end
63
+
64
+ it 'should not automatically include the feature name tag for features' do
65
+ feature.active_tags.should_not include(:autoload_steps)
66
+ end
67
+ end
68
+ end
30
69
  end
@@ -2,53 +2,41 @@ require 'spec_helper'
2
2
 
3
3
  describe Turnip::DSL do
4
4
  before do
5
- @context = stub
6
- @context.extend(Turnip::DSL)
5
+ Turnip::StepModule.clear_module_registry
7
6
  end
8
7
 
9
- describe '#step' do
10
- it 'adds a step to the list of step definitions' do
11
- Turnip::StepDefinition.should_receive(:add).with('this is a :thing test', {})
12
- @context.step 'this is a :thing test'
13
- end
8
+ let(:context) { stub.tap { |s| s.extend(Turnip::DSL) }}
14
9
 
15
- it 'sends through options' do
16
- Turnip::StepDefinition.should_receive(:add).with('foo', {:for => [:monkey]})
17
- @context.step 'foo', :for => [:monkey]
10
+ describe '.steps_for' do
11
+ it 'delegates to StepModule' do
12
+ Turnip::StepModule.should_receive(:steps_for).with(:example)
13
+ context.steps_for(:example) {}
18
14
  end
19
15
  end
20
16
 
21
- describe '#steps_for' do
22
- it 'executes the given block and adds steps with for set' do
23
- Turnip::StepDefinition.should_receive(:add).with('foo', {:for => [:gorilla]})
24
- @context.steps_for :gorilla do
25
- @context.step 'foo'
26
- end
27
- end
28
-
29
- it 'combines step for option and block options' do
30
- Turnip::StepDefinition.should_receive(:add).with('foo', {:for => [:a, :b, :gorilla]})
31
- @context.steps_for :gorilla do
32
- @context.step 'foo', :for => [:a, :b]
17
+ describe '.step' do
18
+ context 'first step defined globally' do
19
+ it 'creates a new global entry' do
20
+ context.step('this is a test') {}
21
+ Turnip::StepModule.should be_registered(:global)
33
22
  end
34
23
  end
35
24
 
36
- it 'can be nested' do
37
- Turnip::StepDefinition.should_receive(:add).with('foo', {:for => [:c, :b, :a]})
38
- @context.steps_for :a do
39
- @context.steps_for :b do
40
- @context.step 'foo', :for => :c
41
- end
25
+ context 'all other steps defined globally' do
26
+ it 'adds more steps to the :global step module' do
27
+ context.step('this is a test') {}
28
+ context.step('this is another test') {}
29
+ Turnip::StepModule.module_registry[:global].first.step_module.steps.size.should eq(2)
42
30
  end
43
31
  end
44
32
  end
45
33
 
46
- describe '#placeholder' do
47
- it 'adds a placeholder to the list of placeholders' do
48
- @context.placeholder :quox do
49
- match(/foo/) { 'bar' }
50
- end
51
- Turnip::Placeholder.apply(:quox, 'foo').should == 'bar'
34
+ describe '.placeholder' do
35
+ before { Turnip::Placeholder.send(:placeholders).clear }
36
+
37
+ it 'registers the placeholder globally' do
38
+ context.placeholder('example') { true }
39
+ Turnip::Placeholder.send(:placeholders).should have_key('example')
52
40
  end
53
41
  end
54
42
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Turnip::FeatureFile do
4
+ let(:file_name) {File.expand_path('../examples/with_comments.feature', File.dirname(__FILE__))}
5
+ let(:feature_file) {Turnip::FeatureFile.new(file_name)}
6
+
7
+ describe '.feature_name' do
8
+ it 'allows access to short name for the feature based on the file name' do
9
+ feature_file.feature_name.should == 'with_comments'
10
+ end
11
+ end
12
+
13
+ describe '.content' do
14
+ it 'allows access to the content in the feature file' do
15
+ feature_file.content.should be
16
+ end
17
+ end
18
+ end
@@ -11,6 +11,6 @@ describe 'The CLI', :type => :integration do
11
11
  end
12
12
 
13
13
  it "prints out failures and successes" do
14
- @result.should include('16 examples, 1 failure, 1 pending')
14
+ @result.should include('23 examples, 1 failure, 2 pending')
15
15
  end
16
16
  end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Turnip::RunnerDSL do
4
+ describe '#step' do
5
+ include Turnip::RunnerDSL
6
+
7
+ it 'runs the step' do
8
+ self.turnip_runner = Class.new do
9
+ attr_accessor :args
10
+ def run_steps(steps)
11
+ self.args << steps
12
+ end
13
+ end.new
14
+ self.turnip_runner.args = []
15
+
16
+ step('description', 'extra_arg')
17
+ step = turnip_runner.args.flatten.first
18
+ step.should be_kind_of(Turnip::Builder::Step)
19
+ step.description.should eq('description')
20
+ step.extra_arg.should eq('extra_arg')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Turnip::ScenarioContext do
4
+ let(:context) { Turnip::ScenarioContext.new(feature, scenario) }
5
+ let(:feature) { stub(:active_tags => feature_tags) }
6
+ let(:feature_tags) { %w(feature both) }
7
+ let(:scenario) { stub(:active_tags => scenario_tags) }
8
+ let(:scenario_tags) { %w(scenario both) }
9
+ let(:unique_tags) { (feature_tags + scenario_tags).uniq }
10
+
11
+ describe '#initialize' do
12
+ let(:feature) { stub }
13
+ let(:scenario) { stub }
14
+
15
+ it 'keeps track of the feature' do
16
+ Turnip::ScenarioContext.new(feature, scenario).feature.should eq(feature)
17
+ end
18
+
19
+ it 'keeps track of the scenario' do
20
+ Turnip::ScenarioContext.new(feature, scenario).scenario.should eq(scenario)
21
+ end
22
+ end
23
+
24
+ describe '#available_background_steps' do
25
+ it 'gathers the steps for the feature tags' do
26
+ Turnip::StepModule.should_receive(:all_steps_for).with(*feature_tags)
27
+ context.available_background_steps
28
+ end
29
+ end
30
+
31
+ describe '#available_scenario_steps' do
32
+ it 'gathers the steps for the unique scenario and feature tags' do
33
+ Turnip::StepModule.should_receive(:all_steps_for).with(*unique_tags)
34
+ context.available_scenario_steps
35
+ end
36
+ end
37
+
38
+ describe '#backgrounds' do
39
+ it 'delegates to the feature' do
40
+ feature.should_receive :backgrounds
41
+ context.backgrounds
42
+ end
43
+ end
44
+
45
+ describe '#modules' do
46
+ it 'gathers the modules for the unique scenario and feature tags' do
47
+ Turnip::StepModule.should_receive(:modules_for).with(*unique_tags)
48
+ context.modules
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe Turnip::ScenarioRunner do
4
+ let(:runner) { Turnip::ScenarioRunner.new(world) }
5
+ let(:world) { Object.new }
6
+
7
+ describe '#initialize' do
8
+ it 'keeps track of the world' do
9
+ Turnip::ScenarioRunner.new(world).world.should eq(world)
10
+ end
11
+ end
12
+
13
+ describe '#load' do
14
+ let(:context) { stub(:modules => [some_module]) }
15
+ let(:some_module) { Module.new }
16
+
17
+ it 'is chainable' do
18
+ runner.load(context).should eq(runner)
19
+ end
20
+
21
+ it 'keeps track of the scenario context' do
22
+ runner.load(context).context.should eq(context)
23
+ end
24
+
25
+ it 'loads the given modules into the world' do
26
+ runner.load(context).world.should be_kind_of(some_module)
27
+ end
28
+
29
+ it 'loads the DSL module into the world' do
30
+ runner.load(context).world.should be_kind_of(Turnip::RunnerDSL)
31
+ end
32
+
33
+ it 'adds the runner to the world' do
34
+ runner.load(context).world.turnip_runner.should eq(runner)
35
+ end
36
+ end
37
+
38
+ describe '#run' do
39
+ it 'iterates over the background steps' do
40
+ runner.context = stub(:backgrounds => (0..2).map { stub(:steps => [stub]) },
41
+ :available_background_steps => [],
42
+ :available_scenario_steps => [],
43
+ :scenario => stub(:steps => []))
44
+
45
+ Turnip::StepDefinition.should_receive(:execute).exactly(3).times
46
+ runner.run
47
+ end
48
+
49
+ it 'iterates over the scenario steps' do
50
+ runner.context = stub(:backgrounds => [],
51
+ :available_background_steps => [],
52
+ :available_scenario_steps => [],
53
+ :scenario => stub(:steps => (0..3)))
54
+
55
+ Turnip::StepDefinition.should_receive(:execute).exactly(4).times
56
+ runner.run
57
+ end
58
+ end
59
+
60
+ describe '#run_steps' do
61
+ let(:available_steps) { stub }
62
+
63
+ it 'executes the steps with the current world' do
64
+ step = stub
65
+ steps = [step]
66
+ runner.available_steps = available_steps
67
+
68
+ Turnip::StepDefinition.should_receive(:execute).with(world, available_steps, step)
69
+ runner.run_steps(steps)
70
+ end
71
+
72
+ it 'iterates over the steps' do
73
+ steps = (0..2)
74
+
75
+ Turnip::StepDefinition.should_receive(:execute).exactly(3).times
76
+ runner.run_steps(steps)
77
+ end
78
+ end
79
+ end