turnip 0.2.0 → 0.3.0

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