turnip 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  .bundle
3
+ bundler_stubs
3
4
  Gemfile.lock
4
5
  pkg/*
5
6
  .rvmrc
data/.travis.yml CHANGED
@@ -3,4 +3,7 @@ rvm:
3
3
  - 1.9.3
4
4
  - ruby-head
5
5
  - rbx-19mode
6
+ matrix:
7
+ allow_failures:
8
+ - rvm: ruby-head
6
9
 
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Turnip
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/jnicklas/turnip.png)](http://travis-ci.org/jnicklas/turnip)
4
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/jnicklas/turnip)
4
5
 
5
6
  Turnip is a [Gherkin](https://github.com/cucumber/cucumber/wiki/Gherkin)
6
7
  extension for RSpec. It allows you to write tests in Gherkin and run them
@@ -206,6 +207,11 @@ you were to put your steps in `spec/steps`, you could load them like this:
206
207
  Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }
207
208
  ```
208
209
 
210
+ Before loading your `spec_helper`, Turnip also tries to load a file called
211
+ `turnip_helper` where you can setup anything specific to your turnip examples.
212
+ You might find it beneficial to load your steps from this file so that they
213
+ don't have to be loaded when you run your other tests.
214
+
209
215
  ### Calling steps from other steps
210
216
 
211
217
  Since steps are Ruby methods you can call them like other Ruby methods.
@@ -238,36 +244,20 @@ step "the value is the magic number"
238
244
  end
239
245
  ```
240
246
 
241
- ### Calling steps manually
242
-
243
- This is a more esoteric feature of Turnip, of use mostly to people who want to
244
- do crazy stuff. You can use `send` to call any Turnip step, no matter where it
245
- is defined or included. Additionally, the `Turnip::Execute` module has a method
246
- called `step`, this method executes a step, given a string as it might appear
247
- in a feature file. This is the same `step` method you used above to call steps
248
- from within other steps.
247
+ ### Methods as steps
249
248
 
250
- For example:
249
+ You can mark an existing method as a step. This will make it available in your
250
+ Turnip features. For example:
251
251
 
252
252
  ``` ruby
253
- class Monster
254
- include Turnip::Execute
255
-
256
- step("sing a song") { "Arrrghghggh" }
257
- step("eat :count villager(s)") { Villager.eat(count) }
253
+ module MonsterSteps
254
+ def create_monster(name)
255
+ @monster = Monster.new(:name => name)
256
+ end
257
+ step :create_monster, "there is a monster called :name"
258
258
  end
259
-
260
- monster = Monster.new
261
- monster.step("sing a song")
262
- monster.step("eat 1 villager")
263
- monster.step("eat 5 villagers")
264
- monster.send("eat :count villager(s)", 5)
265
259
  ```
266
260
 
267
- Note that in this case `step` from `Turnip::Execute` is an *instance* method,
268
- whereas `step` used to define the step is a *class* method, they are *not* the
269
- same method.
270
-
271
261
  ## Custom step placeholders
272
262
 
273
263
  Do you want to be more specific in what to match in your step placeholders? Do
@@ -0,0 +1,12 @@
1
+ Feature: using scenario outlines
2
+ Scenario Outline: a simple outline
3
+ Given there is a monster with hitpoints:
4
+ | hit_points |
5
+ | <hp> |
6
+ When I attack the monster and do <damage> points damage
7
+ Then the monster should be <state>
8
+
9
+ Examples:
10
+ | hp | damage | state |
11
+ | 10 | 13 | dead |
12
+ | 8 | 5 | alive |
data/lib/turnip.rb CHANGED
@@ -24,7 +24,7 @@ module Turnip
24
24
  end
25
25
  end
26
26
 
27
- Turnip.type = :turnip
27
+ Turnip.type = :feature
28
28
 
29
29
  Module.send(:include, Turnip::Define)
30
30
 
@@ -81,12 +81,22 @@ module Turnip
81
81
  rows.map do |row|
82
82
  Scenario.new(@raw).tap do |scenario|
83
83
  scenario.steps = steps.map do |step|
84
- new_description = step.description.gsub(/<([^>]*)>/) { |_| Hash[headers.zip(row)][$1] }
85
- Step.new(new_description, step.extra_args, step.line)
84
+ new_description = substitute(step.description, headers, row)
85
+ new_extra_args = step.extra_args.map do |ea|
86
+ next ea unless ea.instance_of?(Turnip::Table)
87
+ Turnip::Table.new(ea.map {|t_row| t_row.map {|t_col| substitute(t_col, headers, row) } })
88
+ end
89
+ Step.new(new_description, new_extra_args, step.line)
86
90
  end
87
91
  end
88
92
  end
89
93
  end
94
+
95
+ private
96
+
97
+ def substitute(text, headers, row)
98
+ text.gsub(/<([^>]*)>/) { |_| Hash[headers.zip(row)][$1] }
99
+ end
90
100
  end
91
101
 
92
102
  class Step < Struct.new(:description, :extra_args, :line)
@@ -106,7 +116,7 @@ module Turnip
106
116
  def build(feature_file)
107
117
  Turnip::Builder.new.tap do |builder|
108
118
  parser = Gherkin::Parser::Parser.new(builder, true)
109
- parser.parse(File.read(feature_file), nil, 0)
119
+ parser.parse(File.read(feature_file), feature_file, 0)
110
120
  end
111
121
  end
112
122
  end
@@ -1,7 +1,5 @@
1
1
  require 'capybara/rspec'
2
2
 
3
- Turnip.type = :request
4
-
5
3
  RSpec.configure do |config|
6
4
  config.before do
7
5
  if self.class.include?(Capybara::DSL) and example.metadata[:turnip]
data/lib/turnip/define.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  module Turnip
2
2
  module Define
3
- def step(expression, &block)
4
- step = Turnip::StepDefinition.new(expression, &block)
3
+ def step(method_name=nil, expression, &block)
4
+ if method_name and block
5
+ raise ArgumentError, "can't specify both method name and a block for a step"
6
+ end
7
+ step = Turnip::StepDefinition.new(expression, method_name, caller.first, &block)
5
8
  send(:define_method, "match: #{expression}") { |description| step.match(description) }
6
- send(:define_method, expression, &block)
9
+ send(:define_method, expression, &block) if block
7
10
  end
8
11
  end
9
12
  end
@@ -7,9 +7,17 @@ module Turnip
7
7
  next unless method.to_s.start_with?("match: ")
8
8
  send(method.to_s, description.to_s)
9
9
  end.compact
10
- raise Turnip::Pending, description if matches.length == 0
11
- raise Turnip::Ambiguous, description if matches.length > 1
12
- send(matches.first.expression, *(matches.first.params + extra_args))
10
+
11
+ if matches.length == 0
12
+ raise Turnip::Pending, description
13
+ end
14
+
15
+ if matches.length > 1
16
+ msg = ['Ambiguous step definitions'].concat(matches.map(&:trace)).join("\r\n")
17
+ raise Turnip::Ambiguous, msg
18
+ end
19
+
20
+ send(matches.first.method_name, *(matches.first.params + extra_args))
13
21
  end
14
22
  end
15
23
  end
data/lib/turnip/rspec.rb CHANGED
@@ -12,15 +12,23 @@ module Turnip
12
12
  module Loader
13
13
  def load(*a, &b)
14
14
  if a.first.end_with?('.feature')
15
- begin
16
- require 'spec_helper'
17
- rescue LoadError
18
- end
15
+ require_if_exists 'turnip_helper'
16
+ require_if_exists 'spec_helper'
17
+
19
18
  Turnip::RSpec.run(a.first)
20
19
  else
21
20
  super
22
21
  end
23
22
  end
23
+
24
+ private
25
+
26
+ def require_if_exists(filename)
27
+ require filename
28
+ rescue LoadError => e
29
+ # Don't hide LoadErrors raised in the spec helper.
30
+ raise unless e.message.include?(filename)
31
+ end
24
32
  end
25
33
 
26
34
  ##
@@ -74,7 +82,7 @@ end
74
82
  ::RSpec::Core::Configuration.send(:include, Turnip::RSpec::Loader)
75
83
 
76
84
  ::RSpec.configure do |config|
77
- config.include Turnip::RSpec::Execute
78
- config.include Turnip::Steps
85
+ config.include Turnip::RSpec::Execute, turnip: true
86
+ config.include Turnip::Steps, turnip: true
79
87
  config.pattern << ",**/*.feature"
