turnip 0.2.0 → 0.3.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.
Files changed (46) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +5 -0
  3. data/README.md +196 -44
  4. data/Rakefile +8 -0
  5. data/examples/alignment_steps.rb +7 -0
  6. data/examples/autoload_steps.feature +5 -0
  7. data/examples/autoload_steps.rb +5 -0
  8. data/examples/dragon_steps.rb +17 -0
  9. data/examples/evil_steps.rb +7 -0
  10. data/examples/knight_steps.rb +29 -0
  11. data/examples/more_steps.rb +7 -0
  12. data/examples/neutral_steps.rb +7 -0
  13. data/examples/red_dragon_steps.rb +18 -0
  14. data/examples/step_calling.feature +14 -0
  15. data/examples/step_calling_steps.rb +23 -0
  16. data/examples/steps.rb +0 -22
  17. data/examples/steps_for_super.feature +16 -0
  18. data/lib/turnip.rb +12 -12
  19. data/lib/turnip/builder.rb +20 -4
  20. data/lib/turnip/config.rb +18 -0
  21. data/lib/turnip/dsl.rb +19 -13
  22. data/lib/turnip/feature_file.rb +20 -0
  23. data/lib/turnip/loader.rb +6 -3
  24. data/lib/turnip/placeholder.rb +1 -1
  25. data/lib/turnip/runner_dsl.rb +9 -0
  26. data/lib/turnip/scenario_context.rb +41 -0
  27. data/lib/turnip/scenario_runner.rb +35 -0
  28. data/lib/turnip/step_definition.rb +14 -30
  29. data/lib/turnip/step_loader.rb +27 -0
  30. data/lib/turnip/step_module.rb +89 -0
  31. data/lib/turnip/table.rb +12 -0
  32. data/lib/turnip/version.rb +1 -1
  33. data/spec/builder_spec.rb +41 -2
  34. data/spec/dsl_spec.rb +22 -34
  35. data/spec/feature_file_spec.rb +18 -0
  36. data/spec/integration_spec.rb +1 -1
  37. data/spec/runner_dsl_spec.rb +23 -0
  38. data/spec/scenario_context_spec.rb +51 -0
  39. data/spec/scenario_runner_spec.rb +79 -0
  40. data/spec/spec_helper.rb +1 -3
  41. data/spec/step_definition_spec.rb +22 -34
  42. data/spec/step_loader_spec.rb +29 -0
  43. data/spec/step_module_spec.rb +106 -0
  44. data/spec/table_spec.rb +8 -1
  45. data/turnip.gemspec +2 -1
  46. metadata +51 -7
data/.gitignore CHANGED
@@ -2,4 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
- .rvmrc
5
+ .rvmrc
6
+ .rbx
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - ruby-head
5
+ - rbx-2.0
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Turnip
2
2
 
