turnip 0.3.1 → 1.0.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 (49) hide show
  1. data/README.md +128 -166
  2. data/examples/autoload_steps.feature +3 -2
  3. data/examples/errors.feature +8 -0
  4. data/examples/step_calling.feature +2 -0
  5. data/examples/steps/alignment_steps.rb +23 -0
  6. data/examples/{autoload_steps.rb → steps/autoload_steps.rb} +0 -0
  7. data/examples/steps/backtick_steps.rb +10 -0
  8. data/examples/steps/dragon_steps.rb +41 -0
  9. data/examples/{knight_steps.rb → steps/knight_steps.rb} +3 -1
  10. data/examples/{more_steps.rb → steps/more_steps.rb} +0 -0
  11. data/examples/{step_calling_steps.rb → steps/step_calling_steps.rb} +0 -0
  12. data/examples/{steps.rb → steps/steps.rb} +25 -1
  13. data/examples/steps_for.feature +2 -2
  14. data/examples/steps_with_variations.feature +17 -0
  15. data/lib/turnip.rb +19 -34
  16. data/lib/turnip/builder.rb +26 -24
  17. data/lib/turnip/define.rb +9 -0
  18. data/lib/turnip/dsl.rb +11 -17
  19. data/lib/turnip/execute.rb +15 -0
  20. data/lib/turnip/rspec.rb +80 -0
  21. data/lib/turnip/step_definition.rb +7 -32
  22. data/lib/turnip/version.rb +1 -1
  23. data/spec/builder_spec.rb +1 -40
  24. data/spec/define_and_execute.rb +38 -0
  25. data/spec/dsl_spec.rb +36 -19
  26. data/spec/integration_spec.rb +5 -1
  27. data/spec/spec_helper.rb +1 -3
  28. data/spec/step_definition_spec.rb +37 -51
  29. metadata +25 -55
  30. data/examples/alignment_steps.rb +0 -7
  31. data/examples/backtick_steps.rb +0 -4
  32. data/examples/dragon_steps.rb +0 -17
  33. data/examples/evil_steps.rb +0 -7
  34. data/examples/neutral_steps.rb +0 -7
  35. data/examples/red_dragon_steps.rb +0 -18
  36. data/lib/turnip/config.rb +0 -18
  37. data/lib/turnip/feature_file.rb +0 -20
  38. data/lib/turnip/loader.rb +0 -16
  39. data/lib/turnip/runner_dsl.rb +0 -9
  40. data/lib/turnip/scenario_context.rb +0 -41
  41. data/lib/turnip/scenario_runner.rb +0 -35
  42. data/lib/turnip/step_loader.rb +0 -27
  43. data/lib/turnip/step_module.rb +0 -89
  44. data/spec/feature_file_spec.rb +0 -18
  45. data/spec/runner_dsl_spec.rb +0 -23
  46. data/spec/scenario_context_spec.rb +0 -51
  47. data/spec/scenario_runner_spec.rb +0 -79
  48. data/spec/step_loader_spec.rb +0 -29
  49. data/spec/step_module_spec.rb +0 -106
data/README.md CHANGED
@@ -27,7 +27,7 @@ Now edit the `.rspec` file in your project directory (create it if doesn't
27
27
  exist), and add the following line:
28
28
 
29
29
  ```
30
- -r turnip
30
+ -r turnip/rspec
31
31
  ```
32
32
 
33
33
  ## Development
@@ -73,33 +73,31 @@ Yes, that's really it.
73
73
 
74
74
  ## Defining steps
75
75
 
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:
76
+ You can define steps on any module:
82
77
 
83
78
  ``` ruby
84
- # spec/spec_helper.rb
85
- RSpec.configure do |config|
86
- Turnip::Config.step_dirs = 'examples'
87
- Turnip::StepLoader.load_steps
79
+ module MonsterSteps
80
+ step "there is a monster" do
81
+ @monster = Monster.new
82
+ end
88
83
  end
89
84
  ```
90
85
 