80
88
  end
@@ -2,12 +2,20 @@ module Turnip
2
2
  class StepDefinition
3
3
  class Match < Struct.new(:step_definition, :params, :block)
4
4
  def expression; step_definition.expression; end
5
+ def method_name; step_definition.method_name; end
6
+ def called_from; step_definition.called_from; end
7
+
8
+ def trace
9
+ trace = %{ - "#{expression}" (#{called_from})}
10
+ end
5
11
  end
6
12
 
7
- attr_reader :expression, :block
13
+ attr_reader :expression, :block, :method_name, :called_from
8
14
 
9
- def initialize(expression, &block)
15
+ def initialize(expression, method_name=nil, called_from=nil, &block)
10
16
  @expression = expression
17
+ @method_name = method_name || expression
18
+ @called_from = called_from
11
19
  @block = block
12
20
  end
13
21
 
@@ -1,3 +1,3 @@
1
1
  module Turnip
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
data/spec/builder_spec.rb CHANGED
@@ -27,4 +27,23 @@ describe Turnip::Builder do
27
27
  ])
28
28
  end
29
29
  end
30
+
31
+ context "with example tables in scenario outlines" do
32
+ let(:feature_file) { File.expand_path('../examples/scenario_outline_table_substitution.feature', File.dirname(__FILE__)) }
33
+ let(:builder) { Turnip::Builder.build(feature_file) }
34
+ let(:feature) { builder.features.first }
35
+
36
+ it "replaces placeholders in tables in steps" do
37
+ feature.scenarios[0].steps.map(&:description).should eq([
38
+ "there is a monster with hitpoints:",
39
+ "I attack the monster and do 13 points damage",
40
+ "the monster should be dead"
41
+ ])
42
+ table = feature.scenarios[0].steps[0].extra_args.find {|a| a.instance_of?(Turnip::Table)}
43
+ table.hashes[0]['hit_points'].should == '10'
44
+ table = feature.scenarios[1].steps[0].extra_args.find {|a| a.instance_of?(Turnip::Table)}
45
+ table.hashes[0]['hit_points'].should == '8'
46
+ end
47
+
48
+ end
30
49
  end
