turnip 1.0.0 → 1.1.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 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