spinach 0.3.4 → 0.4.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/lib/spinach/cli.rb +19 -1
- data/lib/spinach/config.rb +19 -3
- data/lib/spinach/runner/feature_runner.rb +11 -4
- data/lib/spinach/tags_matcher.rb +47 -0
- data/lib/spinach/version.rb +1 -1
- data/test/spinach/cli_test.rb +36 -0
- data/test/spinach/config_test.rb +11 -1
- data/test/spinach/runner/feature_runner_test.rb +34 -4
- data/test/spinach/tags_matcher_test.rb +118 -0
- metadata +5 -2
data/lib/spinach/cli.rb
CHANGED
@@ -75,8 +75,26 @@ module Spinach
|
|
75
75
|
reporter_options[:backtrace] = show_backtrace
|
76
76
|
end
|
77
77
|
|
78
|
+
opts.on('-t', '--tags TAG',
|
79
|
+
'Run all scenarios for given tags.') do |tag|
|
80
|
+
config[:tags] ||= []
|
81
|
+
tags = tag.delete('@').split(',')
|
82
|
+
|
83
|
+
references_wip = lambda { |tag_groups|
|
84
|
+
tag_groups.any? { |tag_group|
|
85
|
+
tag_group.any? { |tag| tag =~ /wip$/ }
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
unless references_wip.(config[:tags]) || references_wip.([tags])
|
90
|
+
tags.unshift '~wip'
|
91
|
+
end
|
92
|
+
|
93
|
+
config[:tags] << tags
|
94
|
+
end
|
95
|
+
|
78
96
|
opts.on('-g', '--generate',
|
79
|
-
'Auto-generate the
|
97
|
+
'Auto-generate the feature steps files') do
|
80
98
|
Spinach::Generators.bind
|
81
99
|
end
|
82
100
|
|
data/lib/spinach/config.rb
CHANGED
@@ -20,8 +20,14 @@ module Spinach
|
|
20
20
|
# to run.
|
21
21
|
#
|
22
22
|
class Config
|
23
|
-
attr_writer :features_path,
|
24
|
-
|
23
|
+
attr_writer :features_path,
|
24
|
+
:step_definitions_path,
|
25
|
+
:default_reporter,
|
26
|
+
:support_path,
|
27
|
+
:failure_exceptions,
|
28
|
+
:config_path,
|
29
|
+
:tags,
|
30
|
+
:save_and_open_page_on_failure
|
25
31
|
|
26
32
|
# The "features path" holds the place where your features will be
|
27
33
|
# searched for. Defaults to 'features'
|
@@ -106,13 +112,23 @@ module Spinach
|
|
106
112
|
@config_path ||= 'config/spinach.yml'
|
107
113
|
end
|
108
114
|
|
109
|
-
# When using capybara, it automatically shows the current page when there's
|
115
|
+
# When using capybara, it automatically shows the current page when there's
|
110
116
|
# a failure
|
111
117
|
#
|
112
118
|
def save_and_open_page_on_failure
|
113
119
|
@save_and_open_page_on_failure ||= false
|
114
120
|
end
|
115
121
|
|
122
|
+
# Tags to tell Spinach that you only want to run scenarios that have (or
|
123
|
+
# don't have) certain tags.
|
124
|
+
#
|
125
|
+
# @return [Array]
|
126
|
+
# The tags.
|
127
|
+
#
|
128
|
+
def tags
|
129
|
+
@tags ||= []
|
130
|
+
end
|
131
|
+
|
116
132
|
# Parse options from the config file
|
117
133
|
#
|
118
134
|
# @return [Boolean]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../tags_matcher'
|
2
|
+
|
1
3
|
module Spinach
|
2
4
|
class Runner
|
3
5
|
# A feature runner handles a particular feature run.
|
@@ -55,20 +57,25 @@ module Spinach
|
|
55
57
|
|
56
58
|
def run_scenarios!
|
57
59
|
scenarios.each_with_index do |scenario, current_scenario_index|
|
58
|
-
if
|
60
|
+
if run_scenario?(scenario, current_scenario_index)
|
59
61
|
success = ScenarioRunner.new(scenario).run
|
60
62
|
@failed = true unless success
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
def
|
67
|
+
def run_scenario?(scenario, current_scenario_index)
|
68
|
+
match_line(current_scenario_index) && TagsMatcher.match(scenario.tags)
|
69
|
+
end
|
70
|
+
|
71
|
+
def match_line(current_scenario_index)
|
66
72
|
return true unless @line
|
67
|
-
return false if @line<scenarios[current_scenario_index].line
|
73
|
+
return false if @line < scenarios[current_scenario_index].line
|
68
74
|
next_scenario = scenarios[current_scenario_index+1]
|
69
|
-
!next_scenario || @line<next_scenario.line
|
75
|
+
!next_scenario || @line < next_scenario.line
|
70
76
|
end
|
71
77
|
|
78
|
+
|
72
79
|
def undefined_steps!
|
73
80
|
Spinach.hooks.run_on_undefined_feature @feature
|
74
81
|
@failed = true
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Spinach
|
2
|
+
module TagsMatcher
|
3
|
+
|
4
|
+
NEGATION_SIGN = '~'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Matches an array of tags (e.g. of a scenario) against the tags present
|
9
|
+
# in Spinach' runtime options.
|
10
|
+
#
|
11
|
+
# Spinach' tag option is an array which consists of (possibly) multiple
|
12
|
+
# arrays containing tags provided by the user running the features and
|
13
|
+
# scenarios. Each of these arrays is considered a tag group.
|
14
|
+
#
|
15
|
+
# When matching tags against the tags groups, the tags inside a tag group
|
16
|
+
# are OR-ed and the tag groups themselves are AND-ed.
|
17
|
+
def match(tags)
|
18
|
+
return true if tag_groups.empty?
|
19
|
+
|
20
|
+
tag_groups.all? { |tag_group| match_tag_group(tag_group, tags) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def tag_groups
|
26
|
+
Spinach.config.tags
|
27
|
+
end
|
28
|
+
|
29
|
+
def match_tag_group(tag_group, tags)
|
30
|
+
tag_group.any? do |tag|
|
31
|
+
tag_matched = tags.include?(tag.delete(NEGATION_SIGN))
|
32
|
+
|
33
|
+
if tag_negated?(tag)
|
34
|
+
!tag_matched
|
35
|
+
else
|
36
|
+
tag_matched
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def tag_negated?(tag)
|
42
|
+
tag.start_with? NEGATION_SIGN
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/spinach/version.rb
CHANGED
data/test/spinach/cli_test.rb
CHANGED
@@ -30,6 +30,42 @@ describe Spinach::Cli do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
describe 'tags' do
|
34
|
+
%w{-t --tags}.each do |opt|
|
35
|
+
it 'sets the given tag' do
|
36
|
+
config = Spinach::Config.new
|
37
|
+
Spinach.stubs(:config).returns(config)
|
38
|
+
cli = Spinach::Cli.new([opt,'wip'])
|
39
|
+
cli.options
|
40
|
+
config.tags.must_equal [['wip']]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets OR-ed tags' do
|
44
|
+
config = Spinach::Config.new
|
45
|
+
Spinach.stubs(:config).returns(config)
|
46
|
+
cli = Spinach::Cli.new([opt,'wip,javascript'])
|
47
|
+
cli.options
|
48
|
+
config.tags.must_equal [['wip', 'javascript']]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'adds ~wip by default' do
|
52
|
+
config = Spinach::Config.new
|
53
|
+
Spinach.stubs(:config).returns(config)
|
54
|
+
cli = Spinach::Cli.new([opt,'javascript'])
|
55
|
+
cli.options
|
56
|
+
config.tags.must_equal [['~wip', 'javascript']]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets AND-ed tags' do
|
61
|
+
config = Spinach::Config.new
|
62
|
+
Spinach.stubs(:config).returns(config)
|
63
|
+
cli = Spinach::Cli.new(['-t','javascript', '-t', 'wip'])
|
64
|
+
cli.options
|
65
|
+
config.tags.must_equal [['~wip', 'javascript'],['wip']]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
33
69
|
describe 'generate' do
|
34
70
|
%w{-g --generate}.each do |opt|
|
35
71
|
it 'inits the generator if #{opt}' do
|
data/test/spinach/config_test.rb
CHANGED
@@ -9,7 +9,7 @@ describe Spinach::Config do
|
|
9
9
|
it 'returns a default' do
|
10
10
|
subject[:features_path].must_be_kind_of String
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it 'can be overwritten' do
|
14
14
|
subject[:features_path] = 'test'
|
15
15
|
subject[:features_path].must_equal 'test'
|
@@ -81,4 +81,14 @@ describe Spinach::Config do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
describe '#tags' do
|
85
|
+
it 'returns a default' do
|
86
|
+
subject[:tags].must_be_kind_of Array
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'can be overwritten' do
|
90
|
+
subject[:tags] = ['wip']
|
91
|
+
subject[:tags].must_equal ['wip']
|
92
|
+
end
|
93
|
+
end
|
84
94
|
end
|
@@ -37,8 +37,8 @@ describe Spinach::Runner::FeatureRunner do
|
|
37
37
|
@feature = stub('feature', name: 'Feature')
|
38
38
|
Spinach.stubs(:find_step_definitions).returns(true)
|
39
39
|
@scenarios = [
|
40
|
-
scenario = stub,
|
41
|
-
another_scenario = stub
|
40
|
+
scenario = stub(tags: []),
|
41
|
+
another_scenario = stub(tags: [])
|
42
42
|
]
|
43
43
|
@feature.stubs(:scenarios).returns @scenarios
|
44
44
|
@runner = Spinach::Runner::FeatureRunner.new(@feature)
|
@@ -80,31 +80,61 @@ describe Spinach::Runner::FeatureRunner do
|
|
80
80
|
@feature = stub('feature', name: 'Feature')
|
81
81
|
Spinach.stubs(:find_step_definitions).returns(true)
|
82
82
|
@scenarios = [
|
83
|
-
scenario = stub(line: 4),
|
84
|
-
another_scenario = stub(line: 12)
|
83
|
+
scenario = stub(line: 4, tags: []),
|
84
|
+
another_scenario = stub(line: 12, tags: [])
|
85
85
|
]
|
86
86
|
@feature.stubs(:scenarios).returns @scenarios
|
87
87
|
end
|
88
|
+
|
88
89
|
it "runs exactly matching scenario" do
|
89
90
|
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[1]).returns stub(run: true)
|
90
91
|
@runner = Spinach::Runner::FeatureRunner.new(@feature, "12")
|
91
92
|
@runner.run
|
92
93
|
end
|
94
|
+
|
93
95
|
it "runs no scenario and returns false" do
|
94
96
|
Spinach::Runner::ScenarioRunner.expects(:new).never
|
95
97
|
@runner = Spinach::Runner::FeatureRunner.new(@feature, "3")
|
96
98
|
@runner.run
|
97
99
|
end
|
100
|
+
|
98
101
|
it "runs matching scenario" do
|
99
102
|
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[0]).returns stub(run: true)
|
100
103
|
@runner = Spinach::Runner::FeatureRunner.new(@feature, "8")
|
101
104
|
@runner.run
|
102
105
|
end
|
106
|
+
|
103
107
|
it "runs last scenario" do
|
104
108
|
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[1]).returns stub(run: true)
|
105
109
|
@runner = Spinach::Runner::FeatureRunner.new(@feature, "15")
|
106
110
|
@runner.run
|
107
111
|
end
|
108
112
|
end
|
113
|
+
|
114
|
+
describe "when running for specific tags configured" do
|
115
|
+
|
116
|
+
before do
|
117
|
+
@feature = stub('feature', name: 'Feature')
|
118
|
+
Spinach.stubs(:find_step_definitions).returns(true)
|
119
|
+
@scenario = stub(line: 4, tags: [])
|
120
|
+
@feature.stubs(:scenarios).returns [@scenario]
|
121
|
+
end
|
122
|
+
|
123
|
+
it "runs matching scenario" do
|
124
|
+
Spinach::TagsMatcher.stubs(:match).returns true
|
125
|
+
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenario).returns stub(run: true)
|
126
|
+
|
127
|
+
@runner = Spinach::Runner::FeatureRunner.new(@feature)
|
128
|
+
@runner.run
|
129
|
+
end
|
130
|
+
|
131
|
+
it "skips scenarios that do not match" do
|
132
|
+
Spinach::TagsMatcher.stubs(:match).returns false
|
133
|
+
Spinach::Runner::ScenarioRunner.expects(:new).never
|
134
|
+
|
135
|
+
@runner = Spinach::Runner::FeatureRunner.new(@feature)
|
136
|
+
@runner.run
|
137
|
+
end
|
138
|
+
end
|
109
139
|
end
|
110
140
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe Spinach::TagsMatcher do
|
4
|
+
|
5
|
+
describe '#match' do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@config = Spinach::Config.new
|
9
|
+
Spinach.stubs(:config).returns(@config)
|
10
|
+
end
|
11
|
+
|
12
|
+
subject { Spinach::TagsMatcher }
|
13
|
+
|
14
|
+
describe "when matching against a single tag" do
|
15
|
+
|
16
|
+
before { @config.tags = [['wip']] }
|
17
|
+
|
18
|
+
it "matches the same tag" do
|
19
|
+
subject.match(['wip']).must_equal true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "does not match a different tag" do
|
23
|
+
subject.match(['important']).must_equal false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not match when no tags are present" do
|
27
|
+
subject.match([]).must_equal false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'when matching against a single negated tag' do
|
32
|
+
|
33
|
+
before { @config.tags = [['~wip']] }
|
34
|
+
|
35
|
+
it "returns false for the same tag" do
|
36
|
+
subject.match(['wip']).must_equal false
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns true for a different tag" do
|
40
|
+
subject.match(['important']).must_equal true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns true when no tags are present" do
|
44
|
+
subject.match([]).must_equal true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "when matching against ANDed tags" do
|
49
|
+
|
50
|
+
before { @config.tags = [['wip'], ['important']] }
|
51
|
+
|
52
|
+
it "returns true when all tags match" do
|
53
|
+
subject.match(['wip', 'important']).must_equal true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns false when one tag matches" do
|
57
|
+
subject.match(['important']).must_equal false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns false when no tags match" do
|
61
|
+
subject.match(['foo']).must_equal false
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns false when no tags are present" do
|
65
|
+
subject.match([]).must_equal false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "when matching against ORed tags" do
|
70
|
+
|
71
|
+
before { @config.tags = [['wip', 'important']] }
|
72
|
+
|
73
|
+
it "returns true when all tags match" do
|
74
|
+
subject.match(['wip', 'important']).must_equal true
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns true when one tag matches" do
|
78
|
+
subject.match(['important']).must_equal true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns false when no tags match" do
|
82
|
+
subject.match(['foo']).must_equal false
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns false when no tags are present" do
|
86
|
+
subject.match([]).must_equal false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'when matching against combined ORed and ANDed tags' do
|
91
|
+
|
92
|
+
before { @config.tags = [['billing', 'wip'], ['important']] }
|
93
|
+
|
94
|
+
it "returns true when all tags match" do
|
95
|
+
subject.match(['billing', 'wip', 'important']).must_equal true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "returns true when tags from both AND-groups match" do
|
99
|
+
subject.match(['wip', 'important']).must_equal true
|
100
|
+
subject.match(['billing', 'important']).must_equal true
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns false when tags from one AND-group match" do
|
104
|
+
subject.match(['important']).must_equal false
|
105
|
+
subject.match(['billing']).must_equal false
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns false when no tags match" do
|
109
|
+
subject.match(['foo']).must_equal false
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns false when no tags are present" do
|
113
|
+
subject.match([]).must_equal false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spinach
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2012-
|
15
|
+
date: 2012-04-02 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: gherkin-ruby
|
@@ -293,6 +293,7 @@ files:
|
|
293
293
|
- lib/spinach/scenario.rb
|
294
294
|
- lib/spinach/step.rb
|
295
295
|
- lib/spinach/support.rb
|
296
|
+
- lib/spinach/tags_matcher.rb
|
296
297
|
- lib/spinach/version.rb
|
297
298
|
- spinach.gemspec
|
298
299
|
- test/spinach/background_test.rb
|
@@ -319,6 +320,7 @@ files:
|
|
319
320
|
- test/spinach/scenario_test.rb
|
320
321
|
- test/spinach/step_test.rb
|
321
322
|
- test/spinach/support_test.rb
|
323
|
+
- test/spinach/tags_matcher_test.rb
|
322
324
|
- test/spinach_test.rb
|
323
325
|
- test/test_helper.rb
|
324
326
|
homepage: http://github.com/codegram/spinach
|
@@ -392,6 +394,7 @@ test_files:
|
|
392
394
|
- test/spinach/scenario_test.rb
|
393
395
|
- test/spinach/step_test.rb
|
394
396
|
- test/spinach/support_test.rb
|
397
|
+
- test/spinach/tags_matcher_test.rb
|
395
398
|
- test/spinach_test.rb
|
396
399
|
- test/test_helper.rb
|
397
400
|
has_rdoc:
|