rsel 0.0.4 → 0.0.5

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.
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