turnip 0.2.0 → 0.3.0

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