@@ -19,6 +19,22 @@ describe Turnip::Execute do
19
19
  obj.send("a :test step", "cool").should == "COOL"
20
20
  end
21
21
 
22
+ it "can use an existing method as a step" do
23
+ mod.module_eval do
24
+ def a_test_step(test)
25
+ test.upcase
26
+ end
27
+ end
28
+ mod.step(:a_test_step, "a :test step")
29
+ obj.step("a cool step").should == "COOL"
30
+ end
31
+
32
+ it "raises an argument error when both method name and block given" do
33
+ expect do
34
+ mod.step(:a_test_step, "a :test step") { "foo" }
35
+ end.to raise_error(ArgumentError)
36
+ end
37
+
22
38
  it "sends in extra arg from a builder step" do
23
39
  mod.step("a :test step") { |test, foo| test.upcase + foo }
24
40
  obj.step("a cool step", "foo").should == "COOLfoo"
@@ -35,4 +51,20 @@ describe Turnip::Execute do
35
51
  mod.step("a :test step") { |test, foo| test.upcase + foo }
36
52
  obj.step(builder_step).should == "COOLfoo"
37
53
  end
54
+
55
+ it "defines ambiguous steps and run a matching step" do
56
+ mod.step("an ambiguous step") {}
57
+ mod.step("an :ambiguous step") {}
58
+ expect {
59
+ obj.step("an ambiguous step")
60
+ }.to raise_error(Turnip::Ambiguous)
61
+ end
62
+
63
+ it "shows useful information on the ambiguous steps" do
64
+ mod.step("an ambiguous step") {}
65
+ mod.step("an :ambiguous step") {}
66
+ expect {
67
+ obj.step("an ambiguous step")
68
+ }.to raise_error(Turnip::Ambiguous, %r{(ambiguous).*(define_and_execute_spec.rb)})
69
+ end
38
70
  end
