turnip 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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