spreewald 0.1.1 → 0.1.2
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/README.md +397 -29
- data/Rakefile +10 -0
- data/lib/spreewald/development_steps.rb +6 -0
- data/lib/spreewald/email_steps.rb +43 -15
- data/lib/spreewald/table_steps.rb +5 -0
- data/lib/spreewald/timecop_steps.rb +16 -0
- data/lib/spreewald/web_steps.rb +89 -30
- data/lib/spreewald_support/step_fallback.rb +11 -0
- data/lib/spreewald_support/version.rb +1 -1
- data/spreewald.gemspec +1 -1
- data/support/documentation_generator.rb +110 -0
- metadata +7 -7
data/README.md
CHANGED
@@ -28,58 +28,426 @@ Alternatively, you can require everything by doing
|
|
28
28
|
|
29
29
|
require 'spreewald/all_steps'
|
30
30
|
|
31
|
+
## Waiting for page load
|
32
|
+
|
33
|
+
Spreewald's web steps are all aware, that you might run them with a Selenium/Capybara webdriver, and wait for the browser to finish loading the page, if necessary.
|
34
|
+
|
35
|
+
This is done, by rerunning any assertions until they suceed, or a timeout is reached.
|
36
|
+
|
37
|
+
You can achieve this in your own steps by wrapping them inside a `patiently do` block, like
|
38
|
+
|
39
|
+
Then /^I should see "([^\"]*)" in the HTML$/ do |text|
|
40
|
+
patiently do
|
41
|
+
page.body.should include(text)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
More info [here](https://makandracards.com/makandra/12139-waiting-for-page-load-with-spreewald).
|
46
|
+
|
31
47
|
## Steps
|
32
48
|
|
33
|
-
|
49
|
+
### development_steps.rb
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
* **Then it should work**
|
54
|
+
|
55
|
+
Marks scenario as pending
|
56
|
+
|
57
|
+
|
58
|
+
* **Then debugger**
|
59
|
+
|
60
|
+
Starts debugger
|
61
|
+
|
62
|
+
|
63
|
+
* **@slow**
|
64
|
+
|
65
|
+
Waits 2 seconds after each step
|
66
|
+
|
67
|
+
|
68
|
+
* **@single**
|
69
|
+
|
70
|
+
Waits for keypress after each step
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
### email_steps.rb
|
75
|
+
|
76
|
+
|
34
77
|
|
35
|
-
|
78
|
+
* **When I clear my emails**
|
36
79
|
|
37
|
-
|
80
|
+
|
38
81
|
|
39
|
-
* `Then debugger`
|
40
|
-
* `Then it should work` (marks step as pending)
|
41
|
-
* `@slow-motion` (waits 2 seconds after each step)
|
42
|
-
* `@single-step` (waits for keyboard input after each step)
|
43
82
|
|
83
|
+
* **Then (an|no) e?mail should have been sent with:**
|
44
84
|
|
45
|
-
|
85
|
+
Example:
|
86
|
+
|
87
|
+
Then an email should have been sent with:
|
88
|
+
"""
|
89
|
+
From: max.mustermann@example.com
|
90
|
+
To: john.doe@example.com
|
91
|
+
Subject: Unter anderem der Betreff kann auch "Anführungszeichen" enthalten
|
92
|
+
Body: ...
|
93
|
+
Attachments: ...
|
94
|
+
"""
|
95
|
+
|
96
|
+
You can skip lines, of course.
|
46
97
|
|
47
|
-
Check for the existance of an email with
|
48
98
|
|
49
|
-
|
50
|
-
"""
|
51
|
-
From: max.mustermann@example.com
|
52
|
-
To: john.doe@example.com
|
53
|
-
Subject: Unter anderem der Betreff kann auch "Anführungszeichen" enthalten
|
54
|
-
Body: ...
|
55
|
-
Attachments: ...
|
56
|
-
"""
|
99
|
+
* **When I follow the (first|second|third)? link in the e?mail**
|
57
100
|
|
58
|
-
|
101
|
+
Only works after you have retrieved the mail using "Then an email should have been sent with:"
|
59
102
|
|
60
|
-
After you have used that step, you can also check for content with
|
61
103
|
|
62
|
-
|
63
|
-
"""
|
64
|
-
Jede dieser Text-Zeilen
|
65
|
-
muss irgendwo im Body vorhanden sein
|
66
|
-
"""
|
104
|
+
* **Then no e?mail should have been sent**
|
67
105
|
|
68
|
-
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
* **Then I should see "..." in the e?mail**
|
110
|
+
|
111
|
+
Checks that the last sent email includes some text
|
112
|
+
|
113
|
+
|
114
|
+
* **Then show me the e?mails**
|
115
|
+
|
116
|
+
Print all sent emails to STDOUT.
|
117
|
+
|
118
|
+
|
119
|
+
* **Then that e?mail should have the following lines in the body:**
|
120
|
+
|
121
|
+
Only works after you've retrieved the email using "Then an email should have been sent with:"
|
122
|
+
|
123
|
+
Example:
|
124
|
+
|
125
|
+
And that mail should have the following lines in the body:
|
126
|
+
"""
|
127
|
+
All of these lines
|
128
|
+
need to be present
|
129
|
+
"""
|
130
|
+
|
131
|
+
|
132
|
+
* **Then that e?mail should have the following body:**
|
133
|
+
|
134
|
+
Only works after you've retrieved the email using "Then an email should have been sent with:"
|
135
|
+
Checks that the text should be included in the retrieved email
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
### table_steps.rb
|
69
140
|
|
70
141
|
Check the content of tables in your HTML.
|
71
142
|
|
72
143
|
See [this article](https://makandracards.com/makandra/763-cucumber-step-to-match-table-rows-with-capybara) for details.
|
73
144
|
|
145
|
+
* **Then I should( not)? see a table with the following rows( in any order)?**
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
|
74
150
|
|
75
|
-
###
|
151
|
+
### timecop_steps.rb
|
76
152
|
|
77
153
|
Steps to travel through time using [Timecop](https://github.com/jtrupiano/timecop).
|
78
154
|
|
79
155
|
See [this article](https://makandracards.com/makandra/1222-useful-cucumber-steps-to-travel-through-time-with-timecop) for details.
|
80
156
|
|
81
|
-
|
157
|
+
* **When the (date|time) is "(\d{4}-\d{2}-\d{2}( \d{1,2}:\d{2})?)"**
|
158
|
+
|
159
|
+
Example:
|
160
|
+
|
161
|
+
Given the date is "2012-02-10"
|
162
|
+
Given the time is "2012-02-10 13:40"
|
163
|
+
|
164
|
+
|
165
|
+
* **When the time is "(\d{1,2}:\d{2})"**
|
166
|
+
|
167
|
+
Example:
|
168
|
+
|
169
|
+
Given the time is "13:40"
|
170
|
+
|
171
|
+
|
172
|
+
* **When it is (\d+|a|some|a few) (seconds?|minutes?|hours?|days?|weeks?|months?|years?) (later|earlier)**
|
173
|
+
|
174
|
+
Example:
|
175
|
+
|
176
|
+
When it is 10 minutes later
|
177
|
+
When it is a few hours earlier
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
### web_steps.rb
|
182
|
+
|
183
|
+
Most of cucumber-rails' original web steps plus a few of our own.
|
184
|
+
|
185
|
+
Note that cucumber-rails deprecated all its steps quite a while ago with the following
|
186
|
+
deprecation notice. Decide for yourself whether you want to use them:
|
187
|
+
|
188
|
+
> This file was generated by Cucumber-Rails and is only here to get you a head start
|
189
|
+
> These step definitions are thin wrappers around the Capybara/Webrat API that lets you
|
190
|
+
> visit pages, interact with widgets and make assertions about page content.
|
191
|
+
|
192
|
+
> If you use these step definitions as basis for your features you will quickly end up
|
193
|
+
> with features that are:
|
194
|
+
|
195
|
+
> * Hard to maintain
|
196
|
+
> * Verbose to read
|
197
|
+
|
198
|
+
> A much better approach is to write your own higher level step definitions, following
|
199
|
+
> the advice in the following blog posts:
|
200
|
+
|
201
|
+
> * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
|
202
|
+
> * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
|
203
|
+
> * http://elabs.se/blog/15-you-re-cuking-it-wrong
|
204
|
+
|
205
|
+
|
206
|
+
* **When ... within (.*[^:])**
|
207
|
+
|
208
|
+
You can append 'within [selector]' to any other web step
|
209
|
+
Example:
|
210
|
+
|
211
|
+
Then I should see "some text" within ".page_body"
|
212
|
+
|
213
|
+
|
214
|
+
* **Given I am on ...**
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
* **When I go to ...**
|
220
|
+
|
221
|
+
|
222
|
+
|
223
|
+
|
224
|
+
* **When I press "..."**
|
225
|
+
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
* **When I follow "..."**
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
* **When I fill in "..." with "..."**
|
235
|
+
|
236
|
+
Fill in text field
|
237
|
+
|
238
|
+
|
239
|
+
* **When I fill in "..." for "..."**
|
240
|
+
|
241
|
+
Fill in text field
|
242
|
+
|
243
|
+
|
244
|
+
* **When I select "..." from "..."**
|
245
|
+
|
246
|
+
Select from select box
|
247
|
+
|
248
|
+
|
249
|
+
* **When I check "..."**
|
250
|
+
|
251
|
+
Check a checkbox
|
252
|
+
|
253
|
+
|
254
|
+
* **When I uncheck "..."**
|
255
|
+
|
256
|
+
Uncheck a checkbox
|
257
|
+
|
258
|
+
|
259
|
+
* **When I choose "..."**
|
260
|
+
|
261
|
+
Select a radio button
|
262
|
+
|
263
|
+
|
264
|
+
* **When I attach the file "..." to "..."**
|
265
|
+
|
266
|
+
|
267
|
+
|
268
|
+
|
269
|
+
* **Then I should see "..."**
|
270
|
+
|
271
|
+
Checks that some text appears on the page
|
272
|
+
|
273
|
+
Note that this does not detect if the text might be hidden via CSS
|
274
|
+
|
275
|
+
|
276
|
+
* **Then I should see \/([^\/]*)\/**
|
277
|
+
|
278
|
+
Checks that a regexp appears on the page
|
279
|
+
|
280
|
+
Note that this does not detect if the text might be hidden via CSS
|
281
|
+
|
282
|
+
|
283
|
+
* **Then I should not see "..."**
|
284
|
+
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
* **Then I should not see \/([^\/]*)\/**
|
289
|
+
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
* **Then the "..." field( within ...)? should contain "..."**
|
294
|
+
|
295
|
+
Checks that a input field contains some value
|
296
|
+
|
297
|
+
|
298
|
+
* **Then the "..." field( within ...)? should not contain "..."**
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
|
303
|
+
* **Then the "..." field should have the error "..."**
|
304
|
+
|
305
|
+
checks that an input field was wrapped with a validation error
|
306
|
+
|
307
|
+
|
308
|
+
* **Then the "..." field should( not)? have an error**
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
* **Then the "..." field should have no error**
|
314
|
+
|
315
|
+
|
316
|
+
|
317
|
+
|
318
|
+
* **Then the radio button "..." should( not)? be (checked|selected)**
|
319
|
+
|
320
|
+
|
321
|
+
|
322
|
+
|
323
|
+
* **Then I should be on ...**
|
324
|
+
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
* **Then I should have the following query string:**
|
329
|
+
|
330
|
+
Example:
|
331
|
+
|
332
|
+
I should have the following query string:
|
333
|
+
| locale | de |
|
334
|
+
| currency_code | EUR |
|
335
|
+
|
336
|
+
Succeeds when the URL contains the given "locale" and "currency_code" params
|
337
|
+
|
338
|
+
|
339
|
+
* **Then show me the page**
|
340
|
+
|
341
|
+
Open the current Capybara page using the "launchy" gem
|
342
|
+
|
343
|
+
|
344
|
+
* **Then I should( not)? see a field "..."**
|
345
|
+
|
346
|
+
checks for the existance of a input field (given its id or label)
|
347
|
+
|
348
|
+
|
349
|
+
* **Then I should( not)? see the (number|amount) ([\-\d,\.]+)( (.*?))?**
|
350
|
+
|
351
|
+
Better way to test for a number of money amount than a `Then I should see`
|
352
|
+
|
353
|
+
Checks that there is unexpected minus sign, decimal places etc.
|
354
|
+
|
355
|
+
See [here](https://makandracards.com/makandra/1225-test-that-a-number-or-money-amount-is-shown-with-cucumber) for details
|
356
|
+
|
357
|
+
|
358
|
+
* **Then I should get a response with content-type "..."**
|
359
|
+
|
360
|
+
Checks "Content-Type" HTTP header
|
361
|
+
|
362
|
+
|
363
|
+
* **Then I should get a download with filename "..."**
|
364
|
+
|
365
|
+
Checks "Content-Disposition" HTTP header
|
366
|
+
|
367
|
+
|
368
|
+
* **Then "..." should be selected for "..."**
|
369
|
+
|
370
|
+
Checks that a certain option is selected for a text field
|
371
|
+
|
372
|
+
|
373
|
+
* **Then nothing should be selected for "..."?**
|
374
|
+
|
375
|
+
|
376
|
+
|
377
|
+
|
378
|
+
* **Then "..." should( not)? be an option for "..."( within "...")?**
|
379
|
+
|
380
|
+
Checks for the presence of an option in a select
|
381
|
+
|
382
|
+
|
383
|
+
* **Then I should see '([^']*)'( within '([^']*)')?**
|
384
|
+
|
385
|
+
Like `Then I should see`, but with single instead of double quotes. In case the string contains quotes as well.
|
386
|
+
|
387
|
+
|
388
|
+
* **Then I should see "..." in the HTML**
|
389
|
+
|
390
|
+
Check that the raw HTML contains a string
|
391
|
+
|
392
|
+
|
393
|
+
* **Then I should not see "..." in the HTML**
|
394
|
+
|
395
|
+
|
396
|
+
|
397
|
+
|
398
|
+
* **Then I should see an error**
|
399
|
+
|
400
|
+
Checks that status code is 400..599
|
401
|
+
|
402
|
+
|
403
|
+
* **When I reload the page**
|
404
|
+
|
405
|
+
|
406
|
+
|
407
|
+
|
408
|
+
* **Then "..." should( not)? be visible**
|
409
|
+
|
410
|
+
Checks that an element is actually visible, also considering styles
|
411
|
+
Within a selenium test, the browser is asked whether the element is really visible
|
412
|
+
In a non-selenium test, we only check for ".hidden", ".invisible" or "style: display:none"
|
413
|
+
|
414
|
+
More details [here](https://makandracards.com/makandra/1049-capybara-check-that-a-page-element-is-hidden-via-css)
|
415
|
+
|
416
|
+
|
417
|
+
* **When I click on "..."**
|
418
|
+
|
419
|
+
Click on some text that might not be a link
|
420
|
+
|
421
|
+
|
422
|
+
* **Then I should (not )?see an element "..."**
|
423
|
+
|
424
|
+
Example:
|
425
|
+
|
426
|
+
Then I should see an element ".page .container"
|
427
|
+
|
428
|
+
|
429
|
+
* **Then I should get a text response**
|
430
|
+
|
431
|
+
Checks that the result has content type text/plain
|
432
|
+
|
433
|
+
|
434
|
+
* **When I follow "..." inside any "..."**
|
435
|
+
|
436
|
+
Click a link within an element matching the given selector. Will try to be clever
|
437
|
+
and disregard elements that don't contain a matching link.
|
438
|
+
|
439
|
+
Example:
|
440
|
+
|
441
|
+
When I follow "Read more" inside any ".text_snippet"
|
442
|
+
|
443
|
+
|
444
|
+
|
445
|
+
* **Then I should( not)? see "..." inside any "..."**
|
446
|
+
|
447
|
+
|
448
|
+
|
449
|
+
|
450
|
+
* **When I fill in "..." with "..." inside any "..."**
|
82
451
|
|
83
|
-
|
452
|
+
|
84
453
|
|
85
|
-
Note that cucumber-rails deprecated those a while ago (you can see the original deprecation notice at the top of [our web_steps](/makandra/spreewald/blob/master/lib/spreewald/web_steps.rb)). Make up your own mind whether you want to use them or not.
|
data/Rakefile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
|
5
|
+
task "update_readme" do
|
6
|
+
require 'support/documentation_generator'
|
7
|
+
readme = File.read('README.md')
|
8
|
+
start_of_steps_section = readme =~ /^## Steps/
|
9
|
+
length_of_steps_section = (readme[(start_of_steps_section+1)..-1] =~ /^##[^#]/) || readme.size - start_of_steps_section
|
10
|
+
readme[start_of_steps_section, length_of_steps_section] = "## Steps\n\n" + DocumentationGenerator::StepDefinitionsDirectory.new('lib/spreewald').format
|
11
|
+
File.open('README.md', 'w') { |f| f.write(readme) }
|
12
|
+
end
|
@@ -1,15 +1,21 @@
|
|
1
|
+
#
|
2
|
+
|
3
|
+
# Marks scenario as pending
|
1
4
|
Then /^it should work$/ do
|
2
5
|
pending
|
3
6
|
end
|
4
7
|
|
8
|
+
# Starts debugger
|
5
9
|
Then /^debugger$/ do
|
6
10
|
debugger
|
7
11
|
end
|
8
12
|
|
13
|
+
# Waits 2 seconds after each step
|
9
14
|
AfterStep('@slow-motion') do
|
10
15
|
sleep 2
|
11
16
|
end
|
12
17
|
|
18
|
+
# Waits for keypress after each step
|
13
19
|
AfterStep('@single-step') do
|
14
20
|
print "Single Stepping. Hit enter to continue"
|
15
21
|
STDIN.getc
|
@@ -4,10 +4,38 @@ Before do
|
|
4
4
|
ActionMailer::Base.deliveries.clear
|
5
5
|
end
|
6
6
|
|
7
|
-
When /^I clear my
|
7
|
+
When /^I clear my e?mails$/ do
|
8
8
|
ActionMailer::Base.deliveries.clear
|
9
9
|
end
|
10
10
|
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# Then an email should have been sent with:
|
14
|
+
# """
|
15
|
+
# From: max.mustermann@example.com
|
16
|
+
# To: john.doe@example.com
|
17
|
+
# Subject: Unter anderem der Betreff kann auch "Anführungszeichen" enthalten
|
18
|
+
# Body: ...
|
19
|
+
# Attachments: ...
|
20
|
+
# """
|
21
|
+
#
|
22
|
+
# You can skip lines, of course.
|
23
|
+
Then /^(an|no) e?mail should have been sent with:$/ do |mode, raw_data|
|
24
|
+
raw_data.strip!
|
25
|
+
conditions = {}.tap do |hash|
|
26
|
+
raw_data.split("\n").each do |row|
|
27
|
+
if row.match(/^[a-z]+: /i)
|
28
|
+
key, value = row.split(": ", 2)
|
29
|
+
hash[key.downcase.to_sym] = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@mail = MailFinder.find(conditions)
|
34
|
+
expectation = mode == 'no' ? 'should_not' : 'should'
|
35
|
+
@mail.send(expectation, be_present)
|
36
|
+
end
|
37
|
+
|
38
|
+
# nodoc
|
11
39
|
Then /^(an|no) e?mail should have been sent((?: |and|with|from "[^"]+"|to "[^"]+"|the subject "[^"]+"|the body "[^"]+"|the attachments "[^"]+")+)$/ do |mode, query|
|
12
40
|
conditions = {}
|
13
41
|
conditions[:to] = $1 if query =~ /to "([^"]+)"/
|
@@ -22,6 +50,7 @@ Then /^(an|no) e?mail should have been sent((?: |and|with|from "[^"]+"|to "[^"]+
|
|
22
50
|
@mail.send(expectation, be_present)
|
23
51
|
end
|
24
52
|
|
53
|
+
# Only works after you have retrieved the mail using "Then an email should have been sent with:"
|
25
54
|
When /^I follow the (first|second|third)? ?link in the e?mail$/ do |index_in_words|
|
26
55
|
mail = @mail || ActionMailer::Base.deliveries.last
|
27
56
|
index = { nil => 0, 'first' => 0, 'second' => 1, 'third' => 2 }[index_in_words]
|
@@ -32,37 +61,36 @@ Then /^no e?mail should have been sent$/ do
|
|
32
61
|
ActionMailer::Base.deliveries.should be_empty
|
33
62
|
end
|
34
63
|
|
64
|
+
# Checks that the last sent email includes some text
|
35
65
|
Then /^I should see "([^\"]*)" in the e?mail$/ do |text|
|
36
66
|
ActionMailer::Base.deliveries.last.body.should include(text)
|
37
67
|
end
|
38
68
|
|
69
|
+
# Print all sent emails to STDOUT.
|
39
70
|
Then /^show me the e?mails$/ do
|
40
71
|
ActionMailer::Base.deliveries.each do |mail|
|
41
72
|
p [mail.from, mail.to, mail.subject]
|
42
73
|
end
|
43
74
|
end
|
44
75
|
|
45
|
-
Then /^(an|no) e?mail should have been sent with:$/ do |mode, raw_data|
|
46
|
-
raw_data.strip!
|
47
|
-
conditions = {}.tap do |hash|
|
48
|
-
raw_data.split("\n").each do |row|
|
49
|
-
if row.match(/^[a-z]+: /i)
|
50
|
-
key, value = row.split(": ", 2)
|
51
|
-
hash[key.downcase.to_sym] = value
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
@mail = MailFinder.find(conditions)
|
56
|
-
expectation = mode == 'no' ? 'should_not' : 'should'
|
57
|
-
@mail.send(expectation, be_present)
|
58
|
-
end
|
59
76
|
|
77
|
+
# Only works after you've retrieved the email using "Then an email should have been sent with:"
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# And that mail should have the following lines in the body:
|
82
|
+
# """
|
83
|
+
# All of these lines
|
84
|
+
# need to be present
|
85
|
+
# """
|
60
86
|
Then /^that e?mail should have the following lines in the body:$/ do |body|
|
61
87
|
body.each do |line|
|
62
88
|
@mail.body.should include(line.strip)
|
63
89
|
end
|
64
90
|
end
|
65
91
|
|
92
|
+
# Only works after you've retrieved the email using "Then an email should have been sent with:"
|
93
|
+
# Checks that the text should be included in the retrieved email
|
66
94
|
Then /^that e?mail should have the following body:$/ do |body|
|
67
95
|
@mail.body.should include(body.strip)
|
68
96
|
end
|
@@ -1,3 +1,8 @@
|
|
1
|
+
# Check the content of tables in your HTML.
|
2
|
+
#
|
3
|
+
# See [this article](https://makandracards.com/makandra/763-cucumber-step-to-match-table-rows-with-capybara) for details.
|
4
|
+
|
5
|
+
|
1
6
|
require 'spreewald_support/tolerance_for_selenium_sync_issues'
|
2
7
|
|
3
8
|
module TableStepsHelper
|
@@ -1,13 +1,29 @@
|
|
1
|
+
# Steps to travel through time using [Timecop](https://github.com/jtrupiano/timecop).
|
2
|
+
#
|
3
|
+
# See [this article](https://makandracards.com/makandra/1222-useful-cucumber-steps-to-travel-through-time-with-timecop) for details.
|
4
|
+
|
5
|
+
|
1
6
|
if defined?(Timecop)
|
2
7
|
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# Given the date is "2012-02-10"
|
11
|
+
# Given the time is "2012-02-10 13:40"
|
3
12
|
When /^the (?:date|time) is "(\d{4}-\d{2}-\d{2}(?: \d{1,2}:\d{2})?)"$/ do |time|
|
4
13
|
Timecop.travel Time.parse(time)
|
5
14
|
end
|
6
15
|
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# Given the time is "13:40"
|
7
19
|
When /^the time is "(\d{1,2}:\d{2})"$/ do |time|
|
8
20
|
Timecop.travel Time.parse(time) # date will be today
|
9
21
|
end
|
10
22
|
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# When it is 10 minutes later
|
26
|
+
# When it is a few hours earlier
|
11
27
|
When /^it is (\d+|a|some|a few) (seconds?|minutes?|hours?|days?|weeks?|months?|years?) (later|earlier)$/ do |amount, unit, direction|
|
12
28
|
amount = case amount
|
13
29
|
when 'a'
|
data/lib/spreewald/web_steps.rb
CHANGED
@@ -1,34 +1,42 @@
|
|
1
|
-
#
|
1
|
+
# Most of cucumber-rails' original web steps plus a few of our own.
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# visit pages, interact with widgets and make assertions about page content.
|
3
|
+
# Note that cucumber-rails deprecated all its steps quite a while ago with the following
|
4
|
+
# deprecation notice. Decide for yourself whether you want to use them:
|
6
5
|
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# > This file was generated by Cucumber-Rails and is only here to get you a head start
|
7
|
+
# > These step definitions are thin wrappers around the Capybara/Webrat API that lets you
|
8
|
+
# > visit pages, interact with widgets and make assertions about page content.
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# > If you use these step definitions as basis for your features you will quickly end up
|
11
|
+
# > with features that are:
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
13
|
+
# > * Hard to maintain
|
14
|
+
# > * Verbose to read
|
15
15
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
16
|
+
# > A much better approach is to write your own higher level step definitions, following
|
17
|
+
# > the advice in the following blog posts:
|
18
|
+
#
|
19
|
+
# > * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
|
20
|
+
# > * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
|
21
|
+
# > * http://elabs.se/blog/15-you-re-cuking-it-wrong
|
19
22
|
#
|
20
23
|
|
21
24
|
require 'spreewald_support/tolerance_for_selenium_sync_issues'
|
22
25
|
require 'spreewald_support/path_selector_fallbacks'
|
26
|
+
require 'spreewald_support/step_fallback'
|
23
27
|
require 'uri'
|
24
28
|
require 'cgi'
|
25
29
|
|
26
|
-
|
30
|
+
|
31
|
+
# You can append 'within [selector]' to any other web step
|
32
|
+
# Example:
|
33
|
+
#
|
34
|
+
# Then I should see "some text" within ".page_body"
|
27
35
|
When /^(.*) within (.*[^:])$/ do |nested_step, parent|
|
28
36
|
with_scope(parent) { step(nested_step) }
|
29
37
|
end
|
30
38
|
|
31
|
-
#
|
39
|
+
# nodoc
|
32
40
|
When /^(.*) within (.*[^:]):$/ do |nested_step, parent, table_or_string|
|
33
41
|
with_scope(parent) { step("#{nested_step}:", table_or_string) }
|
34
42
|
end
|
@@ -53,36 +61,42 @@ When /^(?:|I )follow "([^"]*)"$/ do |link|
|
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
64
|
+
# Fill in text field
|
56
65
|
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
|
57
66
|
patiently do
|
58
67
|
fill_in(field, :with => value)
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
71
|
+
# Fill in text field
|
62
72
|
When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
|
63
73
|
patiently do
|
64
74
|
fill_in(field, :with => value)
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
# Select from select box
|
68
79
|
When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
|
69
80
|
patiently do
|
70
81
|
select(value, :from => field)
|
71
82
|
end
|
72
83
|
end
|
73
84
|
|
85
|
+
# Check a checkbox
|
74
86
|
When /^(?:|I )check "([^"]*)"$/ do |field|
|
75
87
|
patiently do
|
76
88
|
check(field)
|
77
89
|
end
|
78
90
|
end
|
79
91
|
|
92
|
+
# Uncheck a checkbox
|
80
93
|
When /^(?:|I )uncheck "([^"]*)"$/ do |field|
|
81
94
|
patiently do
|
82
95
|
uncheck(field)
|
83
96
|
end
|
84
97
|
end
|
85
98
|
|
99
|
+
# Select a radio button
|
86
100
|
When /^(?:|I )choose "([^"]*)"$/ do |field|
|
87
101
|
patiently do
|
88
102
|
choose(field)
|
@@ -95,12 +109,18 @@ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
|
|
95
109
|
end
|
96
110
|
end
|
97
111
|
|
112
|
+
# Checks that some text appears on the page
|
113
|
+
#
|
114
|
+
# Note that this does not detect if the text might be hidden via CSS
|
98
115
|
Then /^(?:|I )should see "([^"]*)"$/ do |text|
|
99
116
|
patiently do
|
100
117
|
page.should have_content(text)
|
101
118
|
end
|
102
119
|
end
|
103
120
|
|
121
|
+
# Checks that a regexp appears on the page
|
122
|
+
#
|
123
|
+
# Note that this does not detect if the text might be hidden via CSS
|
104
124
|
Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
|
105
125
|
regexp = Regexp.new(regexp)
|
106
126
|
patiently do
|
@@ -121,6 +141,8 @@ Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
|
|
121
141
|
end
|
122
142
|
end
|
123
143
|
|
144
|
+
|
145
|
+
# Checks that a input field contains some value
|
124
146
|
Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
|
125
147
|
patiently do
|
126
148
|
with_scope(parent) do
|
@@ -141,6 +163,9 @@ Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |f
|
|
141
163
|
end
|
142
164
|
end
|
143
165
|
end
|
166
|
+
|
167
|
+
|
168
|
+
# checks that an input field was wrapped with a validation error
|
144
169
|
Then /^the "([^"]*)" field should have the error "([^"]*)"$/ do |field, error_message|
|
145
170
|
patiently do
|
146
171
|
element = find_field(field)
|
@@ -177,6 +202,7 @@ Then /^the "([^"]*)" field should have no error$/ do |field|
|
|
177
202
|
end
|
178
203
|
end
|
179
204
|
|
205
|
+
# nodoc
|
180
206
|
Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
|
181
207
|
patiently do
|
182
208
|
with_scope(parent) do
|
@@ -186,6 +212,7 @@ Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, pa
|
|
186
212
|
end
|
187
213
|
end
|
188
214
|
|
215
|
+
# nodoc
|
189
216
|
Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
|
190
217
|
patiently do
|
191
218
|
with_scope(parent) do
|
@@ -208,6 +235,13 @@ Then /^(?:|I )should be on (.+)$/ do |page_name|
|
|
208
235
|
end
|
209
236
|
end
|
210
237
|
|
238
|
+
# Example:
|
239
|
+
#
|
240
|
+
# I should have the following query string:
|
241
|
+
# | locale | de |
|
242
|
+
# | currency_code | EUR |
|
243
|
+
#
|
244
|
+
# Succeeds when the URL contains the given "locale" and "currency_code" params
|
211
245
|
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
212
246
|
patiently do
|
213
247
|
query = URI.parse(current_url).query
|
@@ -219,11 +253,13 @@ Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
|
219
253
|
end
|
220
254
|
end
|
221
255
|
|
256
|
+
# Open the current Capybara page using the "launchy" gem
|
222
257
|
Then /^show me the page$/ do
|
223
258
|
save_and_open_page
|
224
259
|
end
|
225
260
|
|
226
261
|
|
262
|
+
# checks for the existance of a input field (given its id or label)
|
227
263
|
Then /^I should( not)? see a field "([^"]*)"$/ do |negate, name|
|
228
264
|
expectation = negate ? :should_not : :should
|
229
265
|
patiently do
|
@@ -236,6 +272,11 @@ Then /^I should( not)? see a field "([^"]*)"$/ do |negate, name|
|
|
236
272
|
end
|
237
273
|
end
|
238
274
|
|
275
|
+
# Better way to test for a number of money amount than a `Then I should see`
|
276
|
+
#
|
277
|
+
# Checks that there is unexpected minus sign, decimal places etc.
|
278
|
+
#
|
279
|
+
# See [here](https://makandracards.com/makandra/1225-test-that-a-number-or-money-amount-is-shown-with-cucumber) for details
|
239
280
|
Then /^I should( not)? see the (?:number|amount) ([\-\d,\.]+)(?: (.*?))?$/ do |negate, amount, unit|
|
240
281
|
no_minus = amount.starts_with?('-') ? '' : '[^\\-]'
|
241
282
|
nbsp = 0xC2.chr + 0xA0.chr
|
@@ -246,20 +287,20 @@ Then /^I should( not)? see the (?:number|amount) ([\-\d,\.]+)(?: (.*?))?$/ do |n
|
|
246
287
|
end
|
247
288
|
end
|
248
289
|
|
290
|
+
# Checks "Content-Type" HTTP header
|
249
291
|
Then /^I should get a response with content-type "([^\"]*)"$/ do |expected_content_type|
|
250
292
|
page.response_headers['Content-Type'].should =~ /\A#{Regexp.quote(expected_content_type)}($|;)/
|
251
293
|
end
|
252
294
|
|
295
|
+
# Checks "Content-Disposition" HTTP header
|
253
296
|
Then /^I should get a download with filename "([^\"]*)"$/ do |filename|
|
254
297
|
page.response_headers['Content-Disposition'].should =~ /filename="#{filename}"$/
|
255
298
|
end
|
256
299
|
|
257
|
-
|
258
|
-
Then /^"([^"]*)" should be selected for "([^"]*)"
|
300
|
+
# Checks that a certain option is selected for a text field
|
301
|
+
Then /^"([^"]*)" should be selected for "([^"]*)"$/ do |value, field|
|
259
302
|
patiently do
|
260
|
-
|
261
|
-
field_labeled(field).find(:xpath, ".//option[@selected = 'selected'][text() = '#{value}']").should be_present
|
262
|
-
end
|
303
|
+
field_labeled(field).find(:xpath, ".//option[@selected = 'selected'][text() = '#{value}']").should be_present
|
263
304
|
end
|
264
305
|
end
|
265
306
|
|
@@ -270,23 +311,22 @@ Then /^nothing should be selected for "([^"]*)"?$/ do |field|
|
|
270
311
|
end
|
271
312
|
end
|
272
313
|
|
273
|
-
|
314
|
+
# Checks for the presence of an option in a select
|
315
|
+
Then /^"([^"]*)" should( not)? be an option for "([^"]*)"$/ do |value, negate, field|
|
274
316
|
patiently do
|
275
|
-
|
276
|
-
|
277
|
-
field_labeled(field).first(:xpath, ".//option[text() = '#{value}']").send(expectation, be_present)
|
278
|
-
end
|
317
|
+
expectation = negate ? :should_not : :should
|
318
|
+
field_labeled(field).find(:xpath, ".//option[text() = '#{value}']").send(expectation, be_present)
|
279
319
|
end
|
280
320
|
end
|
281
321
|
|
282
|
-
Then
|
322
|
+
# Like `Then I should see`, but with single instead of double quotes. In case the string contains quotes as well.
|
323
|
+
Then /^(?:|I )should see '([^']*)'$/ do |text|
|
283
324
|
patiently do
|
284
|
-
|
285
|
-
page.should have_content(text)
|
286
|
-
end
|
325
|
+
page.should have_content(text)
|
287
326
|
end
|
288
327
|
end
|
289
328
|
|
329
|
+
# Check that the raw HTML contains a string
|
290
330
|
Then /^I should see "([^\"]*)" in the HTML$/ do |text|
|
291
331
|
patiently do
|
292
332
|
page.body.should include(text)
|
@@ -299,10 +339,12 @@ Then /^I should not see "([^\"]*)" in the HTML$/ do |text|
|
|
299
339
|
end
|
300
340
|
end
|
301
341
|
|
342
|
+
# Checks that status code is 400..599
|
302
343
|
Then /^I should see an error$/ do
|
303
344
|
(400 .. 599).should include(page.status_code)
|
304
345
|
end
|
305
346
|
|
347
|
+
#nodoc
|
306
348
|
Then /^the window should be titled "([^"]*)"$/ do |title|
|
307
349
|
page.should have_css('title', :text => title)
|
308
350
|
end
|
@@ -311,6 +353,11 @@ When /^I reload the page$/ do
|
|
311
353
|
visit current_path
|
312
354
|
end
|
313
355
|
|
356
|
+
# Checks that an element is actually visible, also considering styles
|
357
|
+
# Within a selenium test, the browser is asked whether the element is really visible
|
358
|
+
# In a non-selenium test, we only check for ".hidden", ".invisible" or "style: display:none"
|
359
|
+
#
|
360
|
+
# More details [here](https://makandracards.com/makandra/1049-capybara-check-that-a-page-element-is-hidden-via-css)
|
314
361
|
Then /^"([^\"]+)" should( not)? be visible$/ do |text, negate|
|
315
362
|
case Capybara::current_driver
|
316
363
|
when :selenium, :webkit
|
@@ -365,17 +412,21 @@ Then /^"([^\"]+)" should( not)? be visible$/ do |text, negate|
|
|
365
412
|
end
|
366
413
|
end
|
367
414
|
|
415
|
+
# Click on some text that might not be a link
|
368
416
|
When /^I click on "([^\"]+)"$/ do |text|
|
369
417
|
matcher = ['*', { :text => text }]
|
370
418
|
patiently do
|
371
419
|
element = page.find(:css, *matcher)
|
372
|
-
while better_match = element.
|
420
|
+
while better_match = element.find(:css, *matcher)
|
373
421
|
element = better_match
|
374
422
|
end
|
375
423
|
element.click
|
376
424
|
end
|
377
425
|
end
|
378
426
|
|
427
|
+
# Example:
|
428
|
+
#
|
429
|
+
# Then I should see an element ".page .container"
|
379
430
|
Then /^I should (not )?see an element "([^"]*)"$/ do |negate, selector|
|
380
431
|
expectation = negate ? :should_not : :should
|
381
432
|
patiently do
|
@@ -383,10 +434,18 @@ Then /^I should (not )?see an element "([^"]*)"$/ do |negate, selector|
|
|
383
434
|
end
|
384
435
|
end
|
385
436
|
|
437
|
+
# Checks that the result has content type text/plain
|
386
438
|
Then /^I should get a text response$/ do
|
387
439
|
step 'I should get a response with content-type "text/plain"'
|
388
440
|
end
|
389
441
|
|
442
|
+
# Click a link within an element matching the given selector. Will try to be clever
|
443
|
+
# and disregard elements that don't contain a matching link.
|
444
|
+
#
|
445
|
+
# Example:
|
446
|
+
#
|
447
|
+
# When I follow "Read more" inside any ".text_snippet"
|
448
|
+
#
|
390
449
|
When /^I follow "([^"]*)" inside any "([^"]*)"$/ do |label, selector|
|
391
450
|
node = first("#{selector} a", :text => label)
|
392
451
|
node.click
|
data/spreewald.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = Spreewald::VERSION
|
18
18
|
|
19
|
-
gem.add_runtime_dependency('cucumber-rails'
|
19
|
+
gem.add_runtime_dependency('cucumber-rails')
|
20
20
|
gem.add_runtime_dependency('cucumber')
|
21
21
|
gem.add_runtime_dependency('capybara')
|
22
22
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module DocumentationGenerator
|
2
|
+
|
3
|
+
module CommentExtractor
|
4
|
+
def parse_and_format_comment(comment)
|
5
|
+
comment.strip!
|
6
|
+
comment_lines = comment.split("\n").take_while { |line| line =~ /^\s*#/ }
|
7
|
+
comment_lines && comment_lines.join("\n").gsub(/^\s*# ?/, '')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class StepDefinition
|
12
|
+
|
13
|
+
extend CommentExtractor
|
14
|
+
|
15
|
+
def initialize(definition, comment = nil)
|
16
|
+
@definition = definition
|
17
|
+
@comment = comment
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.try_and_parse(code)
|
21
|
+
definition = code[/^\s*((When|Then|Given|AfterStep).*)do/, 1]
|
22
|
+
return unless definition
|
23
|
+
comment = parse_and_format_comment(code)
|
24
|
+
return if comment =~ /\bnodoc\b/
|
25
|
+
new(definition.strip, comment)
|
26
|
+
end
|
27
|
+
|
28
|
+
def format
|
29
|
+
<<-TEXT
|
30
|
+
* **#{format_definition}**
|
31
|
+
|
32
|
+
#{@comment.gsub(/^/, ' ')}
|
33
|
+
TEXT
|
34
|
+
end
|
35
|
+
|
36
|
+
def format_definition
|
37
|
+
if @definition =~ /AfterStep/
|
38
|
+
@definition[/@\w*/]
|
39
|
+
else
|
40
|
+
@definition.
|
41
|
+
gsub('/^', '').
|
42
|
+
gsub('$/', '').
|
43
|
+
gsub(' ?', ' ').
|
44
|
+
gsub(/\(\[\^\\?"\](\*|\+)\)/, '...').
|
45
|
+
gsub('(?:|I )', 'I ').
|
46
|
+
gsub('?:', '').
|
47
|
+
gsub(/\(\.(\+|\*)\)/, '...')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class StepDefinitionFile
|
53
|
+
|
54
|
+
include CommentExtractor
|
55
|
+
|
56
|
+
def initialize(filename)
|
57
|
+
@filename = filename
|
58
|
+
@code = File.read(filename)
|
59
|
+
@steps = []
|
60
|
+
extract_comment
|
61
|
+
add_steps
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_comment
|
65
|
+
@comment = parse_and_format_comment(@code)
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_steps
|
69
|
+
@code.split("\n\n").each do |block|
|
70
|
+
step = StepDefinition.try_and_parse(block)
|
71
|
+
if step
|
72
|
+
@steps << step
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def format
|
78
|
+
<<-TEXT
|
79
|
+
### #{format_filename}
|
80
|
+
|
81
|
+
#{@comment}
|
82
|
+
|
83
|
+
#{format_steps}
|
84
|
+
TEXT
|
85
|
+
end
|
86
|
+
|
87
|
+
def format_filename
|
88
|
+
@filename.split('/').last
|
89
|
+
end
|
90
|
+
|
91
|
+
def format_steps
|
92
|
+
@steps.collect { |step| step.format }.join("\n\n")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class StepDefinitionsDirectory
|
97
|
+
def initialize(directory)
|
98
|
+
@step_definition_files = []
|
99
|
+
Dir["#{directory}/*.rb"].to_a.sort.each do |filename|
|
100
|
+
next if filename =~ /all_steps/
|
101
|
+
@step_definition_files << StepDefinitionFile.new(filename)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def format
|
106
|
+
@step_definition_files.collect { |step_definition_file| step_definition_file.format }.join("\n\n")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spreewald
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tobias Kraze
|
@@ -26,12 +26,10 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 3
|
30
30
|
segments:
|
31
|
-
- 1
|
32
|
-
- 3
|
33
31
|
- 0
|
34
|
-
version:
|
32
|
+
version: "0"
|
35
33
|
type: :runtime
|
36
34
|
version_requirements: *id001
|
37
35
|
- !ruby/object:Gem::Dependency
|
@@ -89,9 +87,11 @@ files:
|
|
89
87
|
- lib/spreewald_support/github.rb
|
90
88
|
- lib/spreewald_support/mail_finder.rb
|
91
89
|
- lib/spreewald_support/path_selector_fallbacks.rb
|
90
|
+
- lib/spreewald_support/step_fallback.rb
|
92
91
|
- lib/spreewald_support/tolerance_for_selenium_sync_issues.rb
|
93
92
|
- lib/spreewald_support/version.rb
|
94
93
|
- spreewald.gemspec
|
94
|
+
- support/documentation_generator.rb
|
95
95
|
has_rdoc: true
|
96
96
|
homepage: https://github.com/makandra/spreewald
|
97
97
|
licenses: []
|