spinach 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -3
- data/README.markdown +46 -3
- data/lib/spinach/cli.rb +1 -1
- data/lib/spinach/config.rb +2 -0
- data/lib/spinach/feature.rb +2 -1
- data/lib/spinach/generators/feature_generator.rb +2 -0
- data/lib/spinach/hooks.rb +2 -2
- data/lib/spinach/parser/visitor.rb +8 -3
- data/lib/spinach/runner/feature_runner.rb +10 -1
- data/lib/spinach/version.rb +1 -1
- data/spinach.gemspec +2 -2
- data/test/spinach/cli_test.rb +11 -0
- data/test/spinach/hooks_test.rb +13 -1
- data/test/spinach/parser/visitor_test.rb +16 -2
- data/test/spinach/runner/feature_runner_test.rb +4 -4
- data/test/support/filesystem.rb +1 -1
- metadata +34 -83
data/.travis.yml
CHANGED
data/README.markdown
CHANGED
@@ -136,9 +136,52 @@ use private methods, mix in modules or whatever!
|
|
136
136
|
|
137
137
|
Then run your feature again running `spinach` and watch it all turn green! :)
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
## Tags
|
140
|
+
|
141
|
+
Feature and Scenarios can be marked with tags in the form: `@tag`. Tags can be
|
142
|
+
used for different purposes:
|
143
|
+
|
144
|
+
- applying some actions using hooks (eg: `@javascript`, `@transaction`, `@vcr`)
|
145
|
+
|
146
|
+
# When using Capybara, you can switch the driver to use another one with
|
147
|
+
# javascript capabilities (Selenium, Poltergeist, capybara-webkit, ...)
|
148
|
+
#
|
149
|
+
# Spinach already integrates with Capybara if you add
|
150
|
+
# `require spinach/capybara` in `features/support/env.rb`.
|
151
|
+
#
|
152
|
+
# This example is extracted from this integration.
|
153
|
+
Spinach.hooks.on_tag("javascript") do
|
154
|
+
::Capybara.current_driver = ::Capybara.javascript_driver
|
155
|
+
end
|
156
|
+
|
157
|
+
- filtering (eg: `@module-a`, `@customer`, `@admin`, `@bug-12`, `@feat-1`)
|
158
|
+
|
159
|
+
# Given a feature file with this content
|
160
|
+
|
161
|
+
@feat-1
|
162
|
+
Feature: So something great
|
163
|
+
|
164
|
+
Scenario: Make it possible
|
165
|
+
|
166
|
+
@bug-12
|
167
|
+
Scenario: Ensure no regression on this
|
168
|
+
|
169
|
+
Then you can run all Scenarios in your suite related to `@feat-1` using:
|
170
|
+
|
171
|
+
spinach --tags @feat-1
|
172
|
+
|
173
|
+
Or only Scenarios related to `@feat-1` and `@bug-12` using:
|
174
|
+
|
175
|
+
spinach --tags @feat-1,@bug-12
|
176
|
+
|
177
|
+
Or only Scenarios related to `@feat-1` excluding `@bug-12` using:
|
178
|
+
|
179
|
+
spinach --tags @feat-1,~@bug-12
|
180
|
+
|
181
|
+
By default Spinach will ignore Scenarios marked with the tag `@wip` or whose
|
182
|
+
Feature is marked with the tag `@wip`. Those are meant to be work in progress,
|
183
|
+
scenarios that are pending while you work on them. To explicitly run those, use
|
184
|
+
the `--tags` option:
|
142
185
|
|
143
186
|
spinach --tags @wip
|
144
187
|
|
data/lib/spinach/cli.rb
CHANGED
data/lib/spinach/config.rb
CHANGED
data/lib/spinach/feature.rb
CHANGED
data/lib/spinach/hooks.rb
CHANGED
@@ -147,10 +147,10 @@ module Spinach
|
|
147
147
|
# # change capybara driver
|
148
148
|
# end
|
149
149
|
def on_tag(tag)
|
150
|
-
before_scenario do |scenario|
|
150
|
+
before_scenario do |scenario, step_definitions|
|
151
151
|
tags = scenario.tags
|
152
152
|
next unless tags.any?
|
153
|
-
yield(scenario) if tags.include? tag.to_s
|
153
|
+
yield(scenario, step_definitions) if tags.include? tag.to_s
|
154
154
|
end
|
155
155
|
end
|
156
156
|
end
|
@@ -38,6 +38,11 @@ module Spinach
|
|
38
38
|
def visit_Feature(node)
|
39
39
|
@feature.name = node.name
|
40
40
|
node.background.accept(self) if node.background
|
41
|
+
|
42
|
+
@current_tag_set = @feature
|
43
|
+
node.tags.each { |tag| tag.accept(self) }
|
44
|
+
@current_tag_set = nil
|
45
|
+
|
41
46
|
node.scenarios.each { |scenario| scenario.accept(self) }
|
42
47
|
end
|
43
48
|
|
@@ -69,9 +74,9 @@ module Spinach
|
|
69
74
|
scenario.name = node.name
|
70
75
|
scenario.line = node.line
|
71
76
|
|
72
|
-
@
|
77
|
+
@current_tag_set = scenario
|
73
78
|
node.tags.each { |tag| tag.accept(self) }
|
74
|
-
@
|
79
|
+
@current_tag_set = nil
|
75
80
|
|
76
81
|
@current_step_set = scenario
|
77
82
|
node.steps.each { |step| step.accept(self) }
|
@@ -87,7 +92,7 @@ module Spinach
|
|
87
92
|
#
|
88
93
|
# @api public
|
89
94
|
def visit_Tag(node)
|
90
|
-
@
|
95
|
+
@current_tag_set.tags << node.name
|
91
96
|
end
|
92
97
|
|
93
98
|
# Adds the step to the current scenario.
|
@@ -55,6 +55,14 @@ module Spinach
|
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
+
def feature_tags
|
59
|
+
if @feature.respond_to?(:tags)
|
60
|
+
@feature.tags
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
58
66
|
def run_scenarios!
|
59
67
|
scenarios.each_with_index do |scenario, current_scenario_index|
|
60
68
|
if run_scenario?(scenario, current_scenario_index)
|
@@ -65,7 +73,8 @@ module Spinach
|
|
65
73
|
end
|
66
74
|
|
67
75
|
def run_scenario?(scenario, current_scenario_index)
|
68
|
-
match_line(current_scenario_index) &&
|
76
|
+
match_line(current_scenario_index) &&
|
77
|
+
TagsMatcher.match(feature_tags | scenario.tags)
|
69
78
|
end
|
70
79
|
|
71
80
|
def match_line(current_scenario_index)
|
data/lib/spinach/version.rb
CHANGED
data/spinach.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.summary = %q{Spinach is a BDD framework on top of gherkin}
|
10
10
|
gem.homepage = "http://github.com/codegram/spinach"
|
11
11
|
|
12
|
-
gem.add_runtime_dependency 'gherkin-ruby', '~> 0.
|
12
|
+
gem.add_runtime_dependency 'gherkin-ruby', '~> 0.2.0'
|
13
13
|
gem.add_runtime_dependency 'colorize'
|
14
14
|
gem.add_development_dependency 'rake'
|
15
15
|
gem.add_development_dependency 'mocha'
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.add_development_dependency 'pry'
|
19
19
|
gem.add_development_dependency 'simplecov'
|
20
20
|
gem.add_development_dependency 'rspec'
|
21
|
-
gem.add_development_dependency 'minitest'
|
21
|
+
gem.add_development_dependency 'minitest'
|
22
22
|
gem.add_development_dependency 'turn'
|
23
23
|
|
24
24
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
data/test/spinach/cli_test.rb
CHANGED
@@ -145,6 +145,17 @@ describe Spinach::Cli do
|
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
148
|
+
describe 'when a particular feature list is passed with line' do
|
149
|
+
it 'runs the feature' do
|
150
|
+
cli = Spinach::Cli.new(['features/some_feature.feature:10'])
|
151
|
+
File.expects(:file?).with('features/some_feature.feature').returns(true)
|
152
|
+
|
153
|
+
Spinach::Runner.expects(:new).with(['features/some_feature.feature:10']).
|
154
|
+
returns(stub(:run))
|
155
|
+
cli.run
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
148
159
|
describe 'when no feature is passed' do
|
149
160
|
it 'runs the feature' do
|
150
161
|
cli = Spinach::Cli.new([])
|
data/test/spinach/hooks_test.rb
CHANGED
@@ -31,16 +31,28 @@ describe Spinach::Hooks do
|
|
31
31
|
let(:scenario) do
|
32
32
|
stub(tags: ['javascript', 'capture'])
|
33
33
|
end
|
34
|
+
let(:step_definitions) do
|
35
|
+
stub(something: "step_definitions")
|
36
|
+
end
|
34
37
|
|
35
38
|
it "calls the block if the scenario includes the tag" do
|
36
39
|
assertion = false
|
37
40
|
subject.on_tag('javascript') do
|
38
41
|
assertion = true
|
39
42
|
end
|
40
|
-
subject.run_before_scenario(scenario)
|
43
|
+
subject.run_before_scenario(scenario, step_definitions)
|
41
44
|
assertion.must_equal true
|
42
45
|
end
|
43
46
|
|
47
|
+
it "passes in the step_definitions" do
|
48
|
+
assertion = false
|
49
|
+
subject.on_tag('javascript') do |scenario, step_definitions|
|
50
|
+
assertion = step_definitions.something
|
51
|
+
end
|
52
|
+
subject.run_before_scenario(scenario, step_definitions)
|
53
|
+
assertion.must_equal "step_definitions"
|
54
|
+
end
|
55
|
+
|
44
56
|
it "doesn't call the block if the scenario doesn't include the tag" do
|
45
57
|
assertion = false
|
46
58
|
subject.on_tag('screenshot') do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative '../../test_helper'
|
2
2
|
|
3
3
|
module Spinach
|
4
4
|
class Parser
|
@@ -24,8 +24,14 @@ module Spinach
|
|
24
24
|
describe '#visit_Feature' do
|
25
25
|
before do
|
26
26
|
@background = stub_everything
|
27
|
+
@tags = [stub_everything, stub_everything, stub_everything]
|
27
28
|
@scenarios = [stub_everything, stub_everything, stub_everything]
|
28
|
-
@node = stub(
|
29
|
+
@node = stub(
|
30
|
+
scenarios: @scenarios,
|
31
|
+
name: 'Go shopping',
|
32
|
+
background: @background,
|
33
|
+
tags: @tags
|
34
|
+
)
|
29
35
|
end
|
30
36
|
|
31
37
|
it 'sets the name' do
|
@@ -33,6 +39,13 @@ module Spinach
|
|
33
39
|
visitor.feature.name.must_equal 'Go shopping'
|
34
40
|
end
|
35
41
|
|
42
|
+
it 'sets the tags' do
|
43
|
+
@tags.each do |step|
|
44
|
+
step.expects(:accept).with visitor
|
45
|
+
end
|
46
|
+
visitor.visit_Feature(@node)
|
47
|
+
end
|
48
|
+
|
36
49
|
it 'iterates over its children' do
|
37
50
|
@scenarios.each do |scenario|
|
38
51
|
scenario.expects(:accept).with visitor
|
@@ -121,6 +134,7 @@ module Spinach
|
|
121
134
|
tags = ['tag1', 'tag2', 'tag3']
|
122
135
|
scenario = stub(tags: tags)
|
123
136
|
visitor.instance_variable_set(:@current_scenario, scenario)
|
137
|
+
visitor.instance_variable_set(:@current_tag_set, scenario)
|
124
138
|
|
125
139
|
visitor.visit_Tag(stub(name: 'tag4'))
|
126
140
|
scenario.tags.must_equal ['tag1', 'tag2', 'tag3', 'tag4']
|
@@ -114,14 +114,14 @@ describe Spinach::Runner::FeatureRunner do
|
|
114
114
|
describe "when running for specific tags configured" do
|
115
115
|
|
116
116
|
before do
|
117
|
-
@feature = stub('feature', name: 'Feature')
|
117
|
+
@feature = stub('feature', name: 'Feature', tags: ["feature_tag"])
|
118
118
|
Spinach.stubs(:find_step_definitions).returns(true)
|
119
|
-
@scenario = stub(line: 4, tags: [])
|
119
|
+
@scenario = stub(line: 4, tags: ["scenario_tag"])
|
120
120
|
@feature.stubs(:scenarios).returns [@scenario]
|
121
121
|
end
|
122
122
|
|
123
123
|
it "runs matching scenario" do
|
124
|
-
Spinach::TagsMatcher.
|
124
|
+
Spinach::TagsMatcher.expects(:match).with(["feature_tag", "scenario_tag"]).returns true
|
125
125
|
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenario).returns stub(run: true)
|
126
126
|
|
127
127
|
@runner = Spinach::Runner::FeatureRunner.new(@feature)
|
@@ -129,7 +129,7 @@ describe Spinach::Runner::FeatureRunner do
|
|
129
129
|
end
|
130
130
|
|
131
131
|
it "skips scenarios that do not match" do
|
132
|
-
Spinach::TagsMatcher.
|
132
|
+
Spinach::TagsMatcher.expects(:match).with(["feature_tag", "scenario_tag"]).returns false
|
133
133
|
Spinach::Runner::ScenarioRunner.expects(:new).never
|
134
134
|
|
135
135
|
@runner = Spinach::Runner::FeatureRunner.new(@feature)
|
data/test/support/filesystem.rb
CHANGED
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.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,27 +12,22 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2012-
|
15
|
+
date: 2012-05-24 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: gherkin-ruby
|
19
|
-
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirement: &2168662800 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
22
|
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: 0.
|
24
|
+
version: 0.2.0
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
|
-
version_requirements:
|
28
|
-
none: false
|
29
|
-
requirements:
|
30
|
-
- - ~>
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 0.1.0
|
27
|
+
version_requirements: *2168662800
|
33
28
|
- !ruby/object:Gem::Dependency
|
34
29
|
name: colorize
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirement: &2168662100 !ruby/object:Gem::Requirement
|
36
31
|
none: false
|
37
32
|
requirements:
|
38
33
|
- - ! '>='
|
@@ -40,15 +35,10 @@ dependencies:
|
|
40
35
|
version: '0'
|
41
36
|
type: :runtime
|
42
37
|
prerelease: false
|
43
|
-
version_requirements:
|
44
|
-
none: false
|
45
|
-
requirements:
|
46
|
-
- - ! '>='
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
38
|
+
version_requirements: *2168662100
|
49
39
|
- !ruby/object:Gem::Dependency
|
50
40
|
name: rake
|
51
|
-
requirement: !ruby/object:Gem::Requirement
|
41
|
+
requirement: &2168661120 !ruby/object:Gem::Requirement
|
52
42
|
none: false
|
53
43
|
requirements:
|
54
44
|
- - ! '>='
|
@@ -56,15 +46,10 @@ dependencies:
|
|
56
46
|
version: '0'
|
57
47
|
type: :development
|
58
48
|
prerelease: false
|
59
|
-
version_requirements:
|
60
|
-
none: false
|
61
|
-
requirements:
|
62
|
-
- - ! '>='
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '0'
|
49
|
+
version_requirements: *2168661120
|
65
50
|
- !ruby/object:Gem::Dependency
|
66
51
|
name: mocha
|
67
|
-
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirement: &2168660500 !ruby/object:Gem::Requirement
|
68
53
|
none: false
|
69
54
|
requirements:
|
70
55
|
- - ! '>='
|
@@ -72,15 +57,10 @@ dependencies:
|
|
72
57
|
version: '0'
|
73
58
|
type: :development
|
74
59
|
prerelease: false
|
75
|
-
version_requirements:
|
76
|
-
none: false
|
77
|
-
requirements:
|
78
|
-
- - ! '>='
|
79
|
-
- !ruby/object:Gem::Version
|
80
|
-
version: '0'
|
60
|
+
version_requirements: *2168660500
|
81
61
|
- !ruby/object:Gem::Dependency
|
82
62
|
name: sinatra
|
83
|
-
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirement: &2168687040 !ruby/object:Gem::Requirement
|
84
64
|
none: false
|
85
65
|
requirements:
|
86
66
|
- - ! '>='
|
@@ -88,15 +68,10 @@ dependencies:
|
|
88
68
|
version: '0'
|
89
69
|
type: :development
|
90
70
|
prerelease: false
|
91
|
-
version_requirements:
|
92
|
-
none: false
|
93
|
-
requirements:
|
94
|
-
- - ! '>='
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
71
|
+
version_requirements: *2168687040
|
97
72
|
- !ruby/object:Gem::Dependency
|
98
73
|
name: capybara
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirement: &2168686600 !ruby/object:Gem::Requirement
|
100
75
|
none: false
|
101
76
|
requirements:
|
102
77
|
- - ! '>='
|
@@ -104,15 +79,10 @@ dependencies:
|
|
104
79
|
version: '0'
|
105
80
|
type: :development
|
106
81
|
prerelease: false
|
107
|
-
version_requirements:
|
108
|
-
none: false
|
109
|
-
requirements:
|
110
|
-
- - ! '>='
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
version: '0'
|
82
|
+
version_requirements: *2168686600
|
113
83
|
- !ruby/object:Gem::Dependency
|
114
84
|
name: pry
|
115
|
-
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirement: &2168686180 !ruby/object:Gem::Requirement
|
116
86
|
none: false
|
117
87
|
requirements:
|
118
88
|
- - ! '>='
|
@@ -120,15 +90,10 @@ dependencies:
|
|
120
90
|
version: '0'
|
121
91
|
type: :development
|
122
92
|
prerelease: false
|
123
|
-
version_requirements:
|
124
|
-
none: false
|
125
|
-
requirements:
|
126
|
-
- - ! '>='
|
127
|
-
- !ruby/object:Gem::Version
|
128
|
-
version: '0'
|
93
|
+
version_requirements: *2168686180
|
129
94
|
- !ruby/object:Gem::Dependency
|
130
95
|
name: simplecov
|
131
|
-
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirement: &2168685760 !ruby/object:Gem::Requirement
|
132
97
|
none: false
|
133
98
|
requirements:
|
134
99
|
- - ! '>='
|
@@ -136,15 +101,10 @@ dependencies:
|
|
136
101
|
version: '0'
|
137
102
|
type: :development
|
138
103
|
prerelease: false
|
139
|
-
version_requirements:
|
140
|
-
none: false
|
141
|
-
requirements:
|
142
|
-
- - ! '>='
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '0'
|
104
|
+
version_requirements: *2168685760
|
145
105
|
- !ruby/object:Gem::Dependency
|
146
106
|
name: rspec
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirement: &2168685340 !ruby/object:Gem::Requirement
|
148
108
|
none: false
|
149
109
|
requirements:
|
150
110
|
- - ! '>='
|
@@ -152,31 +112,21 @@ dependencies:
|
|
152
112
|
version: '0'
|
153
113
|
type: :development
|
154
114
|
prerelease: false
|
155
|
-
version_requirements:
|
156
|
-
none: false
|
157
|
-
requirements:
|
158
|
-
- - ! '>='
|
159
|
-
- !ruby/object:Gem::Version
|
160
|
-
version: '0'
|
115
|
+
version_requirements: *2168685340
|
161
116
|
- !ruby/object:Gem::Dependency
|
162
117
|
name: minitest
|
163
|
-
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirement: &2168684920 !ruby/object:Gem::Requirement
|
164
119
|
none: false
|
165
120
|
requirements:
|
166
|
-
- -
|
121
|
+
- - ! '>='
|
167
122
|
- !ruby/object:Gem::Version
|
168
|
-
version: '
|
123
|
+
version: '0'
|
169
124
|
type: :development
|
170
125
|
prerelease: false
|
171
|
-
version_requirements:
|
172
|
-
none: false
|
173
|
-
requirements:
|
174
|
-
- - ~>
|
175
|
-
- !ruby/object:Gem::Version
|
176
|
-
version: '2.0'
|
126
|
+
version_requirements: *2168684920
|
177
127
|
- !ruby/object:Gem::Dependency
|
178
128
|
name: turn
|
179
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirement: &2168684500 !ruby/object:Gem::Requirement
|
180
130
|
none: false
|
181
131
|
requirements:
|
182
132
|
- - ! '>='
|
@@ -184,12 +134,7 @@ dependencies:
|
|
184
134
|
version: '0'
|
185
135
|
type: :development
|
186
136
|
prerelease: false
|
187
|
-
version_requirements:
|
188
|
-
none: false
|
189
|
-
requirements:
|
190
|
-
- - ! '>='
|
191
|
-
- !ruby/object:Gem::Version
|
192
|
-
version: '0'
|
137
|
+
version_requirements: *2168684500
|
193
138
|
description: Spinach is a BDD framework on top of gherkin
|
194
139
|
email:
|
195
140
|
- info@codegram.com
|
@@ -304,15 +249,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
304
249
|
- - ! '>='
|
305
250
|
- !ruby/object:Gem::Version
|
306
251
|
version: '0'
|
252
|
+
segments:
|
253
|
+
- 0
|
254
|
+
hash: 2039979269723833359
|
307
255
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
308
256
|
none: false
|
309
257
|
requirements:
|
310
258
|
- - ! '>='
|
311
259
|
- !ruby/object:Gem::Version
|
312
260
|
version: '0'
|
261
|
+
segments:
|
262
|
+
- 0
|
263
|
+
hash: 2039979269723833359
|
313
264
|
requirements: []
|
314
265
|
rubyforge_project:
|
315
|
-
rubygems_version: 1.8.
|
266
|
+
rubygems_version: 1.8.15
|
316
267
|
signing_key:
|
317
268
|
specification_version: 3
|
318
269
|
summary: Spinach is a BDD framework on top of gherkin
|