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.
- 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
|