3
+ [![Build Status](https://secure.travis-ci.org/jnicklas/turnip.png)](http://travis-ci.org/jnicklas/turnip)
4
+
3
5
  Turnip is a [Gherkin](https://github.com/cucumber/cucumber/wiki/Gherkin)
4
6
  extension for RSpec. It allows you to write tests in Gherkin and run them
5
7
  through your RSpec environment. Basically you can write cucumber features in
@@ -28,6 +30,17 @@ exist), and add the following line:
28
30
  -r turnip
29
31
  ```
30
32
 
33
+ ## Development
34
+
35
+ * Source hosted at [GitHub](http://github.com/jnicklas/turnip).
36
+ * Please direct questions, discussion or problems to the [mailing list](http://groups.google.com/group/ruby-turnip).
37
+ Please do not open an issue on GitHub if you have a question.
38
+ * If you found a reproducible bug, open a [GitHub Issue](http://github.com/jnicklas/turnip/issues) to submit a bug report.
39
+ * Please do not contact any of the maintainers directly, unless you have found a security related issue.
40
+
41
+ Pull requests are very welcome (and even better than bug reports)!
42
+ Please create a topic branch for every separate change you make.
43
+
31
44
  ## Compatibility
32
45
 
33
46
  Turnip does not work on Ruby 1.8.X.
@@ -60,9 +73,33 @@ Yes, that's really it.
60
73
 
61
74
  ## Defining steps
62
75
 
63
- You might want to define some steps. You can put them anywhere. Turnip
64
- automatically requires your `spec_helper`, so you can add them there or put
65
- them in separate files (recommended). Define them like this:
76
+ You might want to define some steps. Just as in cucumber, your step files
77
+ should be named `[something]_steps.rb`. All files ending in `*steps.rb`
78
+ will be automatically required if they are under the Turnip step directory.
79
+
80
+ The default step directory for Turnip is `spec/`. You can override this
81
+ in your `spec_helper` by setting `Turnip::Config.step_dirs`. For example:
82
+
83
+ ``` ruby
84
+ # spec/spec_helper.rb
85
+ RSpec.configure do |config|
86
+ Turnip::Config.step_dirs = 'examples'
87
+ Turnip::StepLoader.load_steps
88
+ end
89
+ ```
90
+
91
+ This would set the Turnip step dirs to `examples/` and automatically load
92
+ all `*steps.rb` files anywhere under that directory.
93
+
94
+ The steps you define in your step files can be global or they can be scoped
95
+ to certain features (or scenarios)...
96
+
97
+ ### Global steps
98
+ Global steps can be used by any feature/scenario you write since they are
99
+ unscoped. The names must be unique across all step files in the global
100
+ namespace.
101
+
102
+ Define them in your step file like this:
66
103
 
67
104
  ``` ruby
68
105
  step "there is a monster" do
@@ -96,89 +133,204 @@ end
96
133
 
97
134
  That will match both "there is X monster" or "there are X monsters".
98
135
 
99
- ## Defining placeholders
136
+ You can also define custom step placeholders. More on that later.
137
+
138
+ ### Scoped steps
139
+ Scoped steps help you to organize steps that are specific to
140
+ certain features or scenarios. They only need to be unique within
141
+ the scopes being used by the running scenario.
100
142
 
101
- But what if you want to be more specific in what to match in those
102
- placeholders, and it is bothersome to have to constantly cast them to the
103
- correct type. Turnip's placeholder solve both problems, like this:
143
+ To define scoped steps use `steps_for`:
104
144
 
105
145
  ``` ruby
106
- step "there are :count monsters" do |count|
107
- count.times { Monster.new(name) }
146
+ steps_for :interface do
147
+ step "I do it" do
148
+ ...
149
+ end
108
150
  end
109
151
 
110
- placeholder :count do
111
- match /\d+/ do |count|
112
- count.to_i
152
+ steps_for :database do
153
+ step "I do it" do
154
+ ...
113
155
  end
156
+ end
157
+ ```
114
158
 
115
- match /no/ do
116
- 0
159
+ Even though the step is named the same, you can now use it in
160
+ your feature files like so:
161
+
162
+ ``` cucumber
163
+ @interface
164
+ Scenario: do it through the interface
165
+
166
+ @database
167
+ Scenario: do it through the database
168
+ ```
169
+
170
+ Note that this would still cause an error if you tagged a Scenario
171
+ with both `@interface` and `@database` at the same time.
172
+
173
+ Scoped steps are really just Ruby modules under the covers so you
174
+ can do anything you'd normally want to do including defining
175
+ helper/utility methods and variables. Check out
176
+ [features/alignment_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/alignment_steps.rb)
177
+ and
178
+ [features/evil_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/evil_steps.rb) for basic examples.
179
+
180
+ ### Reusing steps
181
+ When using scoped steps in Turnip, you can tell it to also include steps
182
+ defined in another `steps_for` block. The syntax for that is `use_steps`:
183
+
184
+ ``` ruby
185
+ # dragon_steps.rb
186
+ steps_for :dragon do
187
+ use_steps :knight
188
+
189
+ attr_accessor :dragon
190
+
191
+ def dragon_attack
192
+ dragon * 10
193
+ end
194
+
195
+ step "there is a dragon" do
196
+ self.dragon = 1
197
+ end
198
+
199
+ step "the dragon attacks the knight" do
200
+ knight.attacked_for(dragon_attack)
201
+ end
202
+ end
203
+
204
+ # red_dragon_steps.rb
205
+ steps_for :red_dragon do
206
+ use_steps :dragon
207
+
208
+ attr_accessor :red_dragon
209
+
210
+ def dragon_attack
211
+ attack = super
212
+ if red_dragon
213
+ attack + 15
214
+ else
215
+ attack
216
+ end
217
+ end
218
+
219
+ step "the dragon breathes fire" do
220
+ self.red_dragon = 1
117
221
  end
118
222
  end
119
223
  ```
120
224
 
121
- You would now be able to use these steps like this:
225
+
226
+ In this example we are making full use of Ruby's modules including using super
227
+ to call the included module's version of `dragon_attack`, for example with the
228
+ following feature file:
122
229
 
123
230
  ``` cucumber
124
- Given there are 4 monsters
125
- Given there are no monsters
231
+ Feature: Red Dragons are deadly
232
+
233
+ @dragon
234
+ Scenario:
235
+ Given there is a dragon
236
+ And there is a knight
237
+ When the dragon attacks the knight
238
+ Then the knight is alive
239
+
240
+ @red_dragon
241
+ Scenario:
242
+ Given there is a dragon
243
+ And the dragon breathes fire
244
+ And there is a knight
245
+ When the dragon attacks the knight
246
+ Then the knight is dead
126
247
  ```
127
248
 
128
- Placeholders can extract matches from the regular expressions as well. For
129
- example:
249
+ ### Auto-included steps
250
+ By default, Turnip will automatically make available any steps defined in
251
+ a `steps_for` block with the same name as the feature file being run. For
252
+ example, given this step file:
130
253
 
131
254
  ``` ruby
132
- placeholder :monster do
133
- match /(blue|green|red) (furry|bald) monster/ do |color, hair|
134
- Monster.new(color, hair)
255
+ # user_signup_steps.rb
256
+ steps_for :user_signup do
257
+ step "I am on the homepage" do
258
+ ...
259
+ end
260
+
261
+ step "I signup with valid info" do
262
+ ...
263
+ end
264
+
265
+ step "I should see a welcome message" do
135
266
  end
136
267
  end
137
268
  ```
138
269
 
139
- These regular expressions must not use anchors, e.g. `^` or `$`. They may not
140
- contain named capture groups, e.g. `(?<color>blue|green)`.
270
+ Then the following feature file would run just fine even though we
271
+ did not explicitly tag it with `@user_signup`.
141
272
 
142
- ## Specific steps
273
+ ``` cucumber
274
+ # user_signup.feature
275
+ Feature: A user can signup
276
+ Scenario: with email address
277
+ Given I am on the homepage
278
+ When I signup with valid info
279
+ Then I should see a welcome message
280
+ ```
281
+
282
+ Note that the `steps_for :user_signup` did not technically have to
283
+ appear in the user_signup_steps.rb file; it could have been located
284
+ in any `steps.rb` file that was autoloaded by Turnip.
285
+
286
+ This feature can be turned off using the `Turnip::Config.autotag_features`
287
+ option if desired.
143
288
 
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:
289
+ ## Custom step placeholders
290
+ Do you want to be more specific in what to match in your step
291
+ placeholders? Do you find it bothersome to have to constantly cast them to the
292
+ correct type? Turnip supports custom placeholders to solve both problems, like this:
146
293
 
147
294
  ``` ruby
148
- step "I do it", :for => :interface do
149
- click_link('Do it')
295
+ step "there are :count monsters" do |count|
296
+ count.times { Monster.new(name) }
150
297
  end
151
298
 
152
- step "I do it", :for => :database do
153
- Do.it!
299
+ placeholder :count do
300
+ match /\d+/ do |count|
301
+ count.to_i
302
+ end
303
+
304
+ match /no/ do
305
+ 0
306
+ end
154
307
  end
155
308
  ```
156
309
 
157
- Not you can use tags in your feature files to decide which step is going to get
158
- run:
310
+ You would now be able to use these steps like this:
159
311
 
160
312
  ``` cucumber
161
- @interface
162
- Scenario: do it through the interface
163
-
164
- @database
165
- Scenario: do it through the database
313
+ Given there are 4 monsters
314
+ Given there are no monsters
166
315
  ```
167
316
 
168
- If you have many steps for the same tag, you can use the `steps_for` helper:
317
+ Placeholders can extract matches from the regular expressions as well. For
318
+ example:
169
319
 
170
320
  ``` ruby
171
- steps_for :interface do
172
- step "I do it" do
173
- ...
321
+ placeholder :monster do
322
+ match /(blue|green|red) (furry|bald) monster/ do |color, hair|
323
+ Monster.new(color, hair)
174
324
  end
175
325
  end
176
326
  ```
177
327
 
328
+ These regular expressions must not use anchors, e.g. `^` or `$`. They may not
329
+ contain named capture groups, e.g. `(?<color>blue|green)`.
330
+
178
331
  ## Using with Capybara
179
332
 
180
- Just require `turnip/capybara`, either in your `spec_helper` or by
181
- adding `-r turnip/capybara` to your `.rspec` file. You can now use the
333
+ Just require `turnip/capybara` in your `spec_helper`. You can now use the
182
334
  same tags you'd use in Cucumber to switch between drivers e.g.
183
335
  `@javascript` or `@selenium`. Your Turnip features will also be run
184
336
  with the `:type => :request` metadata, so that Capybara is included and
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run all examples"
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.rspec_opts = %w[--color]
7
+ end
8
+
9
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ steps_for :alignment do
2
+ attr_accessor :alignment
3
+
4
+ step "that alignment should be :alignment" do |expected_alignment|
5
+ alignment.should eq(expected_alignment)
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ Feature: Auto-loaded steps
2
+
3
+ @scenario_tag
4
+ Scenario:
5
+ Given an auto-loaded step is available
@@ -0,0 +1,5 @@
1
+ steps_for :autoload_steps do
2
+ step 'an auto-loaded step is available' do
3
+ true.should be
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ steps_for :dragon do
2
+ use_steps :knight
3
+
4
+ attr_accessor :dragon
5
+
6
+ def dragon_attack
7
+ dragon * 10
8
+ end
9
+
10
+ step "there is a dragon" do
11
+ self.dragon = 1
12
+ end
13
+
14
+ step "the dragon attacks the knight" do
15
+ knight.attacked_for(dragon_attack)
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ steps_for :evil do
2
+ use_steps :alignment
3
+
4
+ step "the monster has an alignment" do
5
+ self.alignment = 'Evil'
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ steps_for :knight do
2
+ attr_accessor :knight
3
+
4
+ class Knight
5
+ def initialize
6
+ @hp = 20
7
+ end
8
+
9
+ def alive?
10
+ @hp > 0
11
+ end
12
+
13
+ def attacked_for(amount)
14
+ @hp -= amount
15
+ end
16
+ end
17
+
18
+ step "there is a knight" do
19
+ self.knight = Knight.new
20
+ end
21
+
22
+ step "the knight is alive" do
23
+ knight.should be_alive
24
+ end
25
+
26
+ step "the knight is dead" do
27
+ knight.should_not be_alive
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ step "there are :count monkeys with :color hair" do |count, color|
2
+ @monkeys = Array.new(count) { color }
3
+ end
4
+
5
+ step "there should be 3 monkeys with blue hair" do
6
+ @monkeys.should == [:blue, :blue, :blue]
7
+ end
@@ -0,0 +1,7 @@
1
+ steps_for :neutral do
2
+ use_steps :alignment
3
+
4
+ step "the monster has an alignment" do
5
+ self.alignment = 'Neutral'
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ steps_for :red_dragon do
2
+ use_steps :dragon
3
+
4
+ attr_accessor :red_dragon
5
+
6
+ def dragon_attack
7
+ attack = super
8
+ if red_dragon
9
+ attack + 15
10
+ else
11
+ attack
12
+ end
13
+ end
14
+
15
+ step "the dragon breathes fire" do
16
+ self.red_dragon = 1
17
+ end
18
+ end