@@ -11,7 +11,7 @@ describe 'The CLI', :type => :integration do
11
11
  end
12
12
 
13
13
  it "prints out failures and successes" do
14
- @result.should include('33 examples, 3 failures, 3 pending')
14
+ @result.should include('35 examples, 3 failures, 5 pending')
15
15
  end
16
16
 
17
17
  it "includes features in backtraces" do
@@ -1,3 +1,5 @@
1
+ require "spec_helper"
2
+
1
3
  describe Turnip::StepDefinition do
2
4
  let(:all_steps) { [] }
3
5
 
@@ -31,9 +33,11 @@ describe Turnip::StepDefinition do
31
33
 
32
34
  it "can reuse the same custom placeholder multiple times" do
33
35
  Turnip::Placeholder.stub(:resolve).with(:count).and_return(/\d+/)
36
+ Turnip::Placeholder.stub(:apply).with(:count, "3").and_return(3)
37
+ Turnip::Placeholder.stub(:apply).with(:count, "2").and_return(2)
34
38
  step = Turnip::StepDefinition.new(":count monsters and :count knights") {}
35
39
  match = step.match("3 monsters and 2 knights")
36
- match.params.should eq(["3", "2"])
40
+ match.params.should eq([3, 2])
37
41
  end
38
42
 
39
43
  it "does search for the same custom placeholder several times" do
data/spec/table_spec.rb CHANGED
@@ -8,6 +8,11 @@ describe Turnip::Table do
8
8
  it 'returns the raw table' do
9
9
  table.raw.should == [['foo', 'bar'], ['quox', '42']]
10
10
  end
11
+
12
+ it 'reflects changes in the raw table' do
13
+ table.raw[1][1] = '55'
14
+ table.raw.should == [['foo', 'bar'], ['quox', '55']]
15
+ end
11
16
  end
12
17
 
13
18
  describe '#to_a' do
@@ -71,4 +76,5 @@ describe Turnip::Table do
71
76
  table.map(&:first).should == ['moo', 'quox']
72
77
  end
73
78
  end
79
+
74
80
  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: 1.0.0
4
+ version: 1.1.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: 2012-06-06 00:00:00.000000000 Z
12
+ date: 2012-11-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2153019360 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153019360
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: gherkin
27
- requirement: &2153018640 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '2.5'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *2153018640
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '2.5'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rake
38
- requirement: &2153018120 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,7 +53,12 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *2153018120
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  description: Provides the ability to define steps and run Gherkin files from with
48
63
  RSpec
49
64
  email:
@@ -66,6 +81,7 @@ files:
66
81
  - examples/multiline_string.feature
67
82
  - examples/pending.feature
68
83
  - examples/scenario_outline.feature
84
+ - examples/scenario_outline_table_substitution.feature
69
85
  - examples/simple_feature.feature
70
86
  - examples/step_calling.feature
71
87
  - examples/steps/alignment_steps.rb
@@ -95,7 +111,7 @@ files:
95
111
  - lib/turnip/table.rb
96
112
  - lib/turnip/version.rb
97
113
  - spec/builder_spec.rb
98
- - spec/define_and_execute.rb
114
+ - spec/define_and_execute_spec.rb
99
115
  - spec/dsl_spec.rb
100
116
  - spec/integration_spec.rb
101
117
  - spec/placeholder_spec.rb
@@ -123,13 +139,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
139
  version: '0'
124
140
  requirements: []
125
141
  rubyforge_project: turnip
126
- rubygems_version: 1.8.16
142
+ rubygems_version: 1.8.24
127
143
  signing_key:
128
144
  specification_version: 3
129
145
  summary: Gherkin extension for RSpec
130
146
  test_files:
131
147
  - spec/builder_spec.rb
132
- - spec/define_and_execute.rb
148
+ - spec/define_and_execute_spec.rb
133
149
  - spec/dsl_spec.rb
134
150
  - spec/integration_spec.rb
135
151
  - spec/placeholder_spec.rb