rsel 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/docs/development.md +8 -3
- data/docs/examples.md +119 -0
- data/docs/history.md +9 -0
- data/docs/index.md +3 -1
- data/docs/locators.md +72 -0
- data/docs/scoping.md +19 -5
- data/docs/todo.md +4 -2
- data/docs/usage.md +2 -2
- data/lib/rsel/exceptions.rb +1 -1
- data/lib/rsel/selenium_test.rb +37 -132
- data/lib/rsel/support.rb +138 -0
- data/rsel.gemspec +1 -1
- data/spec/selenium_test_spec.rb +103 -12
- data/spec/spec_helper.rb +1 -10
- data/spec/support_spec.rb +48 -0
- data/test/app.rb +6 -6
- metadata +8 -39
- data/vendor/rubyslim-0.1.1/.gitignore +0 -7
- data/vendor/rubyslim-0.1.1/README.md +0 -159
- data/vendor/rubyslim-0.1.1/Rakefile +0 -21
- data/vendor/rubyslim-0.1.1/bin/rubyslim +0 -10
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_deserializer.rb +0 -69
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_executor.rb +0 -12
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_serializer.rb +0 -45
- data/vendor/rubyslim-0.1.1/lib/rubyslim/ruby_slim.rb +0 -61
- data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_error.rb +0 -3
- data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_helper_library.rb +0 -23
- data/vendor/rubyslim-0.1.1/lib/rubyslim/socket_service.rb +0 -53
- data/vendor/rubyslim-0.1.1/lib/rubyslim/statement.rb +0 -79
- data/vendor/rubyslim-0.1.1/lib/rubyslim/statement_executor.rb +0 -174
- data/vendor/rubyslim-0.1.1/lib/rubyslim/table_to_hash_converter.rb +0 -32
- data/vendor/rubyslim-0.1.1/lib/test_module/library_new.rb +0 -13
- data/vendor/rubyslim-0.1.1/lib/test_module/library_old.rb +0 -12
- data/vendor/rubyslim-0.1.1/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +0 -7
- data/vendor/rubyslim-0.1.1/lib/test_module/simple_script.rb +0 -9
- data/vendor/rubyslim-0.1.1/lib/test_module/test_chain.rb +0 -5
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim.rb +0 -86
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_arguments.rb +0 -23
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_no_sut.rb +0 -5
- data/vendor/rubyslim-0.1.1/rubyslim.gemspec +0 -22
- data/vendor/rubyslim-0.1.1/spec/instance_creation_spec.rb +0 -40
- data/vendor/rubyslim-0.1.1/spec/it8f_spec.rb +0 -8
- data/vendor/rubyslim-0.1.1/spec/list_deserializer_spec.rb +0 -66
- data/vendor/rubyslim-0.1.1/spec/list_executor_spec.rb +0 -187
- data/vendor/rubyslim-0.1.1/spec/list_serialzer_spec.rb +0 -38
- data/vendor/rubyslim-0.1.1/spec/method_invocation_spec.rb +0 -127
- data/vendor/rubyslim-0.1.1/spec/slim_helper_library_spec.rb +0 -80
- data/vendor/rubyslim-0.1.1/spec/socket_service_spec.rb +0 -98
- data/vendor/rubyslim-0.1.1/spec/spec_helper.rb +0 -6
- data/vendor/rubyslim-0.1.1/spec/statement_executor_spec.rb +0 -50
- data/vendor/rubyslim-0.1.1/spec/statement_spec.rb +0 -17
- data/vendor/rubyslim-0.1.1/spec/table_to_hash_converter_spec.rb +0 -31
data/docs/development.md
CHANGED
@@ -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
|
-
|
46
|
-
|
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)
|
data/docs/examples.md
ADDED
@@ -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)
|
data/docs/history.md
CHANGED
@@ -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
|
|
data/docs/index.md
CHANGED
@@ -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
|
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)
|
data/docs/locators.md
ADDED
@@ -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
|
+
|
data/docs/scoping.md
CHANGED
@@ -79,10 +79,24 @@ operate on checkboxes, dropdowns, or text fields as well.
|
|
79
79
|
Caveat
|
80
80
|
------
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
of
|
85
|
-
|
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: [
|
121
|
+
Next: [Examples](examples.md)
|
108
122
|
|
data/docs/todo.md
CHANGED
@@ -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
|
9
|
-
|
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)
|
data/docs/usage.md
CHANGED
@@ -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: [
|
49
|
+
Next: [Locators](locators.md)
|
50
50
|
|
data/lib/rsel/exceptions.rb
CHANGED
data/lib/rsel/selenium_test.rb
CHANGED
@@ -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 [
|
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
|
-
|
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?(
|
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?(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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 =
|
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 =
|
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 =
|
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 =
|
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(
|
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(
|
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(
|
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
|
|