rsel 0.1.0 → 0.1.1
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/.gitignore +3 -0
- data/.yardopts +12 -2
- data/Rakefile +26 -0
- data/docs/development.md +23 -0
- data/docs/examples.md +27 -0
- data/docs/fitnesse.md +4 -4
- data/docs/history.md +20 -0
- data/lib/rsel/selenium_test.rb +628 -56
- data/lib/rsel/support.rb +150 -0
- data/rsel.gemspec +5 -3
- data/spec/selenium_test_spec.rb +1484 -30
- data/spec/support_spec.rb +261 -0
- data/test/views/index.erb +1 -0
- data/test/views/slowtext.erb +27 -0
- metadata +35 -6
data/.gitignore
CHANGED
data/.yardopts
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
--readme docs/index.md
|
2
2
|
--markup markdown
|
3
3
|
--no-highlight
|
4
|
-
--exclude lib/rsel/selenium.rb
|
5
4
|
lib/rsel/*.rb
|
6
5
|
-
|
7
|
-
docs
|
6
|
+
docs/index.md
|
7
|
+
docs/install.md
|
8
|
+
docs/usage.md
|
9
|
+
docs/fitnesse.md
|
10
|
+
docs/locators.md
|
11
|
+
docs/scoping.md
|
12
|
+
docs/examples.md
|
13
|
+
docs/custom.md
|
14
|
+
docs/scenarios.md
|
15
|
+
docs/development.md
|
16
|
+
docs/todo.md
|
17
|
+
docs/history.md
|
data/Rakefile
CHANGED
@@ -50,6 +50,32 @@ RSpec::Core::RakeTask.new(:spec) do |t|
|
|
50
50
|
t.rspec_opts = ['--color', '--format doc']
|
51
51
|
end
|
52
52
|
|
53
|
+
namespace 'rcov' do
|
54
|
+
desc "Run support spec tests with coverage analysis"
|
55
|
+
RSpec::Core::RakeTask.new(:support) do |t|
|
56
|
+
t.pattern = 'spec/support_spec.rb'
|
57
|
+
t.rspec_opts = ['--color', '--format doc']
|
58
|
+
t.rcov = true
|
59
|
+
t.rcov_opts = [
|
60
|
+
'--exclude /.gem/,/gems/,spec',
|
61
|
+
'--include lib/**/*.rb',
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Run support spec tests with coverage analysis"
|
66
|
+
RSpec::Core::RakeTask.new(:all) do |t|
|
67
|
+
t.pattern = 'spec/**/*.rb'
|
68
|
+
t.rspec_opts = ['--color', '--format doc']
|
69
|
+
t.rcov = true
|
70
|
+
t.rcov_opts = [
|
71
|
+
'--exclude /.gem/,/gems/,spec',
|
72
|
+
'--include lib/**/*.rb',
|
73
|
+
# Ensure the main .rb file gets included
|
74
|
+
'--include-file lib/rsel/selenium_test.rb',
|
75
|
+
]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
53
79
|
namespace 'servers' do
|
54
80
|
desc "Start the Selenium and testapp servers"
|
55
81
|
task :start do
|
data/docs/development.md
CHANGED
@@ -6,6 +6,29 @@ If you would like to contribute to development of Rsel, create a fork of the
|
|
6
6
|
pull requests for any improvements you would like to share.
|
7
7
|
|
8
8
|
|
9
|
+
Prerequisites
|
10
|
+
-------------
|
11
|
+
|
12
|
+
Before developing and testing Rsel, you will need to install some dependencies.
|
13
|
+
Most of these will be handled by simply running:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
from the git clone of Rsel. The nokogiri gem is known to fail if certain XML
|
18
|
+
development headers are missing; if you encounter this, try:
|
19
|
+
|
20
|
+
$ sudo apt-get install libxml2-dev libxslt-dev
|
21
|
+
|
22
|
+
Then re-run `bundle install`. If this still fails, or if you're on a non-Debian OS,
|
23
|
+
consult the nokogiri
|
24
|
+
[installation instructions](http://nokogiri.org/tutorials/installing_nokogiri.html).
|
25
|
+
|
26
|
+
Aside from gem dependencies, you will need to have Java installed in order to
|
27
|
+
run the Selenium server provided with Rsel (in the `test/server` directory).
|
28
|
+
Startup of the server is handled automatically by the Rake tasks during
|
29
|
+
testing; see below.
|
30
|
+
|
31
|
+
|
9
32
|
Testing
|
10
33
|
-------
|
11
34
|
|
data/docs/examples.md
CHANGED
@@ -115,5 +115,32 @@ Tables
|
|
115
115
|
| See title | Editing Eric |
|
116
116
|
| Close browser |
|
117
117
|
|
118
|
+
Conditionals
|
119
|
+
-------------
|
120
|
+
|
121
|
+
!define do_bad_stuff {false}
|
122
|
+
| script | selenium test | http://localhost:8070 |
|
123
|
+
| Open browser |
|
124
|
+
| Maximize browser |
|
125
|
+
| If parameter | ${do_bad_stuff} |
|
126
|
+
| Click | Nonexistent link |
|
127
|
+
| Page loads in | 30 | seconds or less |
|
128
|
+
| If I see | About this site |
|
129
|
+
| Click | About this site |
|
130
|
+
| Page loads in | 30 | seconds or less |
|
131
|
+
| End If |
|
132
|
+
| Otherwise |
|
133
|
+
| If I see | About this site |
|
134
|
+
| Click | About this site |
|
135
|
+
| Page loads in | 30 | seconds or less |
|
136
|
+
| Otherwise |
|
137
|
+
| Click | Nonexistent link |
|
138
|
+
| Page loads in | 30 | seconds or less |
|
139
|
+
| End If |
|
140
|
+
| End If |
|
141
|
+
| See | This site is really cool. |
|
142
|
+
| Do not see | This is a Sinatra webapp for unit testing Rsel. |
|
143
|
+
| Close browser |
|
144
|
+
|
118
145
|
|
119
146
|
Next: [Customization](custom.md)
|
data/docs/fitnesse.md
CHANGED
@@ -4,10 +4,10 @@ FitNesse
|
|
4
4
|
With FitNesse, some initial configuration steps are necessary. Assuming you
|
5
5
|
have a FitNesse wiki-page hierarchy like this:
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
* `FitNesseRoot`
|
8
|
+
* `SeleniumTests`
|
9
|
+
* `SetUp`
|
10
|
+
* `LoginTest`
|
11
11
|
|
12
12
|
Put this in your `SeleniumTests.SetUp` page:
|
13
13
|
|
data/docs/history.md
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
Rsel History
|
2
2
|
============
|
3
3
|
|
4
|
+
0.1.1
|
5
|
+
-----
|
6
|
+
|
7
|
+
- Conditional expressions (if_is, if_i_see, if_parameter, otherwise, end_if)
|
8
|
+
- Temporal visibility (see|do_not_see)_within_seconds
|
9
|
+
- Generic field equality (generic_field_equals)
|
10
|
+
- Generic field fill-in (set_field, set_field_among)
|
11
|
+
- Hash-based field fill-in (set_fields, sef_fields_among)
|
12
|
+
- Hash-based field equality (fields_equal, field_equals_among, fields_equal_among)
|
13
|
+
- Allow visit to accept URLs containing markup
|
14
|
+
- Show error messages in FitNesse: | Show | errors |
|
15
|
+
- row_exists now ignores cell order
|
16
|
+
|
17
|
+
|
18
|
+
0.1.0
|
19
|
+
-----
|
20
|
+
|
21
|
+
- begin_scenario and end_scenario return true
|
22
|
+
|
23
|
+
|
4
24
|
0.0.9
|
5
25
|
-----
|
6
26
|
|
data/lib/rsel/selenium_test.rb
CHANGED
@@ -54,7 +54,7 @@ module Rsel
|
|
54
54
|
#
|
55
55
|
def initialize(url, options={})
|
56
56
|
# Strip HTML tags from URL
|
57
|
-
@url = url
|
57
|
+
@url = strip_tags(url)
|
58
58
|
@browser = Selenium::Client::Driver.new(
|
59
59
|
:host => options[:host] || 'localhost',
|
60
60
|
:port => options[:port] || 4444,
|
@@ -68,6 +68,9 @@ module Rsel
|
|
68
68
|
@stop_on_failure = false
|
69
69
|
end
|
70
70
|
@found_failure = false
|
71
|
+
@conditional_stack = [ true ]
|
72
|
+
# A list of error messages:
|
73
|
+
@errors = []
|
71
74
|
end
|
72
75
|
|
73
76
|
attr_reader :url, :browser, :stop_on_failure, :found_failure
|
@@ -103,15 +106,35 @@ module Rsel
|
|
103
106
|
|
104
107
|
|
105
108
|
# Stop the session and close the browser window.
|
109
|
+
# Show error messages in an exception if requested.
|
106
110
|
#
|
107
111
|
# @example
|
108
112
|
# | Close browser |
|
113
|
+
# | Close browser | and show errors |
|
114
|
+
# | Close browser | without showing errors |
|
109
115
|
#
|
110
|
-
def close_browser
|
116
|
+
def close_browser(show_errors='')
|
111
117
|
@browser.close_current_browser_session
|
118
|
+
# Show errors in an exception if requested.
|
119
|
+
if (!(/not|without/i === show_errors) && @errors.length > 0)
|
120
|
+
raise StopTestStepFailed, @errors.join("\n").gsub('<','<')
|
121
|
+
end
|
112
122
|
return true
|
113
123
|
end
|
114
124
|
|
125
|
+
# Show any current error messages.
|
126
|
+
# Also clears the error message log.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# | Show | errors |
|
130
|
+
#
|
131
|
+
# @since 0.1.1
|
132
|
+
#
|
133
|
+
def errors
|
134
|
+
current_errors = @errors
|
135
|
+
@errors = []
|
136
|
+
return current_errors.join("\n")
|
137
|
+
end
|
115
138
|
|
116
139
|
# Begin a new scenario, and forget about any previous failures.
|
117
140
|
# This allows you to modularize your tests into standalone sections
|
@@ -125,6 +148,7 @@ module Rsel
|
|
125
148
|
#
|
126
149
|
def begin_scenario
|
127
150
|
@found_failure = false
|
151
|
+
@errors = []
|
128
152
|
return true
|
129
153
|
end
|
130
154
|
|
@@ -153,9 +177,9 @@ module Rsel
|
|
153
177
|
# | Visit | /software |
|
154
178
|
#
|
155
179
|
def visit(path_or_url)
|
156
|
-
return
|
180
|
+
return skip_status if skip_step?
|
157
181
|
fail_on_exception do
|
158
|
-
@browser.open(path_or_url)
|
182
|
+
@browser.open(strip_tags(path_or_url))
|
159
183
|
end
|
160
184
|
end
|
161
185
|
|
@@ -166,7 +190,7 @@ module Rsel
|
|
166
190
|
# | Click back |
|
167
191
|
#
|
168
192
|
def click_back
|
169
|
-
return
|
193
|
+
return skip_status if skip_step?
|
170
194
|
fail_on_exception do
|
171
195
|
@browser.go_back
|
172
196
|
end
|
@@ -179,7 +203,7 @@ module Rsel
|
|
179
203
|
# | Refresh page |
|
180
204
|
#
|
181
205
|
def refresh_page
|
182
|
-
return
|
206
|
+
return skip_status if skip_step?
|
183
207
|
fail_on_exception do
|
184
208
|
@browser.refresh
|
185
209
|
end
|
@@ -206,7 +230,7 @@ module Rsel
|
|
206
230
|
# | See | Welcome, Marcus |
|
207
231
|
#
|
208
232
|
def see(text)
|
209
|
-
return
|
233
|
+
return skip_status if skip_step?
|
210
234
|
pass_if @browser.text?(text)
|
211
235
|
end
|
212
236
|
|
@@ -220,11 +244,59 @@ module Rsel
|
|
220
244
|
# | Do not see | Take a hike |
|
221
245
|
#
|
222
246
|
def do_not_see(text)
|
223
|
-
return
|
247
|
+
return skip_status if skip_step?
|
224
248
|
pass_if !@browser.text?(text)
|
225
249
|
end
|
226
250
|
|
227
251
|
|
252
|
+
# Temporally ensure text is present or absent
|
253
|
+
|
254
|
+
# Ensure that the given text appears on the current page, eventually.
|
255
|
+
#
|
256
|
+
# @param [String] text
|
257
|
+
# Plain text that should be appear on or visible on the current page
|
258
|
+
# @param [String] seconds
|
259
|
+
# Integer number of seconds to wait.
|
260
|
+
#
|
261
|
+
# @example
|
262
|
+
# | Click | ajax_login | button |
|
263
|
+
# | See | Welcome back, Marcus | within | 10 | seconds |
|
264
|
+
# | Note | The following uses the Selenium default timeout: |
|
265
|
+
# | See | How do you feel? | within seconds |
|
266
|
+
#
|
267
|
+
# @since 0.1.1
|
268
|
+
#
|
269
|
+
def see_within_seconds(text, seconds=-1)
|
270
|
+
return skip_status if skip_step?
|
271
|
+
seconds = @browser.default_timeout_in_seconds if seconds == -1
|
272
|
+
pass_if !(Integer(seconds)+1).times{ break if (@browser.text?(text) rescue false); sleep 1 }
|
273
|
+
# This would be better if it worked:
|
274
|
+
# pass_if @browser.wait_for(:text => text, :timeout_in_seconds => seconds);
|
275
|
+
end
|
276
|
+
|
277
|
+
# Ensure that the given text does not appear on the current page, eventually.
|
278
|
+
#
|
279
|
+
# @param [String] text
|
280
|
+
# Plain text that should disappear from or not be present on the current page
|
281
|
+
# @param [String] seconds
|
282
|
+
# Integer number of seconds to wait.
|
283
|
+
#
|
284
|
+
# @example
|
285
|
+
# | Click | close | button | !{within:popup_ad} |
|
286
|
+
# | Do not see | advertisement | within | 10 | seconds |
|
287
|
+
#
|
288
|
+
# @since 0.1.1
|
289
|
+
#
|
290
|
+
def do_not_see_within_seconds(text, seconds=-1)
|
291
|
+
return skip_status if skip_step?
|
292
|
+
seconds = @browser.default_timeout_in_seconds if seconds == -1
|
293
|
+
pass_if !(Integer(seconds)+1).times{ break if (!@browser.text?(text) rescue false); sleep 1 }
|
294
|
+
# This would be better if it worked:
|
295
|
+
# pass_if @browser.wait_for(:no_text => text, :timeout_in_seconds => seconds);
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
|
228
300
|
# Ensure that the current page has the given title text.
|
229
301
|
#
|
230
302
|
# @param [String] title
|
@@ -234,8 +306,8 @@ module Rsel
|
|
234
306
|
# | See title | Our Homepage |
|
235
307
|
#
|
236
308
|
def see_title(title)
|
237
|
-
return
|
238
|
-
pass_if @browser.get_title == title
|
309
|
+
return skip_status if skip_step?
|
310
|
+
pass_if @browser.get_title == title, "Page title is '#{@browser.get_title}', not '#{title}'"
|
239
311
|
end
|
240
312
|
|
241
313
|
|
@@ -248,7 +320,7 @@ module Rsel
|
|
248
320
|
# | Do not see title | Someone else's homepage |
|
249
321
|
#
|
250
322
|
def do_not_see_title(title)
|
251
|
-
return
|
323
|
+
return skip_status if skip_step?
|
252
324
|
pass_if !(@browser.get_title == title)
|
253
325
|
end
|
254
326
|
|
@@ -267,7 +339,7 @@ module Rsel
|
|
267
339
|
# @since 0.0.2
|
268
340
|
#
|
269
341
|
def link_exists(locator, scope={})
|
270
|
-
return
|
342
|
+
return skip_status if skip_step?
|
271
343
|
pass_if @browser.element?(loc(locator, 'link', scope))
|
272
344
|
end
|
273
345
|
|
@@ -286,28 +358,30 @@ module Rsel
|
|
286
358
|
# @since 0.0.2
|
287
359
|
#
|
288
360
|
def button_exists(locator, scope={})
|
289
|
-
return
|
361
|
+
return skip_status if skip_step?
|
290
362
|
pass_if @browser.element?(loc(locator, 'button', scope))
|
291
363
|
end
|
292
364
|
|
293
365
|
|
294
366
|
# Ensure that a table row with the given cell values exists.
|
367
|
+
# Order does not matter as of v0.1.1.
|
295
368
|
#
|
296
369
|
# @param [String] cells
|
297
|
-
# Comma-separated cell values you expect to see
|
370
|
+
# Comma-separated cell values you expect to see. If you need to include a
|
371
|
+
# literal comma, use the {#escape_for_hash} syntax, \'.
|
298
372
|
#
|
299
373
|
# @example
|
300
374
|
# | Row exists | First, Middle, Last, Email |
|
301
|
-
# | Row | First,
|
375
|
+
# | Row | First, Last, Middle, Email | exists |
|
302
376
|
#
|
303
377
|
# @since 0.0.3
|
304
378
|
#
|
305
379
|
def row_exists(cells)
|
306
|
-
return
|
307
|
-
|
308
|
-
pass_if @browser.element?("xpath=#{row.to_s}")
|
380
|
+
return skip_status if skip_step?
|
381
|
+
pass_if @browser.element?("xpath=#{xpath_row_containing(cells.split(/, */).map{|s| escape_for_hash(s)})}")
|
309
382
|
end
|
310
383
|
|
384
|
+
#
|
311
385
|
|
312
386
|
# Type a value into the given field. Passes if the field exists and is
|
313
387
|
# editable. Fails if the field is not found, or is not editable.
|
@@ -324,7 +398,7 @@ module Rsel
|
|
324
398
|
# | Type | Dale | into | First name | field | !{within:contact} |
|
325
399
|
#
|
326
400
|
def type_into_field(text, locator, scope={})
|
327
|
-
return
|
401
|
+
return skip_status if skip_step?
|
328
402
|
field = loc(locator, 'field', scope)
|
329
403
|
fail_on_exception do
|
330
404
|
ensure_editable(field) && @browser.type(field, text)
|
@@ -346,7 +420,7 @@ module Rsel
|
|
346
420
|
# | Fill in | First name | with | Eric |
|
347
421
|
#
|
348
422
|
def fill_in_with(locator, text, scope={})
|
349
|
-
return
|
423
|
+
return skip_status if skip_step?
|
350
424
|
type_into_field(text, locator, scope)
|
351
425
|
end
|
352
426
|
|
@@ -363,13 +437,13 @@ module Rsel
|
|
363
437
|
# | Field | First name | contains | Eric |
|
364
438
|
#
|
365
439
|
def field_contains(locator, text, scope={})
|
366
|
-
return
|
440
|
+
return skip_status if skip_step?
|
367
441
|
begin
|
368
442
|
field = @browser.field(loc(locator, 'field', scope))
|
369
443
|
rescue
|
370
|
-
failure
|
444
|
+
failure "Can't identify field #{locator}"
|
371
445
|
else
|
372
|
-
pass_if field.include?(text)
|
446
|
+
pass_if field.include?(text), "Field contains '#{field}', not '#{text}'"
|
373
447
|
end
|
374
448
|
end
|
375
449
|
|
@@ -387,13 +461,13 @@ module Rsel
|
|
387
461
|
# | Field | First name | equals; | Eric | !{within:contact} |
|
388
462
|
#
|
389
463
|
def field_equals(locator, text, scope={})
|
390
|
-
return
|
464
|
+
return skip_status if skip_step?
|
391
465
|
begin
|
392
466
|
field = @browser.field(loc(locator, 'field', scope))
|
393
467
|
rescue
|
394
|
-
failure
|
468
|
+
failure "Can't identify field #{locator}"
|
395
469
|
else
|
396
|
-
pass_if field == text
|
470
|
+
pass_if field == text, "Field contains '#{field}', not '#{text}'"
|
397
471
|
end
|
398
472
|
end
|
399
473
|
|
@@ -409,7 +483,7 @@ module Rsel
|
|
409
483
|
# | Click; | Logout | !{within:header} |
|
410
484
|
#
|
411
485
|
def click(locator, scope={})
|
412
|
-
return
|
486
|
+
return skip_status if skip_step?
|
413
487
|
fail_on_exception do
|
414
488
|
@browser.click(loc(locator, 'link_or_button', scope))
|
415
489
|
end
|
@@ -429,7 +503,7 @@ module Rsel
|
|
429
503
|
# | Click | Edit | link | !{in_row:Eric} |
|
430
504
|
#
|
431
505
|
def click_link(locator, scope={})
|
432
|
-
return
|
506
|
+
return skip_status if skip_step?
|
433
507
|
fail_on_exception do
|
434
508
|
@browser.click(loc(locator, 'link', scope))
|
435
509
|
end
|
@@ -451,7 +525,7 @@ module Rsel
|
|
451
525
|
# | Click | Search | button | !{within:customers} |
|
452
526
|
#
|
453
527
|
def click_button(locator, scope={})
|
454
|
-
return
|
528
|
+
return skip_status if skip_step?
|
455
529
|
button = loc(locator, 'button', scope)
|
456
530
|
fail_on_exception do
|
457
531
|
ensure_editable(button) && @browser.click(button)
|
@@ -474,7 +548,7 @@ module Rsel
|
|
474
548
|
# | Enable | Send me spam | checkbox | !{within:opt_in} |
|
475
549
|
#
|
476
550
|
def enable_checkbox(locator, scope={})
|
477
|
-
return
|
551
|
+
return skip_status if skip_step?
|
478
552
|
cb = loc(locator, 'checkbox', scope)
|
479
553
|
fail_on_exception do
|
480
554
|
ensure_editable(cb) && checkbox_is_disabled(cb) && @browser.click(cb)
|
@@ -496,7 +570,7 @@ module Rsel
|
|
496
570
|
# | Disable | Send me spam | checkbox | !{within:opt_in} |
|
497
571
|
#
|
498
572
|
def disable_checkbox(locator, scope={})
|
499
|
-
return
|
573
|
+
return skip_status if skip_step?
|
500
574
|
cb = loc(locator, 'checkbox', scope)
|
501
575
|
fail_on_exception do
|
502
576
|
ensure_editable(cb) && checkbox_is_enabled(cb) && @browser.click(cb)
|
@@ -516,12 +590,12 @@ module Rsel
|
|
516
590
|
# | Checkbox | send me spam | is enabled | !{within:opt_in} |
|
517
591
|
#
|
518
592
|
def checkbox_is_enabled(locator, scope={})
|
519
|
-
return
|
593
|
+
return skip_status if skip_step?
|
520
594
|
xp = loc(locator, 'checkbox', scope)
|
521
595
|
begin
|
522
596
|
enabled = @browser.checked?(xp)
|
523
597
|
rescue
|
524
|
-
failure
|
598
|
+
failure "Can't identify checkbox #{locator}"
|
525
599
|
else
|
526
600
|
return enabled
|
527
601
|
end
|
@@ -542,12 +616,12 @@ module Rsel
|
|
542
616
|
# @since 0.0.4
|
543
617
|
#
|
544
618
|
def radio_is_enabled(locator, scope={})
|
545
|
-
return
|
619
|
+
return skip_status if skip_step?
|
546
620
|
xp = loc(locator, 'radio_button', scope)
|
547
621
|
begin
|
548
622
|
enabled = @browser.checked?(xp)
|
549
623
|
rescue
|
550
|
-
failure
|
624
|
+
failure "Can't identify radio #{locator}"
|
551
625
|
else
|
552
626
|
return enabled
|
553
627
|
end
|
@@ -566,12 +640,12 @@ module Rsel
|
|
566
640
|
# | Checkbox | send me spam | is disabled | !{within:opt_in} |
|
567
641
|
#
|
568
642
|
def checkbox_is_disabled(locator, scope={})
|
569
|
-
return
|
643
|
+
return skip_status if skip_step?
|
570
644
|
xp = loc(locator, 'checkbox', scope)
|
571
645
|
begin
|
572
646
|
enabled = @browser.checked?(xp)
|
573
647
|
rescue
|
574
|
-
failure
|
648
|
+
failure "Can't identify checkbox #{locator}"
|
575
649
|
else
|
576
650
|
return !enabled
|
577
651
|
end
|
@@ -592,12 +666,12 @@ module Rsel
|
|
592
666
|
# @since 0.0.4
|
593
667
|
#
|
594
668
|
def radio_is_disabled(locator, scope={})
|
595
|
-
return
|
669
|
+
return skip_status if skip_step?
|
596
670
|
xp = loc(locator, 'radio_button', scope)
|
597
671
|
begin
|
598
672
|
enabled = @browser.checked?(xp)
|
599
673
|
rescue
|
600
|
-
failure
|
674
|
+
failure "Can't identify radio #{locator}"
|
601
675
|
else
|
602
676
|
return !enabled
|
603
677
|
end
|
@@ -617,7 +691,7 @@ module Rsel
|
|
617
691
|
# | Select | female | radio | !{within:gender} |
|
618
692
|
#
|
619
693
|
def select_radio(locator, scope={})
|
620
|
-
return
|
694
|
+
return skip_status if skip_step?
|
621
695
|
radio = loc(locator, 'radio_button', scope)
|
622
696
|
fail_on_exception do
|
623
697
|
ensure_editable(radio) && @browser.click(radio)
|
@@ -639,7 +713,7 @@ module Rsel
|
|
639
713
|
# | Select | Tall | from dropdown | Height |
|
640
714
|
#
|
641
715
|
def select_from_dropdown(option, locator, scope={})
|
642
|
-
return
|
716
|
+
return skip_status if skip_step?
|
643
717
|
dropdown = loc(locator, 'select', scope)
|
644
718
|
fail_on_exception do
|
645
719
|
ensure_editable(dropdown) && @browser.select(dropdown, option)
|
@@ -660,7 +734,7 @@ module Rsel
|
|
660
734
|
# @since 0.0.2
|
661
735
|
#
|
662
736
|
def dropdown_includes(locator, option, scope={})
|
663
|
-
return
|
737
|
+
return skip_status if skip_step?
|
664
738
|
# TODO: Apply scope
|
665
739
|
dropdown = XPath::HTML.select(locator)
|
666
740
|
opt = dropdown[XPath::HTML.option(option)]
|
@@ -682,13 +756,13 @@ module Rsel
|
|
682
756
|
# @since 0.0.2
|
683
757
|
#
|
684
758
|
def dropdown_equals(locator, option, scope={})
|
685
|
-
return
|
759
|
+
return skip_status if skip_step?
|
686
760
|
begin
|
687
761
|
selected = @browser.get_selected_label(loc(locator, 'select', scope))
|
688
762
|
rescue
|
689
|
-
failure
|
763
|
+
failure "Can't identify dropdown #{locator}"
|
690
764
|
else
|
691
|
-
|
765
|
+
pass_if selected == option, "Dropdown equals '#{selected}', not '#{option}'"
|
692
766
|
end
|
693
767
|
end
|
694
768
|
|
@@ -702,7 +776,7 @@ module Rsel
|
|
702
776
|
# | Pause | 5 | seconds |
|
703
777
|
#
|
704
778
|
def pause_seconds(seconds)
|
705
|
-
return
|
779
|
+
return skip_status if skip_step?
|
706
780
|
sleep seconds.to_i
|
707
781
|
return true
|
708
782
|
end
|
@@ -719,13 +793,350 @@ module Rsel
|
|
719
793
|
# | Page loads in | 10 | seconds or less |
|
720
794
|
#
|
721
795
|
def page_loads_in_seconds_or_less(seconds)
|
722
|
-
return
|
796
|
+
return skip_status if skip_step?
|
723
797
|
fail_on_exception do
|
724
798
|
@browser.wait_for_page_to_load(seconds)
|
725
799
|
end
|
726
800
|
end
|
727
801
|
|
728
802
|
|
803
|
+
# A generic way to fill in any field, of any type. (Just about.)
|
804
|
+
# Kind of nasty since it needs to use Javascript on the page.
|
805
|
+
#
|
806
|
+
# Types accepted:
|
807
|
+
#
|
808
|
+
# * a*
|
809
|
+
# * button*
|
810
|
+
# * input
|
811
|
+
# * type=button*
|
812
|
+
# * type=checkbox
|
813
|
+
# * type=image*
|
814
|
+
# * type=radio*
|
815
|
+
# * type=reset*
|
816
|
+
# * type=submit*
|
817
|
+
# * type=text
|
818
|
+
# * select
|
819
|
+
# * textarea
|
820
|
+
#
|
821
|
+
# \* Value is ignored: this control type is just clicked/selected.
|
822
|
+
#
|
823
|
+
# @param [String] locator
|
824
|
+
# Label, name, or id of the field control. Identification by
|
825
|
+
# non-Selenium methods may not work for some links and buttons.
|
826
|
+
# @param [String] value
|
827
|
+
# Value you want to set the field to. (Default: empty string.)
|
828
|
+
# Parsed by {#string_is_true?}
|
829
|
+
#
|
830
|
+
# @since 0.1.1
|
831
|
+
#
|
832
|
+
def set_field(locator, value='', scope={})
|
833
|
+
return skip_status if skip_step?
|
834
|
+
fail_on_exception do
|
835
|
+
# First, use Javascript to find out what the field is.
|
836
|
+
begin
|
837
|
+
loceval = loc(locator, 'field', scope)
|
838
|
+
rescue
|
839
|
+
loceval = loc(locator, 'link_or_button', scope)
|
840
|
+
end
|
841
|
+
|
842
|
+
case tagname(loceval)
|
843
|
+
when 'input.text', /^textarea\./
|
844
|
+
return type_into_field(value, loceval)
|
845
|
+
when 'input.radio'
|
846
|
+
return select_radio(loceval)
|
847
|
+
when 'input.checkbox'
|
848
|
+
if string_is_true?(value)
|
849
|
+
return enable_checkbox(loceval)
|
850
|
+
else
|
851
|
+
return disable_checkbox(loceval)
|
852
|
+
end
|
853
|
+
when /^select\./
|
854
|
+
return select_from_dropdown(value, loceval)
|
855
|
+
when /^(a|button)\./,'input.button','input.submit','input.image','input.reset'
|
856
|
+
return click(loceval)
|
857
|
+
else
|
858
|
+
#raise ArgumentError, "Unidentified field #{locator}."
|
859
|
+
return failure("Unidentified field #{locator}.")
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
|
865
|
+
# Set a value (with {#set_field}) in the named field, based on the given
|
866
|
+
# name/value pairs. Uses {#escape_for_hash} to allow certain characters in
|
867
|
+
# FitNesse.
|
868
|
+
#
|
869
|
+
# @param [String] field
|
870
|
+
# A Locator or a name listed in the ids hash below. If a name listed in
|
871
|
+
# the ids below, this field is case-insensitive.
|
872
|
+
# @param [String] value
|
873
|
+
# Plain text to go into a field
|
874
|
+
# @param ids
|
875
|
+
# A hash mapping common names to Locators. (Optional, but redundant
|
876
|
+
# without it) The hash keys are case-insensitive.
|
877
|
+
#
|
878
|
+
# @since 0.1.1
|
879
|
+
#
|
880
|
+
def set_field_among(field, value, ids={}, scope={})
|
881
|
+
return skip_status if skip_step?
|
882
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
883
|
+
ids = {} if ids == ""
|
884
|
+
|
885
|
+
normalize_ids(ids)
|
886
|
+
|
887
|
+
if ids[field.downcase]
|
888
|
+
return set_field(escape_for_hash(ids[field.downcase]), value, scope)
|
889
|
+
else
|
890
|
+
return set_field(field, value, scope)
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Set values (with {#set_field}) in the named fields of a hash, based on the
|
895
|
+
# given name/value pairs. Uses {#escape_for_hash} to allow certain
|
896
|
+
# characters in FitNesse. Note: Order of entries is not guaranteed, and
|
897
|
+
# depends on the version of Ruby on your server!
|
898
|
+
#
|
899
|
+
# @param fields
|
900
|
+
# A key-value hash where the keys are Locators (case-sensitive) and the
|
901
|
+
# values are the string values you want in the fields.
|
902
|
+
#
|
903
|
+
# @since 0.1.1
|
904
|
+
#
|
905
|
+
def set_fields(fields={}, scope={})
|
906
|
+
return skip_status if skip_step?
|
907
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
908
|
+
fields = {} if fields == ""
|
909
|
+
fields.each do |key, value|
|
910
|
+
key_esc = escape_for_hash(key.to_s)
|
911
|
+
value_esc = escape_for_hash(value.to_s)
|
912
|
+
unless set_field(key_esc, value_esc, scope)
|
913
|
+
return failure "Failed to set field #{key_esc} to #{value_esc}"
|
914
|
+
end
|
915
|
+
end
|
916
|
+
return true
|
917
|
+
end
|
918
|
+
|
919
|
+
# Set values (with {#set_field}) in the named fields, based on the given
|
920
|
+
# name/value pairs, and with mapping of names in the ids field. Uses
|
921
|
+
# {#escape_for_hash} to allow certain characters in FitNesse. Note: Order
|
922
|
+
# of entries is not guaranteed, and depends on the version of Ruby on your
|
923
|
+
# server!
|
924
|
+
#
|
925
|
+
# @param fields
|
926
|
+
# A key-value hash where the keys are keys of the ids hash
|
927
|
+
# (case-insensitive), or Locators (case-sensitive), and the values are
|
928
|
+
# the string values you want in the fields.
|
929
|
+
# @param ids
|
930
|
+
# A hash mapping common names to Locators. (Optional, but redundant
|
931
|
+
# without it) The hash keys are case-insensitive.
|
932
|
+
#
|
933
|
+
# @example
|
934
|
+
# Suppose you have a nasty form whose fields have nasty locators. Suppose
|
935
|
+
# further that you want to fill in this form, many times, filling in
|
936
|
+
# different fields different ways. Begin by creating a Scenario table:
|
937
|
+
#
|
938
|
+
# | scenario | Set nasty form fields | values |
|
939
|
+
# | Set | @values | fields among | !{Name:id=nasty_field_name_1,Email:id=nasty_field_name_2,E-mail:id=nasty_field_name_2,Send me spam:id=nasty_checkbox_name_1} |
|
940
|
+
#
|
941
|
+
# Using that you can now say something like:
|
942
|
+
#
|
943
|
+
# | Set nasty form fields | !{Name:Ken,email:ken@kensaddress.com,send me spam: no} |
|
944
|
+
#
|
945
|
+
# Or:
|
946
|
+
#
|
947
|
+
# | Set nasty form fields | !{Name:Ken,Send me Spam: no} |
|
948
|
+
#
|
949
|
+
# Or:
|
950
|
+
#
|
951
|
+
# | Set nasty form fields | !{name:Ken,e-mail:,SEND ME SPAM: yes} |
|
952
|
+
#
|
953
|
+
# @since 0.1.1
|
954
|
+
#
|
955
|
+
def set_fields_among(fields={}, ids={}, scope={})
|
956
|
+
return skip_status if skip_step?
|
957
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
958
|
+
ids = {} if ids == ""
|
959
|
+
fields = {} if fields == ""
|
960
|
+
|
961
|
+
fields.each do |key, value|
|
962
|
+
key_esc = escape_for_hash(key.to_s)
|
963
|
+
value_esc = escape_for_hash(value.to_s)
|
964
|
+
unless set_field_among(key_esc, value_esc, ids, scope)
|
965
|
+
return failure("Failed to set #{key_esc} (#{ids[key_esc]}) to #{value_esc}")
|
966
|
+
end
|
967
|
+
end
|
968
|
+
return true
|
969
|
+
end
|
970
|
+
|
971
|
+
# A generic way to check any field, of any type. (Just about.) Kind of
|
972
|
+
# nasty since it needs to use Javascript on the page.
|
973
|
+
#
|
974
|
+
# Types accepted:
|
975
|
+
#
|
976
|
+
# * a*
|
977
|
+
# * button*
|
978
|
+
# * input
|
979
|
+
# * type=button*
|
980
|
+
# * type=checkbox
|
981
|
+
# * type=image*
|
982
|
+
# * type=radio*
|
983
|
+
# * type=reset*
|
984
|
+
# * type=submit*
|
985
|
+
# * type=text
|
986
|
+
# * select
|
987
|
+
# * textarea
|
988
|
+
#
|
989
|
+
# \* Value is ignored: this control type is just clicked/selected.
|
990
|
+
#
|
991
|
+
# @param [String] locator
|
992
|
+
# Label, name, or id of the field control. Identification by
|
993
|
+
# non-Selenium methods may not work for some links and buttons.
|
994
|
+
# @param [String] value
|
995
|
+
# Value you want to verify the field equal to. (Default: empty string.)
|
996
|
+
# Parsed by {#string_is_true?}
|
997
|
+
#
|
998
|
+
# @since 0.1.1
|
999
|
+
#
|
1000
|
+
def generic_field_equals(locator, value='', scope={})
|
1001
|
+
return skip_status if skip_step?
|
1002
|
+
fail_on_exception do
|
1003
|
+
# First, use Javascript to find out what the field is.
|
1004
|
+
begin
|
1005
|
+
loceval = loc(locator, 'field', scope)
|
1006
|
+
rescue
|
1007
|
+
loceval = loc(locator, 'link_or_button', scope)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
case tagname(loceval)
|
1011
|
+
when 'input.text', /^textarea\./
|
1012
|
+
return field_equals(loceval, value)
|
1013
|
+
when 'input.radio'
|
1014
|
+
if string_is_true?(value)
|
1015
|
+
return radio_is_enabled(loceval)
|
1016
|
+
else
|
1017
|
+
return radio_is_disabled(loceval)
|
1018
|
+
end
|
1019
|
+
when 'input.checkbox'
|
1020
|
+
if string_is_true?(value)
|
1021
|
+
return checkbox_is_enabled(loceval)
|
1022
|
+
else
|
1023
|
+
return checkbox_is_disabled(loceval)
|
1024
|
+
end
|
1025
|
+
when /^select\./
|
1026
|
+
return dropdown_equals(loceval, value)
|
1027
|
+
else
|
1028
|
+
#raise ArgumentError, "Unidentified field #{locator}."
|
1029
|
+
return failure("Unidentified field for comparison: #{locator}.")
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
|
1035
|
+
def tagname(loceval)
|
1036
|
+
return @browser.get_eval(
|
1037
|
+
'var loceval=this.browserbot.findElement("' +
|
1038
|
+
loceval + '");loceval.tagName+"."+loceval.type').downcase
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
# Check a value (with {#set_field}) in the named field, based on the given
|
1042
|
+
# name/value pairs. Uses {#escape_for_hash} to allow certain characters in
|
1043
|
+
# FitNesse.
|
1044
|
+
#
|
1045
|
+
# @param [String] field
|
1046
|
+
# A Locator or a name listed in the ids hash below. If a name listed in
|
1047
|
+
# the ids below, this field is case-insensitive.
|
1048
|
+
# @param [String] value
|
1049
|
+
# Plain text to go into a field
|
1050
|
+
# @param ids
|
1051
|
+
# A hash mapping common names to Locators. (Optional, but redundant
|
1052
|
+
# without it) The hash keys are case-insensitive.
|
1053
|
+
#
|
1054
|
+
# @since 0.1.1
|
1055
|
+
#
|
1056
|
+
def field_equals_among(field, value, ids={}, scope={})
|
1057
|
+
return skip_status if skip_step?
|
1058
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
1059
|
+
ids = {} if ids == ""
|
1060
|
+
|
1061
|
+
normalize_ids(ids)
|
1062
|
+
|
1063
|
+
if ids[field.downcase]
|
1064
|
+
return generic_field_equals(escape_for_hash(ids[field.downcase]), value, scope)
|
1065
|
+
else
|
1066
|
+
return generic_field_equals(field, value, scope)
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
# Check values (with {#set_field}) in the named fields of a hash, based on
|
1071
|
+
# the given name/value pairs. Uses {#escape_for_hash} to allow certain
|
1072
|
+
# characters in FitNesse. Note: Order of entries is not guaranteed, and
|
1073
|
+
# depends on the version of Ruby on your server!
|
1074
|
+
#
|
1075
|
+
# @param fields
|
1076
|
+
# A key-value hash where the keys are Locators (case-sensitive) and the
|
1077
|
+
# values are the string values you want in the fields.
|
1078
|
+
#
|
1079
|
+
# @since 0.1.1
|
1080
|
+
#
|
1081
|
+
def fields_equal(fields={}, scope={})
|
1082
|
+
return skip_status if skip_step?
|
1083
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
1084
|
+
fields = {} if fields == ""
|
1085
|
+
fields.keys.each do |field|
|
1086
|
+
return failure unless generic_field_equals(escape_for_hash(field.to_s), escape_for_hash(fields[field]), scope)
|
1087
|
+
end
|
1088
|
+
return true
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Check values (with {#set_field}) in the named fields, based on the given
|
1092
|
+
# name/value pairs, and with mapping of names in the ids field. Uses
|
1093
|
+
# {#escape_for_hash} to allow certain characters in FitNesse. Note: Order
|
1094
|
+
# of entries is not guaranteed, and depends on the version of Ruby on your
|
1095
|
+
# server!
|
1096
|
+
#
|
1097
|
+
# @param fields
|
1098
|
+
# A key-value hash where the keys are keys of the ids hash
|
1099
|
+
# (case-insensitive), or Locators (case-sensitive),
|
1100
|
+
# and the values are the string values you want in the fields.
|
1101
|
+
# @param ids
|
1102
|
+
# A hash mapping common names to Locators. (Optional, but redundant
|
1103
|
+
# without it) The hash keys are case-insensitive.
|
1104
|
+
#
|
1105
|
+
# @example
|
1106
|
+
# Suppose you have a nasty form whose fields have nasty locators.
|
1107
|
+
# Suppose further that you want to fill in this form, many times, filling
|
1108
|
+
# in different fields different ways.
|
1109
|
+
# Begin by creating a Scenario table:
|
1110
|
+
#
|
1111
|
+
# | scenario | Check nasty form fields | values |
|
1112
|
+
# | fields equal | @values | among | !{Name:id=nasty_field_name_1,Email:id=nasty_field_name_2,E-mail:id=nasty_field_name_2,Send me spam:id=nasty_checkbox_name_1} |
|
1113
|
+
#
|
1114
|
+
# Using that you can now say something like:
|
1115
|
+
#
|
1116
|
+
# | Check nasty form fields | !{Name:Ken,email:ken@kensaddress.com,send me spam: no} |
|
1117
|
+
#
|
1118
|
+
# Or:
|
1119
|
+
#
|
1120
|
+
# | Check nasty form fields | !{Name:Ken,Send me Spam: no} |
|
1121
|
+
#
|
1122
|
+
# Or:
|
1123
|
+
#
|
1124
|
+
# | Check nasty form fields | !{name:Ken,e-mail:,SEND ME SPAM: yes} |
|
1125
|
+
#
|
1126
|
+
# @since 0.1.1
|
1127
|
+
#
|
1128
|
+
def fields_equal_among(fields={}, ids={}, scope={})
|
1129
|
+
return skip_status if skip_step?
|
1130
|
+
# FitNesse passes in "" for an empty field. Fix it.
|
1131
|
+
ids = {} if ids == ""
|
1132
|
+
fields = {} if fields == ""
|
1133
|
+
|
1134
|
+
fields.keys.each do |field|
|
1135
|
+
return failure unless field_equals_among(escape_for_hash(field.to_s), escape_for_hash(fields[field]), ids, scope)
|
1136
|
+
end
|
1137
|
+
return true
|
1138
|
+
end
|
1139
|
+
|
729
1140
|
# Invoke a missing method. If a method is called on a SeleniumTest
|
730
1141
|
# instance, and that method is not explicitly defined, this method
|
731
1142
|
# will check to see whether the underlying Selenium::Client::Driver
|
@@ -735,15 +1146,18 @@ module Rsel
|
|
735
1146
|
# @since 0.0.6
|
736
1147
|
#
|
737
1148
|
def method_missing(method, *args, &block)
|
1149
|
+
return skip_status if skip_step?
|
738
1150
|
if @browser.respond_to?(method)
|
739
1151
|
begin
|
740
1152
|
result = @browser.send(method, *args, &block)
|
741
1153
|
rescue
|
742
|
-
failure
|
1154
|
+
failure "Method #{method} error"
|
743
1155
|
else
|
744
1156
|
# The method call succeeded; did it return true or false?
|
745
|
-
return
|
746
|
-
#
|
1157
|
+
return failure if result == false
|
1158
|
+
# If a string, return that. We might Check or Show it.
|
1159
|
+
return result if result == true || (result.is_a? String)
|
1160
|
+
# Not a Boolean return value or string--assume passing
|
747
1161
|
return true
|
748
1162
|
end
|
749
1163
|
else
|
@@ -767,6 +1181,144 @@ module Rsel
|
|
767
1181
|
end
|
768
1182
|
end
|
769
1183
|
|
1184
|
+
# Conditionals
|
1185
|
+
|
1186
|
+
# If I see the given text, do the steps until I see an otherwise or end_if.
|
1187
|
+
# Otherwise do not do those steps.
|
1188
|
+
#
|
1189
|
+
# @param [String] text
|
1190
|
+
# Plain text that should be visible on the current page
|
1191
|
+
#
|
1192
|
+
# @example
|
1193
|
+
# | If I see | pop-over ad |
|
1194
|
+
# | Click | Close | button |
|
1195
|
+
# | End if |
|
1196
|
+
#
|
1197
|
+
# @since 0.1.1
|
1198
|
+
#
|
1199
|
+
def if_i_see(text)
|
1200
|
+
return false if aborted?
|
1201
|
+
# If this if is inside a block that's not running, record that.
|
1202
|
+
if !@conditional_stack.last
|
1203
|
+
@conditional_stack.push nil
|
1204
|
+
return nil
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
# Test the condition.
|
1208
|
+
@conditional_stack.push @browser.text?(text)
|
1209
|
+
|
1210
|
+
return true if @conditional_stack.last == true
|
1211
|
+
return nil if @conditional_stack.last == false
|
1212
|
+
return failure
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
# If the given parameter is "yes" or "true", do the steps until I see an
|
1216
|
+
# otherwise or end_if. Otherwise do not do those steps.
|
1217
|
+
#
|
1218
|
+
# @param [String] text
|
1219
|
+
# A string. Parsed by {#string_is_true?}. True values cause the
|
1220
|
+
# following steps to run. Anything else does not.
|
1221
|
+
#
|
1222
|
+
# @example
|
1223
|
+
# | If parameter | ${spam_me} |
|
1224
|
+
# | Enable | Send me spam | checkbox |
|
1225
|
+
# | See | Email: | within | 10 | seconds |
|
1226
|
+
# | Type | ${spam_me_email} | into field | spammable_email |
|
1227
|
+
# | End if |
|
1228
|
+
#
|
1229
|
+
# @since 0.1.1
|
1230
|
+
#
|
1231
|
+
def if_parameter(text)
|
1232
|
+
return false if aborted?
|
1233
|
+
if !@conditional_stack.last
|
1234
|
+
@conditional_stack.push nil
|
1235
|
+
return nil
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
# Test the condition.
|
1239
|
+
@conditional_stack.push string_is_true?(text)
|
1240
|
+
|
1241
|
+
return true if @conditional_stack.last == true
|
1242
|
+
return nil if @conditional_stack.last == false
|
1243
|
+
return failure
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
# If the first parameter is the same as the second, do the steps until I see an
|
1247
|
+
# otherwise or end_if. Otherwise do not do those steps.
|
1248
|
+
#
|
1249
|
+
# @param [String] text
|
1250
|
+
# A string.
|
1251
|
+
#
|
1252
|
+
# @param [String] expected
|
1253
|
+
# Another string.
|
1254
|
+
# Uses `selenium_compare', so glob, regexp, etc. are accepted.
|
1255
|
+
#
|
1256
|
+
# @example
|
1257
|
+
# | $name= | Get value | id=response_field |
|
1258
|
+
# | If | $name | is | George |
|
1259
|
+
# | Type | Hi, George. | into | chat | field |
|
1260
|
+
# | Otherwise |
|
1261
|
+
# | Type | Go away! Bring me George! | into | chat | field |
|
1262
|
+
# | End if |
|
1263
|
+
#
|
1264
|
+
# @since 0.1.1
|
1265
|
+
#
|
1266
|
+
def if_is(text, expected)
|
1267
|
+
return false if aborted?
|
1268
|
+
if !@conditional_stack.last
|
1269
|
+
@conditional_stack.push nil
|
1270
|
+
return nil
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
# Test the condition.
|
1274
|
+
@conditional_stack.push selenium_compare(text, expected)
|
1275
|
+
|
1276
|
+
return true if @conditional_stack.last == true
|
1277
|
+
return nil if @conditional_stack.last == false
|
1278
|
+
return failure
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
# End an if block.
|
1282
|
+
#
|
1283
|
+
# @since 0.1.1
|
1284
|
+
#
|
1285
|
+
def end_if
|
1286
|
+
return false if aborted?
|
1287
|
+
# If there was no prior matching if, fail.
|
1288
|
+
return failure if @conditional_stack.length <= 1
|
1289
|
+
|
1290
|
+
last_status = @conditional_stack.pop
|
1291
|
+
# If this end_if is within an un-executed if block, don't execute it.
|
1292
|
+
return nil if last_status == nil
|
1293
|
+
return true
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
# The else case to match any if.
|
1297
|
+
#
|
1298
|
+
# @example
|
1299
|
+
# | if parameter | ${login_by_phone} |
|
1300
|
+
# | type | ${login} | into field | phone_number |
|
1301
|
+
# | otherwise |
|
1302
|
+
# | type | ${login} | into field | login |
|
1303
|
+
# | end if |
|
1304
|
+
#
|
1305
|
+
# @since 0.1.1
|
1306
|
+
#
|
1307
|
+
def otherwise
|
1308
|
+
return false if aborted?
|
1309
|
+
# If there was no prior matching if, fail.
|
1310
|
+
return failure if @conditional_stack.length <= 1
|
1311
|
+
|
1312
|
+
# If this otherwise is within an un-executed if block, don't execute it.
|
1313
|
+
return nil if @conditional_stack.last == nil
|
1314
|
+
|
1315
|
+
last_stack = @conditional_stack.pop
|
1316
|
+
@conditional_stack.push !last_stack
|
1317
|
+
return true if @conditional_stack.last == true
|
1318
|
+
return nil if @conditional_stack.last == false
|
1319
|
+
return failure
|
1320
|
+
end
|
1321
|
+
|
770
1322
|
|
771
1323
|
private
|
772
1324
|
|
@@ -784,7 +1336,7 @@ module Rsel
|
|
784
1336
|
rescue => e
|
785
1337
|
#puts e.message
|
786
1338
|
#puts e.backtrace
|
787
|
-
failure
|
1339
|
+
failure("#{e.message}")
|
788
1340
|
else
|
789
1341
|
return true
|
790
1342
|
end
|
@@ -814,8 +1366,9 @@ module Rsel
|
|
814
1366
|
#
|
815
1367
|
# @since 0.0.6
|
816
1368
|
#
|
817
|
-
def failure
|
1369
|
+
def failure(reason='')
|
818
1370
|
@found_failure = true
|
1371
|
+
@errors.push(reason) unless (reason == '')
|
819
1372
|
return false
|
820
1373
|
end
|
821
1374
|
|
@@ -824,15 +1377,36 @@ module Rsel
|
|
824
1377
|
#
|
825
1378
|
# @since 0.0.6
|
826
1379
|
#
|
827
|
-
def pass_if(condition)
|
1380
|
+
def pass_if(condition, errormsg='')
|
828
1381
|
if condition
|
829
1382
|
return true
|
830
1383
|
else
|
831
|
-
failure
|
1384
|
+
failure(errormsg)
|
832
1385
|
end
|
833
1386
|
end
|
834
1387
|
|
835
1388
|
|
1389
|
+
# Conditionals
|
1390
|
+
|
1391
|
+
# Should the current step be skipped, either because the test was aborted or
|
1392
|
+
# because we're in a conditional?
|
1393
|
+
#
|
1394
|
+
# @since 0.1.1
|
1395
|
+
#
|
1396
|
+
def skip_step?
|
1397
|
+
return aborted? || !@conditional_stack.last
|
1398
|
+
end
|
1399
|
+
|
1400
|
+
# Presuming the current step should be skipped, what status should I return?
|
1401
|
+
#
|
1402
|
+
# @since 0.1.1
|
1403
|
+
#
|
1404
|
+
def skip_status
|
1405
|
+
return false if aborted?
|
1406
|
+
return nil if !@conditional_stack.last
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
|
836
1410
|
# Return true if this test has been aborted.
|
837
1411
|
#
|
838
1412
|
# @since 0.0.9
|
@@ -844,5 +1418,3 @@ module Rsel
|
|
844
1418
|
end
|
845
1419
|
end
|
846
1420
|
|
847
|
-
|
848
|
-
|