91
- This would set the Turnip step dirs to `examples/` and automatically load
92
- all `*steps.rb` files anywhere under that directory.
86
+ You can now include this module in RSpec:
93
87
 
94
- The steps you define in your step files can be global or they can be scoped
95
- to certain features (or scenarios)...
88
+ ``` ruby
89
+ RSpec.configure { |c| c.include MonsterSteps }
90
+ ```
91
+
92
+ Steps are implemented as regular Ruby methods under the hood, so you can
93
+ use Ruby's normal inheritance chain to mix and match steps.
96
94
 
97
95
  ### 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
96
 
102
- Define them in your step file like this:
97
+ Turnip has a special module called `Turnip::Steps`, which is automatically
98
+ included in RSpec. If you add steps to this module, they are available in all
99
+ your features. As a convenience, there is a shortcut to doing this, just call
100
+ `step` in the global namespace like this:
103
101
 
104
102
  ``` ruby
105
103
  step "there is a monster" do
@@ -107,6 +105,8 @@ step "there is a monster" do
107
105
  end
108
106
  ```
109
107
 
108
+ ### Placeholders
109
+
110
110
  Note that unlike Cucumber, Turnip does not support regexps in step definitions.
111
111
  You can however use placeholders in your step definitions, like this:
112
112
 
@@ -133,31 +133,40 @@ end
133
133
 
134
134
  That will match both "there is X monster" or "there are X monsters".
135
135
 
136
- You can also define custom step placeholders. More on that later.
136
+ You can also define custom step placeholders. More on that later.
137
137
 
138
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.
142
139
 
143
- To define scoped steps use `steps_for`:
140
+ Since steps are defined on modules, you can pick and choose which of them are
141
+ available in which feature. This can be extremely useful if you have a large
142
+ number of steps, and do not want them to potentially conflict.
143
+
144
+ If you had some scenarios which talk to the database directly, and some which
145
+ go through a user interface, you could implement it as follows:
144
146
 
145
147
  ``` ruby
146
- steps_for :interface do
148
+ module InterfaceSteps
147
149
  step "I do it" do
148
150
  ...
149
151
  end
150
152
  end
151
153
 
152
- steps_for :database do
154
+ module DatabaseSteps
153
155
  step "I do it" do
154
156
  ...
155
157
  end
156
158
  end
159
+
160
+ RSpec.configure do |config|
161
+ config.include InterfaceSteps, :interface => true
162
+ config.include DatabaseSteps, :database => true
163
+ end
157
164
  ```
158
165
 
159
- Even though the step is named the same, you can now use it in
160
- your feature files like so:
166
+ Turnip turns tags into RSpec metadata, so you can use RSpec's conditional
167
+ include feature to include these steps only for those scenarios tagged the
168
+ appropriate way. So even though the step is named the same, you can now use it
169
+ in your feature files like so:
161
170
 
162
171
  ``` cucumber
163
172
  @interface
@@ -167,149 +176,103 @@ Scenario: do it through the interface
167
176
  Scenario: do it through the database
168
177
  ```
169
178
 
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
+ Be careful though not to tag a feature with both `@interface` and `@database`
180
+ in this example. Since steps use the Ruby inheritance chain, the step which is
181
+ included last will "win", just like any other Ruby method. This might not be
182
+ what you expect.
179
183
 
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`:
184
+ Since this pattern of creating a module and including it for a specific tag
185
+ is very common, we have created a handy shortcut for it:
183
186
 
184
187
  ``` 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)
188
+ steps_for :interface do
189
+ step "I do it" do
190
+ ...
201
191
  end
202
192
  end
193
+ ```
203
194
 
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
195
+ Check out [features/alignment_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/steps/alignment_steps.rb)
218
196
 
219
- step "the dragon breathes fire" do
220
- self.red_dragon = 1
221
- end
222
- end
223
- ```
197
+ for an example.
224
198
 
