rsel 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/docs/development.md +8 -3
  2. data/docs/examples.md +119 -0
  3. data/docs/history.md +9 -0
  4. data/docs/index.md +3 -1
  5. data/docs/locators.md +72 -0
  6. data/docs/scoping.md +19 -5
  7. data/docs/todo.md +4 -2
  8. data/docs/usage.md +2 -2
  9. data/lib/rsel/exceptions.rb +1 -1
  10. data/lib/rsel/selenium_test.rb +37 -132
  11. data/lib/rsel/support.rb +138 -0
  12. data/rsel.gemspec +1 -1
  13. data/spec/selenium_test_spec.rb +103 -12
  14. data/spec/spec_helper.rb +1 -10
  15. data/spec/support_spec.rb +48 -0
  16. data/test/app.rb +6 -6
  17. metadata +8 -39
  18. data/vendor/rubyslim-0.1.1/.gitignore +0 -7
  19. data/vendor/rubyslim-0.1.1/README.md +0 -159
  20. data/vendor/rubyslim-0.1.1/Rakefile +0 -21
  21. data/vendor/rubyslim-0.1.1/bin/rubyslim +0 -10
  22. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_deserializer.rb +0 -69
  23. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_executor.rb +0 -12
  24. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_serializer.rb +0 -45
  25. data/vendor/rubyslim-0.1.1/lib/rubyslim/ruby_slim.rb +0 -61
  26. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_error.rb +0 -3
  27. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_helper_library.rb +0 -23
  28. data/vendor/rubyslim-0.1.1/lib/rubyslim/socket_service.rb +0 -53
  29. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement.rb +0 -79
  30. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement_executor.rb +0 -174
  31. data/vendor/rubyslim-0.1.1/lib/rubyslim/table_to_hash_converter.rb +0 -32
  32. data/vendor/rubyslim-0.1.1/lib/test_module/library_new.rb +0 -13
  33. data/vendor/rubyslim-0.1.1/lib/test_module/library_old.rb +0 -12
  34. data/vendor/rubyslim-0.1.1/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +0 -7
  35. data/vendor/rubyslim-0.1.1/lib/test_module/simple_script.rb +0 -9
  36. data/vendor/rubyslim-0.1.1/lib/test_module/test_chain.rb +0 -5
  37. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim.rb +0 -86
  38. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_arguments.rb +0 -23
  39. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_no_sut.rb +0 -5
  40. data/vendor/rubyslim-0.1.1/rubyslim.gemspec +0 -22
  41. data/vendor/rubyslim-0.1.1/spec/instance_creation_spec.rb +0 -40
  42. data/vendor/rubyslim-0.1.1/spec/it8f_spec.rb +0 -8
  43. data/vendor/rubyslim-0.1.1/spec/list_deserializer_spec.rb +0 -66
  44. data/vendor/rubyslim-0.1.1/spec/list_executor_spec.rb +0 -187
  45. data/vendor/rubyslim-0.1.1/spec/list_serialzer_spec.rb +0 -38
  46. data/vendor/rubyslim-0.1.1/spec/method_invocation_spec.rb +0 -127
  47. data/vendor/rubyslim-0.1.1/spec/slim_helper_library_spec.rb +0 -80
  48. data/vendor/rubyslim-0.1.1/spec/socket_service_spec.rb +0 -98
  49. data/vendor/rubyslim-0.1.1/spec/spec_helper.rb +0 -6
  50. data/vendor/rubyslim-0.1.1/spec/statement_executor_spec.rb +0 -50
  51. data/vendor/rubyslim-0.1.1/spec/statement_spec.rb +0 -17
  52. data/vendor/rubyslim-0.1.1/spec/table_to_hash_converter_spec.rb +0 -31
@@ -1,10 +1,14 @@
1
1
  Development
2
- -----------
2
+ ===========
3
3
 
4
4
  If you would like to contribute to development of Rsel, create a fork of the
