turnip 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +46 -6
- data/{spec/fixture → examples}/ambiguous.feature +0 -0
- data/{spec/fixture → examples}/backgrounds.feature +0 -0
- data/{spec/fixture → examples}/interpolation.feature +0 -0
- data/examples/multiline_string.feature +8 -0
- data/{spec/fixture → examples}/pending.feature +0 -0
- data/examples/scenario_outline.feature +10 -0
- data/{spec/fixture → examples}/simple_feature.feature +0 -0
- data/examples/steps.rb +102 -0
- data/examples/steps_for.feature +10 -0
- data/examples/table.feature +8 -0
- data/{spec/fixture → examples}/tags.feature +0 -0
- data/examples/with_comments.feature +12 -0
- data/lib/turnip.rb +35 -6
- data/lib/turnip/builder.rb +46 -4
- data/lib/turnip/dsl.rb +15 -4
- data/lib/turnip/step_definition.rb +45 -22
- data/lib/turnip/table.rb +28 -0
- data/lib/turnip/version.rb +1 -1
- data/spec/builder_spec.rb +30 -0
- data/spec/dsl_spec.rb +54 -0
- data/spec/integration_spec.rb +16 -0
- data/spec/spec_helper.rb +3 -51
- data/spec/step_definition_spec.rb +118 -0
- data/spec/table_spec.rb +48 -0
- metadata +30 -21
- data/lib/turnip/rspec.rb +0 -5
- data/lib/turnip/run.rb +0 -27
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -5,12 +5,6 @@ extension for RSpec. It allows you to write tests in Gherkin and run them
|
|
5
5
|
through your RSpec environment. Basically you can write cucumber features in
|
6
6
|
RSpec.
|
7
7
|
|
8
|
-
## DISCLAIMER, READ THIS!!!
|
9
|
-
|
10
|
-
Turnip is a proof of concept, there are currently VERY FEW TESTS, and there is
|
11
|
-
a lot of cucumber's syntax it does NOT support. There are currently no tables,
|
12
|
-
multiline string or scenario outlines.
|
13
|
-
|
14
8
|
## Installation
|
15
9
|
|
16
10
|
Install the gem
|
@@ -92,6 +86,16 @@ Given there is a monster called Jonas
|
|
92
86
|
And there is a monster called "Jonas Nicklas"
|
93
87
|
```
|
94
88
|
|
89
|
+
You can also specify alternative words and optional parts of words, like this:
|
90
|
+
|
91
|
+
``` ruby
|
92
|
+
step "there is/are :count monster(s)" do |count|
|
93
|
+
@monsters = Array.new(count) { Monster.new }
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
That will match both "there is X monster" or "there are X monsters".
|
98
|
+
|
95
99
|
## Defining placeholders
|
96
100
|
|
97
101
|
But what if you want to be more specific in what to match in those
|
@@ -135,6 +139,42 @@ end
|
|
135
139
|
These regular expressions must not use anchors, e.g. `^` or `$`. They may not
|
136
140
|
contain named capture groups, e.g. `(?<color>blue|green)`.
|
137
141
|
|
142
|
+
## Specific steps
|
143
|
+
|
144
|
+
Sometimes you might want to limit where steps are matched. Turnip allows you to
|
145
|
+
do this, through the `:for` option on your steps:
|
146
|
+
|
147
|
+
``` ruby
|
148
|
+
step "I do it", :for => :interface do
|
149
|
+
click_link('Do it')
|
150
|
+
end
|
151
|
+
|
152
|
+
step "I do it", :for => :database do
|
153
|
+
Do.it!
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
Not you can use tags in your feature files to decide which step is going to get
|
158
|
+
run:
|
159
|
+
|
160
|
+
``` cucumber
|
161
|
+
@interface
|
162
|
+
Scenario: do it through the interface
|
163
|
+
|
164
|
+
@database
|
165
|
+
Scenario: do it through the database
|
166
|
+
```
|
167
|
+
|
168
|
+
If you have many steps for the same tag, you can use the `steps_for` helper:
|
169
|
+
|
170
|
+
``` ruby
|
171
|
+
steps_for :interface do
|
172
|
+
step "I do it" do
|
173
|
+
...
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
138
178
|
## Using with Capybara
|
139
179
|
|
140
180
|
Just require `turnip/capybara`, either in your `spec_helper` or by
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: using scenario outlines
|
2
|
+
Scenario Outline: a simple outline
|
3
|
+
Given there is a monster with <hp> hitpoints
|
4
|
+
When I attack the monster and do <damage> points damage
|
5
|
+
Then the monster should be <state>
|
6
|
+
|
7
|
+
Examples:
|
8
|
+
| hp | damage | state |
|
9
|
+
| 10 | 13 | dead |
|
10
|
+
| 8 | 5 | alive |
|
File without changes
|
data/examples/steps.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
step "there is a monster" do
|
2
|
+
@monster = 1
|
3
|
+
end
|
4
|
+
|
5
|
+
step "there is a strong monster" do
|
6
|
+
@monster = 2
|
7
|
+
end
|
8
|
+
|
9
|
+
step "I attack it" do
|
10
|
+
@monster -= 1
|
11
|
+
end
|
12
|
+
|
13
|
+
step "it should die" do
|
14
|
+
@monster.should eq(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
step "this is ambiguous" do
|
18
|
+
end
|
19
|
+
|
20
|
+
step "this is ambiguous" do
|
21
|
+
end
|
22
|
+
|
23
|
+
step "there is a monster called :name" do |name|
|
24
|
+
@monster_name = name
|
25
|
+
end
|
26
|
+
|
27
|
+
step 'it should be called "John Smith"' do
|
28
|
+
@monster_name.should == "John Smith"
|
29
|
+
end
|
30
|
+
|
31
|
+
step 'it should be called "John"' do
|
32
|
+
@monster_name.should == "John"
|
33
|
+
end
|
34
|
+
|
35
|
+
step "there are :count monkeys with :color hair" do |count, color|
|
36
|
+
@monkeys = Array.new(count) { color }
|
37
|
+
end
|
38
|
+
|
39
|
+
step "there should be 3 monkeys with blue hair" do
|
40
|
+
@monkeys.should == [:blue, :blue, :blue]
|
41
|
+
end
|
42
|
+
|
43
|
+
step "there is a monster with :count hitpoints" do |count|
|
44
|
+
@monster = count
|
45
|
+
end
|
46
|
+
|
47
|
+
step "I attack the monster and do :count points damage" do |count|
|
48
|
+
@monster -= count
|
49
|
+
end
|
50
|
+
|
51
|
+
step "the monster should be alive" do
|
52
|
+
@monster.should > 0
|
53
|
+
end
|
54
|
+
|
55
|
+
step "the monster should be dead" do
|
56
|
+
@monster.should <= 0
|
57
|
+
end
|
58
|
+
|
59
|
+
step "there are the following monsters:" do |table|
|
60
|
+
@monsters = {}
|
61
|
+
table.hashes.each do |hash|
|
62
|
+
@monsters[hash['Name']] = hash['Hitpoints'].to_i
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
step ":name should have :count hitpoints" do |name, count|
|
67
|
+
@monsters[name].should eq(count.to_i)
|
68
|
+
end
|
69
|
+
|
70
|
+
step "the monster sings the following song" do |song|
|
71
|
+
@song = song
|
72
|
+
end
|
73
|
+
|
74
|
+
step "the song should have :count lines" do |count|
|
75
|
+
@song.to_s.split("\n").length.should eq(count)
|
76
|
+
end
|
77
|
+
|
78
|
+
step "the monster has an alignment", :for => :evil do
|
79
|
+
@alignment = 'Evil'
|
80
|
+
end
|
81
|
+
|
82
|
+
steps_for :neutral do
|
83
|
+
step "the monster has an alignment" do
|
84
|
+
@alignment = 'Neutral'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
step "that alignment should be :alignment" do |alignment|
|
89
|
+
@alignment.should eq(alignment)
|
90
|
+
end
|
91
|
+
|
92
|
+
placeholder :count do
|
93
|
+
match /\d+/ do |count|
|
94
|
+
count.to_i
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
placeholder :color do
|
99
|
+
match /blue|green|red/ do |color|
|
100
|
+
color.to_sym
|
101
|
+
end
|
102
|
+
end
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Hi.
|
2
|
+
Feature: some feature
|
3
|
+
Comments can be (almost) everywhere.
|
4
|
+
# And another comment
|
5
|
+
|
6
|
+
Scenario: some scenario
|
7
|
+
# Yet another one.
|
8
|
+
Given there is a monster
|
9
|
+
When I attack it
|
10
|
+
Then it should die
|
11
|
+
# Oh my, comments are everywhere.
|
12
|
+
# Thank you for reading.
|
data/lib/turnip.rb
CHANGED
@@ -2,18 +2,47 @@ require "gherkin"
|
|
2
2
|
require "gherkin/formatter/tag_count_formatter"
|
3
3
|
|
4
4
|
require "turnip/version"
|
5
|
-
require "turnip/loader"
|
6
|
-
require "turnip/builder"
|
7
|
-
require "turnip/run"
|
8
|
-
require "turnip/step_definition"
|
9
|
-
require "turnip/placeholder"
|
10
5
|
require "turnip/dsl"
|
11
|
-
require "turnip/rspec"
|
12
6
|
|
13
7
|
module Turnip
|
8
|
+
autoload :Loader, 'turnip/loader'
|
9
|
+
autoload :Builder, 'turnip/builder'
|
10
|
+
autoload :StepDefinition, 'turnip/step_definition'
|
11
|
+
autoload :Placeholder, 'turnip/placeholder'
|
12
|
+
autoload :Table, 'turnip/table'
|
13
|
+
|
14
14
|
class << self
|
15
15
|
attr_accessor :type
|
16
|
+
|
17
|
+
def run(content)
|
18
|
+
Turnip::Builder.build(content).features.each do |feature|
|
19
|
+
describe feature.name, feature.metadata_hash do
|
20
|
+
feature.backgrounds.each do |background|
|
21
|
+
before do
|
22
|
+
background.steps.each do |step|
|
23
|
+
Turnip::StepDefinition.execute(self, step)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
feature.scenarios.each do |scenario|
|
28
|
+
it scenario.name, scenario.metadata_hash do
|
29
|
+
scenario.steps.each do |step|
|
30
|
+
Turnip::StepDefinition.execute(self, step)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
16
37
|
end
|
17
38
|
end
|
18
39
|
|
19
40
|
Turnip.type = :turnip
|
41
|
+
|
42
|
+
RSpec::Core::Configuration.send(:include, Turnip::Loader)
|
43
|
+
|
44
|
+
RSpec.configure do |config|
|
45
|
+
config.pattern << ",**/*.feature"
|
46
|
+
end
|
47
|
+
|
48
|
+
self.extend Turnip::DSL
|
data/lib/turnip/builder.rb
CHANGED
@@ -49,7 +49,7 @@ module Turnip
|
|
49
49
|
include Tags
|
50
50
|
include Name
|
51
51
|
|
52
|
-
|
52
|
+
attr_accessor :steps
|
53
53
|
|
54
54
|
def initialize(raw)
|
55
55
|
@raw = raw
|
@@ -57,16 +57,46 @@ module Turnip
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
class
|
60
|
+
class ScenarioOutline
|
61
|
+
include Tags
|
61
62
|
include Name
|
62
63
|
|
64
|
+
attr_reader :steps
|
65
|
+
|
63
66
|
def initialize(raw)
|
64
67
|
@raw = raw
|
68
|
+
@steps = []
|
65
69
|
end
|
70
|
+
|
71
|
+
def to_scenarios(examples)
|
72
|
+
rows = examples.rows.map(&:cells)
|
73
|
+
headers = rows.shift
|
74
|
+
rows.map do |row|
|
75
|
+
Scenario.new(@raw).tap do |scenario|
|
76
|
+
scenario.steps = steps.map do |step|
|
77
|
+
new_description = step.description.gsub(/<([^>]*)>/) { |_| Hash[headers.zip(row)][$1] }
|
78
|
+
Step.new(new_description, step.extra_arg)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Step < Struct.new(:description, :extra_arg)
|
66
86
|
end
|
67
87
|
|
68
88
|
attr_reader :features
|
69
89
|
|
90
|
+
class << self
|
91
|
+
def build(content)
|
92
|
+
Turnip::Builder.new.tap do |builder|
|
93
|
+
formatter = Gherkin::Formatter::TagCountFormatter.new(builder, {})
|
94
|
+
parser = Gherkin::Parser::Parser.new(formatter, true, "root", false)
|
95
|
+
parser.parse(content, nil, 0)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
70
100
|
def initialize
|
71
101
|
@features = []
|
72
102
|
end
|
@@ -86,9 +116,21 @@ module Turnip
|
|
86
116
|
@current_feature.scenarios << @current_step_context
|
87
117
|
end
|
88
118
|
|
119
|
+
def scenario_outline(outline)
|
120
|
+
@current_step_context = ScenarioOutline.new(outline)
|
121
|
+
end
|
122
|
+
|
123
|
+
def examples(examples)
|
124
|
+
@current_feature.scenarios.push(*@current_step_context.to_scenarios(examples))
|
125
|
+
end
|
126
|
+
|
89
127
|
def step(step)
|
90
|
-
|
91
|
-
|
128
|
+
if step.doc_string
|
129
|
+
extra_arg = step.doc_string.value
|
130
|
+
elsif step.rows
|
131
|
+
extra_arg = Turnip::Table.new(step.rows.map { |row| row.cells(&:value) })
|
132
|
+
end
|
133
|
+
@current_step_context.steps << Step.new(step.name, extra_arg)
|
92
134
|
end
|
93
135
|
|
94
136
|
def eof
|
data/lib/turnip/dsl.rb
CHANGED
@@ -1,7 +1,20 @@
|
|
1
1
|
module Turnip
|
2
2
|
module DSL
|
3
|
-
|
4
|
-
|
3
|
+
class << self
|
4
|
+
attr_accessor :current_taggings
|
5
|
+
end
|
6
|
+
|
7
|
+
def step(description, options={}, &block)
|
8
|
+
if Turnip::DSL.current_taggings
|
9
|
+
options[:for] = [options[:for], *Turnip::DSL.current_taggings].compact.flatten
|
10
|
+
end
|
11
|
+
Turnip::StepDefinition.add(description, options, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def steps_for(*taggings)
|
15
|
+
Turnip::DSL.current_taggings = [taggings, *Turnip::DSL.current_taggings].compact.flatten
|
16
|
+
yield
|
17
|
+
Turnip::DSL.current_taggings = nil
|
5
18
|
end
|
6
19
|
|
7
20
|
def placeholder(name, &block)
|
@@ -9,5 +22,3 @@ module Turnip
|
|
9
22
|
end
|
10
23
|
end
|
11
24
|
end
|
12
|
-
|
13
|
-
self.extend Turnip::DSL
|
@@ -1,62 +1,85 @@
|
|
1
1
|
module Turnip
|
2
2
|
class StepDefinition
|
3
|
-
class Match < Struct.new(:params, :block)
|
3
|
+
class Match < Struct.new(:step_definition, :params, :block)
|
4
|
+
def expression; step_definition.expression; end
|
5
|
+
def options; step_definition.options; end
|
6
|
+
end
|
7
|
+
|
4
8
|
class Pending < StandardError; end
|
5
9
|
class Ambiguous < StandardError; end
|
6
10
|
|
7
|
-
attr_reader :expression, :block
|
11
|
+
attr_reader :expression, :block, :options
|
8
12
|
|
9
13
|
class << self
|
10
|
-
def execute(context,
|
11
|
-
match = find(description)
|
12
|
-
|
14
|
+
def execute(context, step)
|
15
|
+
match = find(step.description, context.example.metadata)
|
16
|
+
params = match.params
|
17
|
+
params << step.extra_arg if step.extra_arg
|
18
|
+
context.instance_exec(*params, &match.block)
|
13
19
|
rescue Pending
|
14
|
-
context.pending "the step '#{description}' is not implemented"
|
20
|
+
context.pending "the step '#{step.description}' is not implemented"
|
15
21
|
end
|
16
22
|
|
17
|
-
def add(expression, &block)
|
18
|
-
|
23
|
+
def add(expression, options={}, &block)
|
24
|
+
all << StepDefinition.new(expression, options, &block)
|
19
25
|
end
|
20
26
|
|
21
|
-
def find(description)
|
22
|
-
found =
|
23
|
-
step.match(description)
|
27
|
+
def find(description, metadata={})
|
28
|
+
found = all.map do |step|
|
29
|
+
step.match(description, metadata)
|
24
30
|
end.compact
|
25
31
|
raise Pending, description if found.length == 0
|
26
32
|
raise Ambiguous, description if found.length > 1
|
27
33
|
found[0]
|
28
34
|
end
|
29
35
|
|
30
|
-
def
|
31
|
-
@
|
36
|
+
def all
|
37
|
+
@all ||= []
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
|
-
def initialize(expression, &block)
|
41
|
+
def initialize(expression, options={}, &block)
|
36
42
|
@expression = expression
|
37
43
|
@block = block
|
44
|
+
@options = options
|
38
45
|
end
|
39
46
|
|
40
47
|
def regexp
|
41
48
|
@regexp ||= compile_regexp
|
42
49
|
end
|
43
50
|
|
44
|
-
def match(description)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
def match(description, metadata={})
|
52
|
+
if matches_metadata?(metadata)
|
53
|
+
result = description.match(regexp)
|
54
|
+
if result
|
55
|
+
params = result.captures
|
56
|
+
result.names.each_with_index do |name, index|
|
57
|
+
params[index] = Turnip::Placeholder.apply(name.to_sym, params[index])
|
58
|
+
end
|
59
|
+
Match.new(self, params, block)
|
50
60
|
end
|
51
|
-
Match.new(params, block)
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
55
64
|
protected
|
56
65
|
|
66
|
+
def matches_metadata?(metadata)
|
67
|
+
not options[:for] or [options[:for]].flatten.any? { |option| metadata.has_key?(option) }
|
68
|
+
end
|
69
|
+
|
70
|
+
OPTIONAL_WORD_REGEXP = /(\\\s)?\\\(([^)]+)\\\)(\\\s)?/
|
71
|
+
PLACEHOLDER_REGEXP = /:([\w]+)/
|
72
|
+
ALTERNATIVE_WORD_REGEXP = /(\w+)((\/\w+)+)/
|
73
|
+
|
57
74
|
def compile_regexp
|
58
75
|
regexp = Regexp.escape(expression)
|
59
|
-
regexp
|
76
|
+
regexp.gsub!(OPTIONAL_WORD_REGEXP) do |_|
|
77
|
+
[$1, $2, $3].compact.map { |m| "(#{m})?" }.join
|
78
|
+
end
|
79
|
+
regexp.gsub!(ALTERNATIVE_WORD_REGEXP) do |_|
|
80
|
+
"(#{$1}#{$2.tr('/', '|')})"
|
81
|
+
end
|
82
|
+
regexp.gsub!(PLACEHOLDER_REGEXP) do |_|
|
60
83
|
"(?<#{$1}>#{Placeholder.resolve($1.to_sym)})"
|
61
84
|
end
|
62
85
|
Regexp.new("^#{regexp}$")
|
data/lib/turnip/table.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Turnip
|
2
|
+
class Table
|
3
|
+
attr_reader :raw
|
4
|
+
alias_method :to_a, :raw
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(raw)
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def headers
|
13
|
+
@raw.first
|
14
|
+
end
|
15
|
+
|
16
|
+
def rows
|
17
|
+
@raw.drop(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def hashes
|
21
|
+
rows.map { |row| Hash[headers.zip(row)] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def each
|
25
|
+
@raw.each { |row| yield(row) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/turnip/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnip::Builder do
|
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) }
|
7
|
+
let(:feature) { builder.features.first }
|
8
|
+
|
9
|
+
|
10
|
+
it "extracts scenarios" do
|
11
|
+
feature.scenarios.map(&:name).should eq([
|
12
|
+
'a simple outline',
|
13
|
+
'a simple outline'
|
14
|
+
])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "replaces placeholders in steps" do
|
18
|
+
feature.scenarios[0].steps.map(&:description).should eq([
|
19
|
+
"there is a monster with 10 hitpoints",
|
20
|
+
"I attack the monster and do 13 points damage",
|
21
|
+
"the monster should be dead"
|
22
|
+
])
|
23
|
+
feature.scenarios[1].steps.map(&:description).should eq([
|
24
|
+
"there is a monster with 8 hitpoints",
|
25
|
+
"I attack the monster and do 5 points damage",
|
26
|
+
"the monster should be alive"
|
27
|
+
])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnip::DSL do
|
4
|
+
before do
|
5
|
+
@context = stub
|
6
|
+
@context.extend(Turnip::DSL)
|
7
|
+
end
|
8
|
+
|
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
|
14
|
+
|
15
|
+
it 'sends through options' do
|
16
|
+
Turnip::StepDefinition.should_receive(:add).with('foo', {:for => [:monkey]})
|
17
|
+
@context.step 'foo', :for => [:monkey]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
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]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
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
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
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'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The CLI', :type => :integration do
|
4
|
+
before do
|
5
|
+
@result = %x(rspec -fs examples/*.feature)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "shows the correct description" do
|
9
|
+
@result.should include('A simple feature')
|
10
|
+
@result.should include('is a simple feature')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "prints out failures and successes" do
|
14
|
+
@result.should include('16 examples, 1 failure, 1 pending')
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,53 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
step "there is a strong monster" do
|
6
|
-
@monster = 2
|
7
|
-
end
|
8
|
-
|
9
|
-
step "I attack it" do
|
10
|
-
@monster -= 1
|
11
|
-
end
|
12
|
-
|
13
|
-
step "it should die" do
|
14
|
-
@monster.should eq(0)
|
15
|
-
end
|
16
|
-
|
17
|
-
step "this is ambiguous" do
|
18
|
-
end
|
19
|
-
|
20
|
-
step "this is ambiguous" do
|
21
|
-
end
|
22
|
-
|
23
|
-
step "there is a monster called :name" do |name|
|
24
|
-
@monster_name = name
|
25
|
-
end
|
26
|
-
|
27
|
-
step 'it should be called "John Smith"' do
|
28
|
-
@monster_name.should == "John Smith"
|
29
|
-
end
|
30
|
-
|
31
|
-
step 'it should be called "John"' do
|
32
|
-
@monster_name.should == "John"
|
33
|
-
end
|
34
|
-
|
35
|
-
step "there are :count monkeys with :color hair" do |count, color|
|
36
|
-
@monkeys = Array.new(count) { color }
|
37
|
-
end
|
38
|
-
|
39
|
-
step "there should be 3 monkeys with blue hair" do
|
40
|
-
@monkeys.should == [:blue, :blue, :blue]
|
41
|
-
end
|
42
|
-
|
43
|
-
placeholder :count do
|
44
|
-
match /\d+/ do |count|
|
45
|
-
count.to_i
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
placeholder :color do
|
50
|
-
match /blue|green|red/ do |color|
|
51
|
-
color.to_sym
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before(:each, :turnip => true) do
|
3
|
+
require File.expand_path('../examples/steps', File.dirname(__FILE__))
|
52
4
|
end
|
53
5
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
describe Turnip::StepDefinition do
|
2
|
+
after do
|
3
|
+
Turnip::StepDefinition.all.clear
|
4
|
+
end
|
5
|
+
|
6
|
+
describe ".add" do
|
7
|
+
it "adds a step definition to the list of definitions" do
|
8
|
+
Turnip::StepDefinition.add "this is a test"
|
9
|
+
Turnip::StepDefinition.all.first.expression.should eq("this is a test")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".find" do
|
14
|
+
it "returns a step definition that matches the description" do
|
15
|
+
Turnip::StepDefinition.add "there are :count monsters"
|
16
|
+
Turnip::StepDefinition.find("there are 23 monsters").expression.should eq("there are :count monsters")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "respects the for option" do
|
20
|
+
Turnip::StepDefinition.add "alignment", :for => [:evil]
|
21
|
+
Turnip::StepDefinition.add "alignment", :for => [:good]
|
22
|
+
Turnip::StepDefinition.find("alignment", :evil => true, :bad => true).options[:for].should == [:evil]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises an error if the match is ambiguous" do
|
26
|
+
Turnip::StepDefinition.add "there are :count monsters"
|
27
|
+
Turnip::StepDefinition.add "there are 23 monsters"
|
28
|
+
expect { Turnip::StepDefinition.find("there are 23 monsters") }.to raise_error(Turnip::StepDefinition::Ambiguous)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "raises an error if there is no match" do
|
32
|
+
expect { Turnip::StepDefinition.find("there are 23 monsters") }.to raise_error(Turnip::StepDefinition::Pending)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".execute" do
|
37
|
+
it "executes a step in the given context" do
|
38
|
+
context = stub(:example => stub(:metadata => {}))
|
39
|
+
Turnip::StepDefinition.add("there are :count monsters") { @testing = 123 }
|
40
|
+
Turnip::StepDefinition.execute(context, stub(:description => "there are 23 monsters", :extra_arg => nil))
|
41
|
+
context.instance_variable_get(:@testing).should == 123
|
42
|
+
end
|
43
|
+
|
44
|
+
it "tells the context that the step is pending" do
|
45
|
+
context = stub(:example => stub(:metadata => {}))
|
46
|
+
context.should_receive(:pending).with("the step 'there are 23 monsters' is not implemented")
|
47
|
+
Turnip::StepDefinition.execute(context, stub(:description => "there are 23 monsters", :extra_arg => nil))
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sends along arguments" do
|
51
|
+
context = stub(:example => stub(:metadata => {}))
|
52
|
+
Turnip::StepDefinition.add("there are :count monsters") { |count| @testing = count.to_i }
|
53
|
+
Turnip::StepDefinition.execute(context, stub(:description => "there are 23 monsters", :extra_arg => nil))
|
54
|
+
context.instance_variable_get(:@testing).should == 23
|
55
|
+
end
|
56
|
+
|
57
|
+
it "sends along extra arguments" do
|
58
|
+
context = stub(:example => stub(:metadata => {}))
|
59
|
+
Turnip::StepDefinition.add("there are :count monsters") { |count, extra| @testing = extra }
|
60
|
+
Turnip::StepDefinition.execute(context, stub(:description => "there are 23 monsters", :extra_arg => 'foo'))
|
61
|
+
context.instance_variable_get(:@testing).should == 'foo'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#match" do
|
66
|
+
it "matches a simple step" do
|
67
|
+
step = Turnip::StepDefinition.new("there are monsters") {}
|
68
|
+
step.should match("there are monsters")
|
69
|
+
step.should_not match("there are monsters around")
|
70
|
+
step.should_not match("there are people")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "matches placeholders" do
|
74
|
+
Turnip::Placeholder.stub(:resolve).with(:count).and_return(/\d+/)
|
75
|
+
step = Turnip::StepDefinition.new("there are :count monsters") {}
|
76
|
+
step.should match("there are 4 monsters")
|
77
|
+
step.should match("there are 324 monsters")
|
78
|
+
step.should_not match("there are no monsters")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "matches alternative words" do
|
82
|
+
step = Turnip::StepDefinition.new("there is/are monsters") {}
|
83
|
+
step.should match("there are monsters")
|
84
|
+
step.should match("there is monsters")
|
85
|
+
step.should_not match("there be monsters")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "matches several alternative words" do
|
89
|
+
step = Turnip::StepDefinition.new("monsters are cool/nice/scary") {}
|
90
|
+
step.should match("monsters are cool")
|
91
|
+
step.should match("monsters are nice")
|
92
|
+
step.should match("monsters are scary")
|
93
|
+
step.should_not match("monsters are sexy")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "matches optional parts of words" do
|
97
|
+
step = Turnip::StepDefinition.new("there is/are X monster(s)") {}
|
98
|
+
step.should match("there is X monster")
|
99
|
+
step.should_not match("there is X monsterQ")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "matches optional words" do
|
103
|
+
step = Turnip::StepDefinition.new("there is a (scary) monster") {}
|
104
|
+
step.should match("there is a monster")
|
105
|
+
step.should match("there is a scary monster")
|
106
|
+
step.should_not match("there is a terrifying monster")
|
107
|
+
|
108
|
+
step = Turnip::StepDefinition.new("there is a monster (that is scary)") {}
|
109
|
+
step.should match("there is a monster that is scary")
|
110
|
+
step.should match("there is a monster")
|
111
|
+
|
112
|
+
step = Turnip::StepDefinition.new("(there is) a monster") {}
|
113
|
+
step.should match("there is a monster")
|
114
|
+
step.should match("a monster")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
data/spec/table_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnip::Table do
|
4
|
+
describe '#raw' do
|
5
|
+
it 'returns the raw table' do
|
6
|
+
table = Turnip::Table.new([['foo', 'bar'], ['quox', '42']])
|
7
|
+
table.raw.should == [['foo', 'bar'], ['quox', '42']]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#to_a' do
|
12
|
+
it 'returns the raw table' do
|
13
|
+
table = Turnip::Table.new([['foo', 'bar'], ['quox', '42']])
|
14
|
+
table.to_a.should == [['foo', 'bar'], ['quox', '42']]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#headers' do
|
19
|
+
it 'returns the first row' do
|
20
|
+
table = Turnip::Table.new([['foo', 'bar'], ['quox', '42']])
|
21
|
+
table.headers.should == ['foo', 'bar']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#rows' do
|
26
|
+
it 'returns the rows beyond the first' do
|
27
|
+
table = Turnip::Table.new([['foo', 'bar'], ['moo', '55'], ['quox', '42']])
|
28
|
+
table.rows.should == [['moo', '55'], ['quox', '42']]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#hashes' do
|
33
|
+
it 'returns a list of hashe based on the headers' do
|
34
|
+
table = Turnip::Table.new([['foo', 'bar'], ['moo', '55'], ['quox', '42']])
|
35
|
+
table.hashes.should == [
|
36
|
+
{'foo' => 'moo', 'bar' => '55'},
|
37
|
+
{'foo' => 'quox', 'bar' => '42'}
|
38
|
+
]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#map' do
|
43
|
+
it 'iterates over the raw table' do
|
44
|
+
table = Turnip::Table.new([['moo', '55'], ['quox', '42']])
|
45
|
+
table.map(&:first).should == ['moo', 'quox']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turnip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152642360 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152642360
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: gherkin
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152641940 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152641940
|
36
36
|
description: Provides the ability to define steps and run Gherkin files from with
|
37
37
|
RSpec
|
38
38
|
email:
|
@@ -46,24 +46,34 @@ files:
|
|
46
46
|
- Gemfile
|
47
47
|
- README.md
|
48
48
|
- Rakefile
|
49
|
+
- examples/ambiguous.feature
|
50
|
+
- examples/backgrounds.feature
|
51
|
+
- examples/interpolation.feature
|
52
|
+
- examples/multiline_string.feature
|
53
|
+
- examples/pending.feature
|
54
|
+
- examples/scenario_outline.feature
|
55
|
+
- examples/simple_feature.feature
|
56
|
+
- examples/steps.rb
|
57
|
+
- examples/steps_for.feature
|
58
|
+
- examples/table.feature
|
59
|
+
- examples/tags.feature
|
60
|
+
- examples/with_comments.feature
|
49
61
|
- lib/turnip.rb
|
50
62
|
- lib/turnip/builder.rb
|
51
63
|
- lib/turnip/capybara.rb
|
52
64
|
- lib/turnip/dsl.rb
|
53
65
|
- lib/turnip/loader.rb
|
54
66
|
- lib/turnip/placeholder.rb
|
55
|
-
- lib/turnip/rspec.rb
|
56
|
-
- lib/turnip/run.rb
|
57
67
|
- lib/turnip/step_definition.rb
|
68
|
+
- lib/turnip/table.rb
|
58
69
|
- lib/turnip/version.rb
|
59
|
-
- spec/
|
60
|
-
- spec/
|
61
|
-
- spec/
|
62
|
-
- spec/fixture/pending.feature
|
63
|
-
- spec/fixture/simple_feature.feature
|
64
|
-
- spec/fixture/tags.feature
|
70
|
+
- spec/builder_spec.rb
|
71
|
+
- spec/dsl_spec.rb
|
72
|
+
- spec/integration_spec.rb
|
65
73
|
- spec/placeholder_spec.rb
|
66
74
|
- spec/spec_helper.rb
|
75
|
+
- spec/step_definition_spec.rb
|
76
|
+
- spec/table_spec.rb
|
67
77
|
- turnip.gemspec
|
68
78
|
homepage: ''
|
69
79
|
licenses: []
|
@@ -85,16 +95,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
95
|
version: '0'
|
86
96
|
requirements: []
|
87
97
|
rubyforge_project: turnip
|
88
|
-
rubygems_version: 1.8.
|
98
|
+
rubygems_version: 1.8.10
|
89
99
|
signing_key:
|
90
100
|
specification_version: 3
|
91
101
|
summary: Gherkin extension for RSpec
|
92
102
|
test_files:
|
93
|
-
- spec/
|
94
|
-
- spec/
|
95
|
-
- spec/
|
96
|
-
- spec/fixture/pending.feature
|
97
|
-
- spec/fixture/simple_feature.feature
|
98
|
-
- spec/fixture/tags.feature
|
103
|
+
- spec/builder_spec.rb
|
104
|
+
- spec/dsl_spec.rb
|
105
|
+
- spec/integration_spec.rb
|
99
106
|
- spec/placeholder_spec.rb
|
100
107
|
- spec/spec_helper.rb
|
108
|
+
- spec/step_definition_spec.rb
|
109
|
+
- spec/table_spec.rb
|
data/lib/turnip/rspec.rb
DELETED
data/lib/turnip/run.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Turnip
|
2
|
-
def self.run(content)
|
3
|
-
builder = Turnip::Builder.new
|
4
|
-
formatter = Gherkin::Formatter::TagCountFormatter.new(builder, {})
|
5
|
-
parser = Gherkin::Parser::Parser.new(formatter, true, "root", false)
|
6
|
-
parser.parse(content, nil, 0)
|
7
|
-
|
8
|
-
builder.features.each do |feature|
|
9
|
-
describe feature.name, feature.metadata_hash do
|
10
|
-
feature.backgrounds.each do |background|
|
11
|
-
before do
|
12
|
-
background.steps.each do |step|
|
13
|
-
Turnip::StepDefinition.execute(self, step.name)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
feature.scenarios.each do |scenario|
|
18
|
-
it scenario.name, scenario.metadata_hash do
|
19
|
-
scenario.steps.each do |step|
|
20
|
-
Turnip::StepDefinition.execute(self, step.name)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|