199
+ ### Where to place steps
225
200
 
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:
201
+ Turnip automatically loads your `spec_helper` file. From there you can place
202
+ your steps wherever you want, and load them however you like. For example, if
203
+ you were to put your steps in `spec/steps`, you could load them like this:
229
204
 
230
- ``` cucumber
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
205
+ ``` ruby
206
+ Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }
247
207
  ```
248
208
 
249
209
  ### Calling steps from other steps
250
- You can also call steps from other steps. This is done by just calling `step
251
- "name_of_the_step"`, so for instance if you have:
210
+
211
+ Since steps are Ruby methods you can call them like other Ruby methods.
212
+ However, since the step description likely contains spaces and other special
213
+ characters, you will probably have to use `send` to call the step:
252
214
 
253
215
  ``` ruby
254
- step "a random step" do
255
- @value = 1
216
+ step "the value is :num" do |num|
217
+ @value = num
256
218
  end
257
219
 
258
- step "calling a step" do
259
- step "a random step"
260
- @value += 1
220
+ step "the value is twice as much as :num" do |num|
221
+ send "the value is :num", num * 2
261
222
  end
262
223
  ```
263
224
 
264
- Now if you use the step `calling a step` in any Scenario, then the value of
265
- `@value` will be 2 afterwards as it first executes the code defined for the step
266
- `a random step`. You can think of it as a simple method call.
225
+ If you use the second step, it will call into the first step, sending in the
226
+ doubled value.
267
227
 
268
- ### Auto-included steps
269
- By default, Turnip will automatically make available any steps defined in
270
- a `steps_for` block with the same name as the feature file being run. For
271
- example, given this step file:
228
+ Sometimes you will want to call the step just like you would from your feature
229
+ file, in that case you can use the `step` method:
272
230
 
273
231
  ``` ruby
274
- # user_signup_steps.rb
275
- steps_for :user_signup do
276
- step "I am on the homepage" do
277
- ...
278
- end
279
-
280
- step "I signup with valid info" do
281
- ...
282
- end
232
+ step "the value is :num" do |num|
233
+ @value = num
234
+ end
283
235
 
284
- step "I should see a welcome message" do
285
- ...
286
- end
236
+ step "the value is the magic number"
237
+ step "the value is 3"
287
238
  end
288
239
  ```
289
240
 
290
- Then the following feature file would run just fine even though we
291
- did not explicitly tag it with `@user_signup`.
241
+ ### Calling steps manually
292
242
 
293
- ``` cucumber
294
- # user_signup.feature
295
- Feature: A user can signup
296
- Scenario: with email address
297
- Given I am on the homepage
298
- When I signup with valid info
299
- Then I should see a welcome message
300
- ```
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.
249
+
250
+ For example:
251
+
252
+ ``` ruby
253
+ class Monster
254
+ include Turnip::Execute
301
255
 
302
- Note that the `steps_for :user_signup` did not technically have to
303
- appear in the user_signup_steps.rb file; it could have been located
304
- in any `steps.rb` file that was autoloaded by Turnip.
256
+ step("sing a song") { "Arrrghghggh" }
257
+ step("eat :count villager(s)") { Villager.eat(count) }
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
+ ```
305
266
 
306
- This feature can be turned off using the `Turnip::Config.autotag_features`
307
- option if desired.
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.
308
270
 
309
271
  ## Custom step placeholders
310
- Do you want to be more specific in what to match in your step
311
- placeholders? Do you find it bothersome to have to constantly cast them to the
312
- correct type? Turnip supports custom placeholders to solve both problems, like this:
272
+
273
+ Do you want to be more specific in what to match in your step placeholders? Do
274
+ you find it bothersome to have to constantly cast them to the correct type?
275
+ Turnip supports custom placeholders to solve both problems, like this:
313
276
 
314
277
  ``` ruby
315
278
  step "there are :count monsters" do |count|
@@ -349,6 +312,7 @@ These regular expressions must not use anchors, e.g. `^` or `$`. They may not
349
312
  contain named capture groups, e.g. `(?<color>blue|green)`.
350
313
 
351
314
  ## Table Steps