5
5
  [Github repository](https://github.com/a-e/rsel), clone your fork, and submit
6
6
  pull requests for any improvements you would like to share.
7
7
 
8
+
9
+ Testing
10
+ -------
11
+
8
12
  While developing, you should run the RSpec tests frequently to ensure nothing
9
13
  gets broken. You can do this with a single `rake` command:
10
14
 
@@ -42,7 +46,8 @@ and stop them:
42
46
  $ rake selenium:rc:stop
43
47
  $ rake testapp:stop
44
48
 
45
- If you are developing new features, please add spec tests in the `spec`
46
- directory!
49
+ When the test application is running, you can view the site in your browser by
50
+ visiting http://localhost:8070/. If you are developing new features, please add
51
+ spec tests in the `spec` directory!
47
52
 
48
53
  Next: [To-do list](todo.md)
@@ -0,0 +1,119 @@
1
+ Examples
2
+ ========
3
+
4
+ This page presents some example Rsel tests, illustrating how you might use many
5
+ of the built-in methods from a FitNesse wiki page. All of these are based on
6
+ the example web application that you can find in the Rsel `test` directory;
7
+ it's the same one used for running the spec tests in the `spec` directory. See
8
+ [Development](development.md) for instructions on running the server if you'd
9
+ like to execute these examples.
10
+
11
+
12
+ Links
13
+ -------------
14
+
15
+ | script | selenium test | !-http://localhost:8070-! |
16
+ | Open browser |
17
+ | Maximize browser |
18
+ | Visit | / |
19
+ | See | Welcome |
20
+ | See | This is a Sinatra webapp for unit testing Rsel |
21
+ | Link | About this site | exists |
22
+ | Click link | About this site |
23
+ | See | This site is really cool |
24
+ | See title | About this site |
25
+ | Click back |
26
+ | See | This is a Sinatra webapp for unit testing Rsel |
27
+ | Do not see | This site is really cool |
28
+ | Close browser |
29
+
30
+
31
+
32
+ Buttons
33
+ -------------
34
+
35
+ | script | selenium test | !-http://localhost:8070-! |
36
+ | Open browser |
37
+ | Maximize browser |
38
+ | Visit | /form |
39
+ | Button | Submit person form | exists |
40
+ | Click | Submit person form | button |
41
+ | See | We appreciate your feedback |
42
+ | Visit | /form |
43
+ | Button | Submit spouse form | exists | !{within:spouse_form} |
44
+ | Click | Submit spouse form | button | !{within:spouse_form} |
45
+ | See | We appreciate your feedback |
46
+ | Close browser |
47
+
48
+
49
+ Checkboxes
50
+ -------------
51
+
52
+ | script | selenium test | !-http://localhost:8070-! |
53
+ | Open browser |
54
+ | Maximize browser |
55
+ | Visit | /form |
56
+ | Enable | I like cheese | checkbox |
57
+ | Checkbox | I like cheese | is enabled |
58
+ | Disable | I like cheese | checkbox |
59
+ | Checkbox | I like cheese | is disabled |
60
+ | Enable | I like salami | checkbox | !{within:salami_checkbox} |
61
+ | Checkbox | I like salami | is enabled | !{within:salami_checkbox} |
62
+ | Disable | I like salami | checkbox | !{within:salami_checkbox} |
63
+ | Checkbox | I like salami | is disabled | !{within:salami_checkbox} |
64
+ | Close browser |
65
+
66
+
67
+ Dropdowns
68
+ -------------
69
+
70
+ | script | selenium test | !-http://localhost:8070-! |
71
+ | Open browser |
72
+ | Maximize browser |
73
+ | Visit | /form |
74
+ | Dropdown | Height | includes | Short |
75
+ | Dropdown | Height | includes | Average |
76
+ | Dropdown | Height | includes | Tall |
77
+ | Select | Tall | from | Height | dropdown |
78
+ | Dropdown | Height | equals | Tall |
79
+ | Select | Short | from | Height | dropdown |
80
+ | Dropdown | Height | equals | Short |
81
+ | Close browser |
82
+
83
+
84
+ Radiobuttons
85
+ -------------
86
+
87
+ | script | selenium test | !-http://localhost:8070-! |
88
+ | Open browser |
89
+ | Maximize browser |
90
+ | Visit | /form |
91
+ | Select | Briefs | radio |
92
+ | Radio | Briefs | is enabled |
93
+ | Radio | Boxers | is disabled |
94
+ | Select | Boxers | radio | !{within:clothing} |
95
+ | Radio | Boxers | is enabled | !{within:clothing} |
96
+ | Radio | Briefs | is disabled | !{within:clothing} |
97
+ | Close browser |
98
+
99
+
100
+ Tables
101
+ -------------
102
+
103
+ | script | selenium test | !-http://localhost:8070-! |
104
+ | Open browser |
105
+ | Maximize browser |
106
+ | Visit | /table |
107
+ | Row | First name, Last name, Email, Actions | exists |
108
+ | Row | !-Eric, Pierce, epierce@example.com-! | exists |
109
+ | Enable | Like | checkbox | !{in_row:Marcus} |
110
+ | Checkbox | Like | is enabled | !{in_row:Marcus} |
111
+ | Disable | Like | checkbox | !{in_row:Marcus} |
112
+ | Checkbox | Like | is disabled | !{in_row:Marcus} |
113
+ | Select | Male | from | Gender | dropdown | !{in_row:Eric} |
114
+ | Click | Edit | link | !{in_row:Eric} |
115
+ | See title | Editing Eric |
116
+ | Close browser |
117
+
118
+
119
+ Next: [Customization](custom.md)
@@ -1,6 +1,15 @@
1
1
  Rsel History
2
2
  ============
3
3
 
4
+ 0.0.5
5
+ -----
6
+
7
+ - Allow Selenium-style locators beginning with id=, name=, xpath=, css= etc.
8
+ - Raise a StopTest exception when Selenium is not running (requires updated rubyslim)
9
+ - Locators are now documented
10
+ - Example FitNesse tables are now included
11
+
12
+
4
13
  0.0.4
5
14
  -----
6
15
 
@@ -5,9 +5,11 @@ Rsel connects [FitNesse](http://fitnesse.org) to
5
5
  [Selenium](http://seleniumhq.org) via [Ruby](http://ruby-lang.org). It allows
6
6
  you to use a natural English syntax to write web application tests.
7
7
 
8
- - [Installation & Configuration](install.md)
8
+ - [Installation](install.md)
9
9
  - [Usage](usage.md)
10
+ - [Locators](locators.md)
10
11
  - [Scoping](scoping.md)
12
+ - [Examples](examples.md)
11
13
  - [Customization](custom.md)
12
14
  - [Development](development.md)
13
15
  - [To-do list](todo.md)
@@ -0,0 +1,72 @@
1
+ Locators
2
+ ========
3
+
4
+ By default, Selenium accepts a [variety of locator strings](http://release.seleniumhq.org/selenium-remote-control/0.9.2/doc/dotnet/Selenium.html)
5
+ for specifying the element you want to inspect or interact with. While these
6
+ are suitable for programmers and web developers with intimate knowledge of
7
+ their webpages' structure, they are often not intuitive to the average user.
8
+
9
+ For example, the default behavior of Selenium is to match elements based on
10
+ their `name` attribute. Given this HTML:
11
+
12
+ <a name="contact_us_link" href="/contact">Contact Us</a>
13
+
14
+ To click on the "Concact Us" link using a standard Selenium locator, you'd have
15
+ to do one of the following:
16
+
17
+ | click | contact_us_link |
18
+ | click | link=Contact Us |
19
+
20
+ This is easy enough, but what if the target element does not have a `name`
21
+ attribute defined? What if the link is an image instead of a piece of text? In
22
+ those cases, you'd have to use a locator string based on `id` (if the element
23
+ has one), `dom`, `xpath`, or `css`. Again, all of these are
24
+ programmer-oriented, and would not make much sense to the average user looking
25
+ at the webpage. While this scheme allows selecting HTML elements with great
26
+ precision, it can become cumbersome to read and write.
27
+
28
+ The situation becomes even more complex with HTML form elements--text fields,
29
+ dropdowns, checkboxes and the like. While HTML provides a way to semantically
30
+ label these fields, Selenium provides no way to select those fields based on
31
+ their label, aside from using a rather long and complex `xpath` expression.
32
+ For instance, given this HTML:
33
+
34
+ <label for="first_name_text_field">First name</label>
35
+ <input id="first_name_text_field" type="text" />
36
+
37
+ With Selenium's default selectors, you could do:
38
+
39
+ | type | id=first_name_text_field | Eric |
40
+ | type | xpath=.//input[@id = ../label[.='First name']/@for] | Eric |
41
+
42
+ Neither of these solutions is very attractive.
43
+
44
+ For these reasons, Rsel has a simpler locator scheme that matches elements
45
+ based on several likely attributes. Links are matched on `id`, link text, or
46
+ the `alt` text in the case of an image link. Form fields are matched on their
47
+ plain-text label, `name` or `id` attribute. In Rsel, the above examples become
48
+ much easier:
49
+
50
+ | click | Contact Us |
51
+ | Type | Eric | into field | First name |
52
+
53
+ This allows you to use human-readable locator strings for most HTML elements,
54
+ keeping your test steps free of ugly markup. Refer to the `SeleniumTest` method
55
+ documentation for details on what locator strings are expected for each element.
56
+
57
+ If you like, you can still pass explicit Selenium-style locators, as long as
58
+ they are prefixed with `id=`, `name=`, `dom=`, `xpath=`, `link=`, or `css=`. If
59
+ you do this, it's up to you to make sure you're using a suitable locator for
60
+ the kind of element you're interacting with. For example, it doesn't make much
61
+ sense to write:
62
+
63
+ | Type | Eric | into field | link=Contact Us |
64
+
65
+ But Rsel won't stop you from trying! One drawback to this approach is that any
66
+ [scoping](scoping.md) clause is ignored. In most cases, it's easier, safer, and
67
+ more readable to use the plain text locator, and rely on Rsel's internal logic
68
+ to find the correct element. This feature is only provided as a workaround for
69
+ the possibility that Rsel's locator scheme is too restrictive for your needs.
70
+
71
+ Next: [Scoping](scoping.md)
72
+
@@ -79,10 +79,24 @@ operate on checkboxes, dropdowns, or text fields as well.
79
79
  Caveat
80
80
  ------
81
81
 
82
- One important thing to note is that due to the way FitNesse Slim script tables
83
- are evaluated, the scoping hash must be added after a cell that contains part
84
- of the method name. Cells in these tables must contain alternating (function,
85
- argument) chunks; the way the above example breaks down is:
82
+ The first thing you need to remember when using a scoping hash is that your tables
83
+ must not be fully escaped. This just means you should not include a `!` at the
84
+ beginning of your table, otherwise the hash syntax will be interpreted
85
+ literally, instead of as an embedded hash.
86
+
87
+ This means you will need to manually escape any cells in your table that
88
+ contain URLs, email addresses, or other auto-interpreted text:
89
+
90
+ | script | selenium test | !-http://www.example.com-! |
91
+ | Open browser |
92
+ | See | Welcome |
93
+ | Click | About | link | !{within:footer} |
94
+ | Close browser |
95
+
96
+ Another important thing to note is that due to the way FitNesse Slim script
97
+ tables are evaluated, the scoping hash must be added after a cell that contains
98
+ part of the method name. Cells in these tables must contain alternating
99
+ (function, argument) chunks; the way the above example breaks down is:
86
100
 
87
101
  | Type | | into | | field | |
88
102
  | | 111-222-3333 | | Phone number | | !{within:work} |
@@ -104,5 +118,5 @@ have a use case that isn't covered by the existing scopes, please [submit an
104
118
  issue](http://github.com/a-e/rsel/issues), or better yet, implement it yourself
105
119
  and submit a pull request. See [Development](development.md) for more info.
106
120
 
107
- Next: [Customization](custom.md)
121
+ Next: [Examples](examples.md)
108
122
 
@@ -5,8 +5,10 @@ To-do list
5
5
  only support true/false results, with no clear way to report on what went
6
6
  wrong (aside from raising an exception, which even then curiously does not
7
7
  mark the step as failed)
8
- - Find a way to abort a test if something goes catastrophically wrong (such as
9
- being unable to connect to the Selenium server)
8
+ - Find a way to abort a test if a step fails (will probably require more
9
+ hacking on rubyslim)
10
10
  - Verify the presence of images, allow clicking on images
11
+ - Create a Selenium IDE plugin to generate FitNesse-formatted output for
12
+ recorded scripts
11
13
 
12
14
  Next: [History](history.md)
@@ -1,5 +1,5 @@
1
1
  Usage
2
- -----
2
+ =====
3
3
 
4
4
  Once you have created your `SetUp` page, you can create sibling pages with
5
5
  tests in them. For instance, continuing with the example from
@@ -46,5 +46,5 @@ driver frowns upon it.)
46
46
  See the `SeleniumTest` class documentation for a full list of available methods
47
47
  and how to use them.
48
48
 
49
- Next: [Scoping](scoping.md)
49
+ Next: [Locators](locators.md)
50
50
 
@@ -1,4 +1,4 @@
1
1
  module Rsel
2
2
  class LocatorNotFound < RuntimeError; end
3
- class SeleniumNotRunning < RuntimeError; end
3
+ class StopTestSeleniumNotRunning < RuntimeError; end
4
4
  end
@@ -1,31 +1,29 @@
1
- require File.join(File.dirname(__FILE__), 'exceptions')
1
+ #require File.join(File.dirname(__FILE__), 'exceptions')
2
2
 
3
3
  require 'rubygems'
4
4
  require 'xpath'
5
5
  require 'selenium/client'
6
+ require 'rsel/support'
7
+ require 'rsel/exceptions'
6
8
 
7
9
  module Rsel
8
10
 
9
- # Main Selenium-test class.
11
+ # Main Selenium-test class, and wrapper for all Selenium method calls. This
12
+ # class is intended to be instantiated in a FitNesse / Slim script table.
13
+ # May be customized or extended using a subclass.
14
+ #
15
+ # @note: Function names beginning with the words 'check', 'ensure', 'reject',
16
+ # 'note', 'show', or 'start' cannot be called from a Slim script table, since
17
+ # these are keywords that receive special handling by Slim.
10
18
  #
11
19
  # @example
12
20
  # !| script | selenium test | http://www.example.com/ |
13
- #
14
- # NOTE: Function names beginning with these words are forbidden:
15
- #
16
- # - check
17
- # - ensure
18
- # - reject
19
- # - note
20
- # - show
21
- # - start
22
- #
23
- # This is because the above words are keywords in Slim script tables; if
24
- # the first cell of a script table begins with any of these words, Slim tries
25
- # to apply special handling to them, which usually doesn't do what you want.
21
+ # | script | selenium test | !-http://www.example.com/-! |
26
22
  #
27
23
  class SeleniumTest
28
24
 
25
+ include Support
26
+
29
27
  # Initialize a test, connecting to the given Selenium server.
30
28
  #
31
29
  # @param [String] url
@@ -59,14 +57,13 @@ module Rsel
59
57
  # @example
60
58
  # | Open browser |
61
59
  #
62
- # @raise [SeleniumNotRunning] if Selenium connection cannot be made
60
+ # @raise [StopTestSeleniumNotRunning] if Selenium connection cannot be made
63
61
  #
64
62
  def open_browser
65
63
  begin
66
64
  @browser.start_new_browser_session
67
65
  rescue
68
- # TODO: Find a way to make the test abort here
69
- raise SeleniumNotRunning, "Could not start Selenium."
66
+ raise StopTestSeleniumNotRunning, "Could not start Selenium."
70
67
  else
71
68
  visit @url
72
69
  end
@@ -204,7 +201,7 @@ module Rsel
204
201
  # @since 0.0.2
205
202
  #
206
203
  def link_exists(locator, scope={})
207
- return @browser.element?(xpath('link', locator, scope))
204
+ return @browser.element?(loc(locator, 'link', scope))
208
205
  end
209
206
 
210
207
 
@@ -222,7 +219,7 @@ module Rsel
222
219
  # @since 0.0.2
223
220
  #
224
221
  def button_exists(locator, scope={})
225
- return @browser.element?(xpath('button', locator, scope))
222
+ return @browser.element?(loc(locator, 'button', scope))
226
223
  end
227
224
 
228
225
 
@@ -258,7 +255,7 @@ module Rsel
258
255
  #
259
256
  def type_into_field(text, locator, scope={})
260
257
  return_error_status do
261
- @browser.type(xpath('field', locator, scope), text)
258
+ @browser.type(loc(locator, 'field', scope), text)
262
259
  end
263
260
  end
264
261
 
@@ -269,12 +266,14 @@ module Rsel
269
266
  # Label, name, or id of the field you want to type into
270
267
  # @param [String] text
271
268
  # What to type into the field
269
+ # @param [Hash] scope
270
+ # Scoping keywords as understood by {#xpath}
272
271
  #
273
272
  # @example
274
273
  # | Fill in | First name | with | Eric |
275
274
  #
276
- def fill_in_with(locator, text)
277
- type_into_field(text, locator)
275
+ def fill_in_with(locator, text, scope={})
276
+ type_into_field(text, locator, scope)
278
277
  end
279
278
 
280
279
 
@@ -290,8 +289,8 @@ module Rsel
290
289
  # @example
291
290
  # | Field | First name | contains | Eric |
292
291
  #
293
- def field_contains(locator, text)
294
- @browser.field(xpath('field', locator)).include?(text)
292
+ def field_contains(locator, text, scope={})
293
+ @browser.field(loc(locator, 'field', scope)).include?(text)
295
294
  end
296
295
 
297
296
 
@@ -308,7 +307,7 @@ module Rsel
308
307
  # | Field | First name | equals; | Eric | !{within:contact} |
309
308
  #
310
309
  def field_equals(locator, text, scope={})
311
- @browser.field(xpath('field', locator, scope)) == text
310
+ @browser.field(loc(locator, 'field', scope)) == text
312
311
  end
313
312
 
314
313
 
@@ -324,7 +323,7 @@ module Rsel
324
323
  #
325
324
  def click(locator, scope={})
326
325
  return_error_status do
327
- @browser.click(xpath('link_or_button', locator, scope))
326
+ @browser.click(loc(locator, 'link_or_button', scope))
328
327
  end
329
328
  end
330
329
 
@@ -344,7 +343,7 @@ module Rsel
344
343
  #
345
344
  def click_link(locator, scope={})
346
345
  return_error_status do
347
- @browser.click(xpath('link', locator, scope))
346
+ @browser.click(loc(locator, 'link', scope))
348
347
  end
349
348
  end
350
349
  alias_method :follow, :click_link
@@ -365,7 +364,7 @@ module Rsel
365
364
  def click_button(locator, scope={})
366
365
  # TODO: Make this fail when the button is disabled
367
366
  return_error_status do
368
- @browser.click(xpath('button', locator, scope))
367
+ @browser.click(loc(locator, 'button', scope))
369
368
  end
370
369
  end
371
370
  alias_method :press, :click_button
@@ -386,7 +385,7 @@ module Rsel
386
385
  def enable_checkbox(locator, scope={})
387
386
  return true if checkbox_is_enabled(locator, scope)
388
387
  return_error_status do
389
- @browser.click(xpath('checkbox', locator, scope))
388
+ @browser.click(loc(locator, 'checkbox', scope))
390
389
  end
391
390
  end
392
391
 
@@ -406,7 +405,7 @@ module Rsel
406
405
  def disable_checkbox(locator, scope={})
407
406
  return true if checkbox_is_disabled(locator, scope)
408
407
  return_error_status do
409
- @browser.click(xpath('checkbox', locator, scope))
408
+ @browser.click(loc(locator, 'checkbox', scope))
410
409
  end
411
410
  end
412
411
 
@@ -423,7 +422,7 @@ module Rsel
423
422
  # | Checkbox | send me spam | is enabled | !{within:opt_in} |
424
423
  #
425
424
  def checkbox_is_enabled(locator, scope={})
426
- xp = xpath('checkbox', locator, scope)
425
+ xp = loc(locator, 'checkbox', scope)
427
426
  begin
428
427
  enabled = @browser.checked?(xp)
429
428
  rescue
@@ -448,7 +447,7 @@ module Rsel
448
447
  # @since 0.0.4
449
448
  #
450
449
  def radio_is_enabled(locator, scope={})
451
- xp = xpath('radio_button', locator, scope)
450
+ xp = loc(locator, 'radio_button', scope)
452
451
  begin
453
452
  enabled = @browser.checked?(xp)
454
453
  rescue
@@ -471,7 +470,7 @@ module Rsel
471
470
  # | Checkbox | send me spam | is disabled | !{within:opt_in} |
472
471
  #
473
472
  def checkbox_is_disabled(locator, scope={})
474
- xp = xpath('checkbox', locator, scope)
473
+ xp = loc(locator, 'checkbox', scope)
475
474
  begin
476
475
  enabled = @browser.checked?(xp)
477
476
  rescue
@@ -496,7 +495,7 @@ module Rsel
496
495
  # @since 0.0.4
497
496
  #
498
497
  def radio_is_disabled(locator, scope={})
499
- xp = xpath('radio_button', locator, scope)
498
+ xp = loc(locator, 'radio_button', scope)
500
499
  begin
501
500
  enabled = @browser.checked?(xp)
502
501
  rescue
@@ -520,7 +519,7 @@ module Rsel
520
519
  #
521
520
  def select_radio(locator, scope={})
522
521
  return_error_status do
523
- @browser.click(xpath('radio_button', locator, scope))
522
+ @browser.click(loc(locator, 'radio_button', scope))
524
523
  end
525
524
  end
526
525
 
@@ -538,7 +537,7 @@ module Rsel
538
537
  #
539
538
  def select_from_dropdown(option, locator, scope={})
540
539
  return_error_status do
541
- @browser.select(xpath('select', locator, scope), option)
540
+ @browser.select(loc(locator, 'select', scope), option)
542
541
  end
543
542
  end
544
543
 
@@ -577,9 +576,8 @@ module Rsel
577
576
  # @since 0.0.2
578
577
  #
579
578
  def dropdown_equals(locator, option, scope={})
580
- # TODO: Apply scope
581
579
  begin
582
- selected = @browser.get_selected_label(xpath('select', locator))
580
+ selected = @browser.get_selected_label(loc(locator, 'select', scope))
583
581
  rescue
584
582
  return false
585
583
  else
@@ -618,99 +616,6 @@ module Rsel
618
616
  end
619
617
  end
620
618
 
621
-
622
- # Execute the given block, and return false if it raises an exception.
623
- # Otherwise, return true.
624
- #
625
- # @example
626
- # return_error_status do
627
- # # some code that might raise an exception
628
- # end
629
- #
630
- def return_error_status
631
- begin
632
- yield
633
- rescue => e
634
- #puts e.message
635
- #puts e.backtrace
636
- return false
637
- else
638
- return true
639
- end
640
- end
641
-
642
-
643
- # Return a Selenium-style xpath generated by calling `XPath::HTML.<kind>`
644
- # with the given `locator`.
645
- #
646
- # @param [String] kind
647
- # What kind of locator you're using (link, button, checkbox, field etc.).
648
- # This must correspond to a method name in `XPath::HTML`.
649
- # @param [String] locator
650
- # Name, id, value, label or whatever other locators are accepted by
651
- # `XPath::HTML.<kind>`
652
- # @param [Hash] scope
653
- # Keywords to restrict the scope of matching elements
654
- # @option scope [String] :within
655
- # Restrict scope to elements having this id, matching `locator` only if
656
- # it's contained within an element with this id.
657
- # @option scope [String] :in_row
658
- # Restrict scope to a table row containing this text, matching `locator`
659
- # only if that locator is in the same row as the given text.
660
- #
661
- # @example
662
- # xpath('link', 'Log in')
663
- # xpath('button', 'Submit')
664
- # xpath('field', 'First name')
665
- # xpath('table_row', ['First', 'Last'])
666
- #
667
- def xpath(kind, locator, scope={})
668
- loc_xp = XPath::HTML.send(kind, locator)
669
- if scope[:within]
670
- parent = XPath.descendant[XPath.attr(:id).equals(scope[:within])]
671
- result = apply_scope(parent, loc_xp)
672
- elsif scope[:in_row]
673
- row = XPath.descendant(:tr)[XPath.contains(scope[:in_row])]
674
- result = apply_scope(row, loc_xp)
675
- else
676
- result = loc_xp.to_s
677
- end
678
- return "xpath=#{result}"
679
- end
680
-
681
-
682
- # Restrict the scope of all XPath expressions in `inner` by prepending
683
- # `container` to each of them, and return new union expression where
684
- # `inner` matches only if it's a child of `container`.
685
- #
686
- def apply_scope(container, inner)
687
- scoped_expressions = xpath_expressions(inner).collect do |expr|
688
- container.child(expr)
689
- end
690
- return XPath::Union.new(*scoped_expressions).to_s
691
- end
692
-
693
-
694
- # Return an array of individual Expressions in the given XPath::Union, or
695
- # just `[union]` if it has no sub-expressions. This is an ugly recursive
696
- # hack, designed to allow splitting up unions into their constituents for
697
- # the purpose of modifying them individually and re-combining them.
698
- #
699
- # @param [XPath::Union, XPath::Expression] union
700
- # The xpath you want to break down into individual expressions
701
- #
702
- # @since 0.0.3
703
- #
704
- def xpath_expressions(union)
705
- if union.respond_to?(:expressions)
706
- return union.expressions.collect do |expr|
707
- xpath_expressions(expr)
708
- end.flatten
709
- else
710
- return [union]
711
- end
712
- end
713
-
714
619
  end
715
620
  end
716
621