turnip 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/README.md +196 -44
- data/Rakefile +8 -0
- data/examples/alignment_steps.rb +7 -0
- data/examples/autoload_steps.feature +5 -0
- data/examples/autoload_steps.rb +5 -0
- data/examples/dragon_steps.rb +17 -0
- data/examples/evil_steps.rb +7 -0
- data/examples/knight_steps.rb +29 -0
- data/examples/more_steps.rb +7 -0
- data/examples/neutral_steps.rb +7 -0
- data/examples/red_dragon_steps.rb +18 -0
- data/examples/step_calling.feature +14 -0
- data/examples/step_calling_steps.rb +23 -0
- data/examples/steps.rb +0 -22
- data/examples/steps_for_super.feature +16 -0
- data/lib/turnip.rb +12 -12
- data/lib/turnip/builder.rb +20 -4
- data/lib/turnip/config.rb +18 -0
- data/lib/turnip/dsl.rb +19 -13
- data/lib/turnip/feature_file.rb +20 -0
- data/lib/turnip/loader.rb +6 -3
- data/lib/turnip/placeholder.rb +1 -1
- data/lib/turnip/runner_dsl.rb +9 -0
- data/lib/turnip/scenario_context.rb +41 -0
- data/lib/turnip/scenario_runner.rb +35 -0
- data/lib/turnip/step_definition.rb +14 -30
- data/lib/turnip/step_loader.rb +27 -0
- data/lib/turnip/step_module.rb +89 -0
- data/lib/turnip/table.rb +12 -0
- data/lib/turnip/version.rb +1 -1
- data/spec/builder_spec.rb +41 -2
- data/spec/dsl_spec.rb +22 -34
- data/spec/feature_file_spec.rb +18 -0
- data/spec/integration_spec.rb +1 -1
- data/spec/runner_dsl_spec.rb +23 -0
- data/spec/scenario_context_spec.rb +51 -0
- data/spec/scenario_runner_spec.rb +79 -0
- data/spec/spec_helper.rb +1 -3
- data/spec/step_definition_spec.rb +22 -34
- data/spec/step_loader_spec.rb +29 -0
- data/spec/step_module_spec.rb +106 -0
- data/spec/table_spec.rb +8 -1
- data/turnip.gemspec +2 -1
- 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
|
data/lib/turnip/table.rb
CHANGED
@@ -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
|
data/lib/turnip/version.rb
CHANGED
data/spec/builder_spec.rb
CHANGED
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Turnip::Builder do
|
4
4
|
context "with scenario outlines" do
|
5
|
-
let(:
|
6
|
-
let(:builder) { Turnip::Builder.build(
|
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
|
data/spec/dsl_spec.rb
CHANGED
@@ -2,53 +2,41 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Turnip::DSL do
|
4
4
|
before do
|
5
|
-
|
6
|
-
@context.extend(Turnip::DSL)
|
5
|
+
Turnip::StepModule.clear_module_registry
|
7
6
|
end
|
8
7
|
|
9
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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 '
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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 '
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
Turnip::Placeholder.
|
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
|
data/spec/integration_spec.rb
CHANGED
@@ -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
|