315
+
352
316
  Turnip also supports steps that take a table as a parameter similar to Cucumber:
353
317
 
354
318
  ``` cucumber
@@ -361,7 +325,7 @@ Scenario: This is a feature with a table
361
325
  And "Moorg" should have 12 hitpoints
362
326
  ```
363
327
  The table is a `Turnip::Table` object which works in much the same way as Cucumber's
364
- `Cucumber::Ast::Table` obects.
328
+ `Cucumber::Ast::Table` objects.
365
329
 
366
330
  E.g. converting the `Turnip::Table` to an array of hashes:
367
331
 
@@ -376,34 +340,32 @@ end
376
340
 
377
341
  ## Using with Capybara
378
342
 
379
- Just require `turnip/capybara` in your `spec_helper`. You can now use the
380
- same tags you'd use in Cucumber to switch between drivers e.g.
381
- `@javascript` or `@selenium`. Your Turnip features will also be run
382
- with the `:type => :request` metadata, so that Capybara is included and
383
- also any other extensions you might want to add.
343
+ Just require `turnip/capybara` in your `spec_helper`. You can now use the same
344
+ tags you'd use in Cucumber to switch between drivers e.g. `@javascript` or
345
+ `@selenium`. Your Turnip features will also be run with the `:type => :request`
346
+ metadata, so that Capybara is included and also any other extensions you might
347
+ want to add.
384
348
 
385
349
  ## License
386
350
 
387
351
  (The MIT License)
388
352
 
389
- Copyright (c) 2011 Jonas Nicklas
390
-
391
- Permission is hereby granted, free of charge, to any person obtaining
392
- a copy of this software and associated documentation files (the
393
- 'Software'), to deal in the Software without restriction, including
394
- without limitation the rights to use, copy, modify, merge, publish,
395
- distribute, sublicense, and/or sell copies of the Software, and to
396
- permit persons to whom the Software is furnished to do so, subject to
397
- the following conditions:
398
-
399
- The above copyright notice and this permission notice shall be
400
- included in all copies or substantial portions of the Software.
401
-
402
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
403
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
404
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
405
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
406
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
407
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
408
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
409
-
353
+ Copyright (c) 2011-2012 Jonas Nicklas
354
+
355
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
356
+ this software and associated documentation files (the 'Software'), to deal in
357
+ the Software without restriction, including without limitation the rights to
358
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
359
+ of the Software, and to permit persons to whom the Software is furnished to do
360
+ so, subject to the following conditions:
361
+
362
+ The above copyright notice and this permission notice shall be included in all
363
+ copies or substantial portions of the Software.
364
+
365
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
366
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
367
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
368
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
369
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
370
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
371
+ SOFTWARE.
@@ -1,5 +1,6 @@
1
+ @autoload_steps
1
2
  Feature: Auto-loaded steps
2
-
3
+
3
4
  @scenario_tag
4
5
  Scenario:
5
- Given an auto-loaded step is available
6
+ Given an auto-loaded step is available
@@ -0,0 +1,8 @@
1
+ Feature: raises errors
2
+ Scenario: Step missing
3
+ When a step just does not exist
4
+ Scenario: Step raises error
5
+ When raise error
6
+ Scenario: Incorrect expectation
7
+ Given there is a monster
8
+ Then it should die
@@ -1,5 +1,7 @@
1
+ @step_calling
1
2
  Feature: Step-calling steps
2
3
 
4
+ @wip
3
5
  Scenario: when the called step is visible
4
6
  Given a visible step call
5
7
 
@@ -0,0 +1,23 @@
1
+ module Alignment
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
8
+
9
+ steps_for :evil do
10
+ include Alignment
11
+
12
+ step "the monster has an alignment" do
13
+ self.alignment = 'Evil'
14
+ end
15
+ end
16
+
17
+ steps_for :neutral do
18
+ include Alignment
19
+
20
+ step "the monster has an alignment" do
21
+ self.alignment = 'Neutral'
22
+ end
23
+ end