rsel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +4 -0
  2. data/.yardopts +7 -0
  3. data/Gemfile +2 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +35 -0
  6. data/Rakefile +75 -0
  7. data/docs/custom.md +87 -0
  8. data/docs/development.md +48 -0
  9. data/docs/index.md +13 -0
  10. data/docs/install.md +33 -0
  11. data/docs/todo.md +15 -0
  12. data/docs/usage.md +50 -0
  13. data/lib/rsel.rb +4 -0
  14. data/lib/rsel/exceptions.rb +4 -0
  15. data/lib/rsel/selenium_test.rb +524 -0
  16. data/rsel.gemspec +29 -0
  17. data/spec/selenium_test_spec.rb +296 -0
  18. data/spec/spec_helper.rb +41 -0
  19. data/test/app.rb +34 -0
  20. data/test/config.ru +4 -0
  21. data/test/views/about.erb +9 -0
  22. data/test/views/form.erb +102 -0
  23. data/test/views/index.erb +15 -0
  24. data/vendor/rubyslim-0.1.1/.gitignore +7 -0
  25. data/vendor/rubyslim-0.1.1/README.md +159 -0
  26. data/vendor/rubyslim-0.1.1/Rakefile +21 -0
  27. data/vendor/rubyslim-0.1.1/bin/rubyslim +10 -0
  28. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_deserializer.rb +69 -0
  29. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_executor.rb +12 -0
  30. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_serializer.rb +45 -0
  31. data/vendor/rubyslim-0.1.1/lib/rubyslim/ruby_slim.rb +61 -0
  32. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_error.rb +3 -0
  33. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_helper_library.rb +23 -0
  34. data/vendor/rubyslim-0.1.1/lib/rubyslim/socket_service.rb +53 -0
  35. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement.rb +79 -0
  36. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement_executor.rb +174 -0
  37. data/vendor/rubyslim-0.1.1/lib/rubyslim/table_to_hash_converter.rb +32 -0
  38. data/vendor/rubyslim-0.1.1/lib/test_module/library_new.rb +13 -0
  39. data/vendor/rubyslim-0.1.1/lib/test_module/library_old.rb +12 -0
  40. data/vendor/rubyslim-0.1.1/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
  41. data/vendor/rubyslim-0.1.1/lib/test_module/simple_script.rb +9 -0
  42. data/vendor/rubyslim-0.1.1/lib/test_module/test_chain.rb +5 -0
  43. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim.rb +86 -0
  44. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_arguments.rb +23 -0
  45. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_no_sut.rb +5 -0
  46. data/vendor/rubyslim-0.1.1/rubyslim.gemspec +22 -0
  47. data/vendor/rubyslim-0.1.1/spec/instance_creation_spec.rb +40 -0
  48. data/vendor/rubyslim-0.1.1/spec/it8f_spec.rb +8 -0
  49. data/vendor/rubyslim-0.1.1/spec/list_deserializer_spec.rb +66 -0
  50. data/vendor/rubyslim-0.1.1/spec/list_executor_spec.rb +187 -0
  51. data/vendor/rubyslim-0.1.1/spec/list_serialzer_spec.rb +38 -0
  52. data/vendor/rubyslim-0.1.1/spec/method_invocation_spec.rb +127 -0
  53. data/vendor/rubyslim-0.1.1/spec/slim_helper_library_spec.rb +80 -0
  54. data/vendor/rubyslim-0.1.1/spec/socket_service_spec.rb +98 -0
  55. data/vendor/rubyslim-0.1.1/spec/spec_helper.rb +6 -0
  56. data/vendor/rubyslim-0.1.1/spec/statement_executor_spec.rb +50 -0
  57. data/vendor/rubyslim-0.1.1/spec/statement_spec.rb +17 -0
  58. data/vendor/rubyslim-0.1.1/spec/table_to_hash_converter_spec.rb +31 -0
  59. metadata +241 -0
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ .yardoc/*
3
+ .rvmrc
4
+ selenium-rc.log
@@ -0,0 +1,7 @@
1
+ --readme docs/index.md
2
+ --markup markdown
3
+ --no-highlight
4
+ --exclude lib/rsel/selenium.rb
5
+ lib/rsel/*.rb
6
+ -
7
+ docs/*.md
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Automation Excellence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ Rsel
2
+ ====
3
+
4
+ Rsel connects [FitNesse](http://fitnesse.org) to [Selenium](http://seleniumhq.org)
5
+ via [Ruby](http://ruby-lang.org).
6
+
7
+ [Full documentation is on rdoc.info](http://rdoc.info/github/a-e/rsel/master/frames).
8
+
9
+
10
+ Copyright
11
+ ---------
12
+
13
+ The MIT License
14
+
15
+ Copyright (c) 2011 Automation Excellence
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining
18
+ a copy of this software and associated documentation files (the
19
+ "Software"), to deal in the Software without restriction, including
20
+ without limitation the rights to use, copy, modify, merge, publish,
21
+ distribute, sublicense, and/or sell copies of the Software, and to
22
+ permit persons to whom the Software is furnished to do so, subject to
23
+ the following conditions:
24
+
25
+ The above copyright notice and this permission notice shall be
26
+ included in all copies or substantial portions of the Software.
27
+
28
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
+
@@ -0,0 +1,75 @@
1
+ require 'rake'
2
+ require 'net/http'
3
+ require 'selenium/rake/tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ ROOT_DIR = File.expand_path(File.dirname(__FILE__))
7
+ TEST_APP = File.join(ROOT_DIR, 'test', 'app.rb')
8
+ SELENIUM_RC_JAR = File.join(ROOT_DIR, 'test', 'server', 'selenium-server-1.0.3-SNAPSHOT-standalone.jar')
9
+ SELENIUM_RC_LOG = File.join(ROOT_DIR, 'selenium-rc.log')
10
+
11
+ namespace 'testapp' do
12
+ desc "Start the test webapp in the background"
13
+ task :start do
14
+ puts "Starting the test webapp: #{TEST_APP}"
15
+ system("ruby #{TEST_APP} &")
16
+ end
17
+
18
+ desc "Stop the test webapp"
19
+ task :stop do
20
+ puts "Stopping the test webapp"
21
+ begin
22
+ Net::HTTP.get('localhost', '/shutdown', 8070)
23
+ rescue EOFError
24
+ # This is expected
25
+ puts "Test webapp stopped."
26
+ end
27
+ end
28
+ end
29
+
30
+ Selenium::Rake::RemoteControlStartTask.new do |rc|
31
+ rc.jar_file = SELENIUM_RC_JAR
32
+ rc.port = 4444
33
+ rc.background = true
34
+ rc.timeout_in_seconds = 60
35
+ rc.wait_until_up_and_running = true
36
+ rc.log_to = SELENIUM_RC_LOG
37
+ end
38
+
39
+ Selenium::Rake::RemoteControlStopTask.new do |rc|
40
+ rc.host = 'localhost'
41
+ rc.port = 4444
42
+ rc.timeout_in_seconds = 60
43
+ rc.wait_until_stopped = true
44
+ end
45
+
46
+ desc "Run spec tests"
47
+ RSpec::Core::RakeTask.new(:spec) do |t|
48
+ t.pattern = 'spec/**/*_spec.rb'
49
+ t.rspec_opts = ['--color', '--format doc']
50
+ end
51
+
52
+ namespace 'servers' do
53
+ desc "Start the Selenium and testapp servers"
54
+ task :start do
55
+ Rake::Task['testapp:start'].invoke
56
+ Rake::Task['selenium:rc:start'].invoke
57
+ end
58
+
59
+ desc "Stop the Selenium and testapp servers"
60
+ task :stop do
61
+ Rake::Task['selenium:rc:stop'].invoke
62
+ Rake::Task['testapp:stop'].invoke
63
+ end
64
+ end
65
+
66
+ desc "Start Selenium and testapp servers, run tests, then stop servers"
67
+ task :test do
68
+ begin
69
+ Rake::Task['servers:start'].invoke
70
+ Rake::Task['spec'].invoke
71
+ ensure
72
+ Rake::Task['servers:stop'].invoke
73
+ end
74
+ end
75
+
@@ -0,0 +1,87 @@
1
+ Customization
2
+ -------------
3
+
4
+ Rsel provides only the most basic imperative navigational steps and
5
+ verification. For real-world testing, you will most likely want to define your
6
+ own steps, built up from the low-level ones provided by Rsel.
7
+
8
+ It's pretty easy to do this by subclassing `SeleniumTest` and adding your own
9
+ methods to it. Create a sibling directory to your `FitNesseRoot`, named
10
+ something like `custom_rsel`, then create a Ruby file in there. The Ruby file
11
+ can be called whatever you want--it's most logical to name it after your
12
+ application:
13
+
14
+ - `FitNesseRoot`
15
+ - `custom_rsel`
16
+ - `my_app_test.rb`
17
+
18
+ Inside `my_app_test.rb`, you must define a module named after the folder, and a
19
+ class named after the file. You'll also need to include some `require` statements
20
+ to make sure `rsel/selenium` gets loaded. In this case, it should be:
21
+
22
+ require `rubygems` # If you installed Rsel from a gem
23
+ require `rsel/selenium_test` # Makes the SeleniumTest class available
24
+
25
+ module CustomRsel
26
+ class MyAppTest < Rsel::SeleniumTest
27
+ # Custom methods go here
28
+ end
29
+ end
30
+
31
+ Inside your custom class, you can define any methods you want, and you can call
32
+ any of the methods defined in the `Rsel::SeleniumTest` class. For example,
33
+ let's say the login process for your application consists of three steps:
34
+
35
+ | Fill in | Username | with | admin |
36
+ | Fill in | Password | with | letmein |
37
+ | Press | Login |
38
+
39
+ If you're logging in a lot, you may want a single-step login method. Here's how
40
+ you might define one:
41
+
42
+ module CustomRsel
43
+ class MyAppTest < Rsel::SeleniumTest
44
+
45
+ # Login with the given username and password
46
+ #
47
+ # Example:
48
+ # | Login as | admin | with password | letmein |
49
+ #
50
+ def login_as_with_password(username, password)
51
+ fill_in_with("Username", username)
52
+ fill_in_with("Password", password)
53
+ press("Login")
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ In your FitNesse `SetUp` page, rather than (or in addition to) importing the `Rsel` module,
60
+ import your custom module:
61
+
62
+ !| import |
63
+ | CustomRsel |
64
+
65
+ Note that this name must match the `module` line in your Ruby file, and the
66
+ folder where your Ruby file resides must be the lowercase_and_underscore
67
+ version of that same module name. Finally, in your actual test table, instead of:
68
+
69
+ !| script | selenium test | ... |
70
+
71
+ You'll use:
72
+
73
+ !| script | my app test | ... |
74
+
75
+ This will ensure that the `MyAppTest` class will be used for evaluating the
76
+ steps contained in that table.
77
+
78
+ It's worth noting that the `SeleniumTest` class (and hence any derived classes)
79
+ have a `@browser` attribute referring to the underlying `Selenium::Client::Driver`
80
+ instance, which includes
81
+ [a wealth of methods](http://rdoc.info/github/ph7/selenium-client/master/Selenium/Client/Driver)
82
+ for interacting with webpages and performing verifications. If `SeleniumTest`
83
+ itself does not provide the methods you need, you can use the `@browser`
84
+ attribute directly.
85
+
86
+ Next: [Development](development.md)
87
+
@@ -0,0 +1,48 @@
1
+ Development
2
+ -----------
3
+
4
+ If you would like to contribute to development of Rsel, create a fork of the
5
+ [Github repository](https://github.com/a-e/rsel), clone your fork, and submit
6
+ pull requests for any improvements you would like to share.
7
+
8
+ While developing, you should run the RSpec tests frequently to ensure nothing
9
+ gets broken. You can do this with a single `rake` command:
10
+
11
+ $ rake test
12
+
13
+ This will do the following:
14
+
15
+ - Start the Sinatra test application (code in `test/app.rb` and `test/views/*.erb`)
16
+ - Start the Selenium server (the `.jar` file in `test/server`)
17
+ - Run RSpec
18
+ - Stop the Selenium server
19
+ - Stop the Sinatra test application
20
+
21
+ Since there's a large startup cost associated with all of this (in particular
22
+ the Selenium server), when running frequent tests you may want to start those
23
+ up and keep them running. There are rake tasks for that also:
24
+
25
+ $ rake servers:start
26
+
27
+ Now do:
28
+
29
+ $ rake spec
30
+
31
+ as many times as you want. When you're done, manually stop the servers:
32
+
33
+ $ rake servers:stop
34
+
35
+ You can also start the Selenium and Sinatra testapp servers individually:
36
+
37
+ $ rake selenium:rc:start
38
+ $ rake testapp:start
39
+
40
+ and stop them:
41
+
42
+ $ rake selenium:rc:stop
43
+ $ rake testapp:stop
44
+
45
+ If you are developing new features, please add spec tests in the `spec`
46
+ directory!
47
+
48
+ Next: [To-do list](todo.md)
@@ -0,0 +1,13 @@
1
+ Rsel
2
+ ====
3
+
4
+ Rsel connects [FitNesse](http://fitnesse.org) to
5
+ [Selenium](http://seleniumhq.org) via [Ruby](http://ruby-lang.org). It allows
6
+ you to use a natural English syntax to write web application tests.
7
+
8
+ - [Installation & Configuration](install.md)
9
+ - [Usage](usage.md)
10
+ - [Customization](custom.md)
11
+ - [Development](development.md)
12
+ - [To-do list](todo.md)
13
+
@@ -0,0 +1,33 @@
1
+ Installation & Configuration
2
+ ----------------------------
3
+
4
+ To install Rsel from a gem:
5
+
6
+ $ gem install rsel
7
+
8
+ If you have the following test hierarchy:
9
+
10
+ - `FitNesseRoot`
11
+ - `SeleniumTests`
12
+ - `SetUp`
13
+ - `LoginTest`
14
+
15
+ You should define the requisite `rubyslim` options in the `SeleniumTests` page content:
16
+
17
+ !define TEST_SYSTEM {slim}
18
+ !define TEST_RUNNER {rubyslim}
19
+ !define COMMAND_PATTERN {rubyslim}
20
+
21
+ If you're using Bundler, you may need to use:
22
+
23
+ !define TEST_SYSTEM {slim}
24
+ !define TEST_RUNNER {bundle exec rubyslim}
25
+ !define COMMAND_PATTERN {bundle exec rubyslim}
26
+
27
+ Finally, put this in your `SeleniumTests.SetUp` page:
28
+
29
+ !| import |
30
+ | Rsel |
31
+
32
+ Next: [Usage](usage.md)
33
+
@@ -0,0 +1,15 @@
1
+ To-do list
2
+ ----------
3
+
4
+ - Pass better error messages back to FitNesse. It seems that Slim script tables
5
+ only support true/false results, with no clear way to report on what went
6
+ wrong (aside from raising an exception, which even then curiously does not
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)
10
+
11
+ Some additional step methods needed:
12
+
13
+ - dropdown_includes
14
+ - dropdown_equals
15
+
@@ -0,0 +1,50 @@
1
+ Usage
2
+ -----
3
+
4
+ Once you have created your `SetUp` page, you can create sibling pages with
5
+ tests in them. For instance, continuing with the example from
6
+ [Installation & Configuration](install.md), your `SeleniumTests.LoginTest`
7
+ might look like this:
8
+
9
+ !| script | selenium test | http://www.mysite.com |
10
+ | Open browser |
11
+ | Fill in | Username | with | castle |
12
+ | Fill in | Password | with | beckett |
13
+ | Click button | Log in |
14
+ | Page loads in | 5 | seconds or less |
15
+ | See | Logged in as castle |
16
+ | Close browser |
17
+
18
+ Before running a test, you must make sure you have Selenium Server installed and running.
19
+ Download [selenium-server-standalone-x.x.x.jar](http://seleniumhq.org/download/), and start
20
+ it up like this:
21
+
22
+ $ java -jar selenium-server-standalone-x.x.x.jar
23
+
24
+ By default, the server runs on port 4444, and this is the port that Rsel uses
25
+ unless you tell it otherwise. Rsel also assumes that you're running
26
+ selenium-server on your localhost (that is, the same host where FitNesse is
27
+ running); if you need to use a different host or port number, pass those as
28
+ arguments to the first line of the table. For example, if you are running
29
+ selenium-server on `my.selenium.host`, port `4455`, do this:
30
+
31
+ !| script | selenium test | http://www.mysite.com | my.selenium.host | 4455 |
32
+
33
+ The first argument after `selenium test` is the URL of the site you will be testing.
34
+ This URL is loaded when you call `Open browser`, and all steps that follow are
35
+ assumed to stay within the same domain. You can navigate around the site by
36
+ clicking links and filling in forms just as a human user would; you can also go
37
+ directly to a specific path within the domain with the `Visit` method:
38
+
39
+ | Visit | /some/path |
40
+ | Visit | /some/other/path |
41
+
42
+ These paths are evaluated relative to the domain your test is running in. (It's
43
+ theoretically possible to navigate to a different domain, but the Selenium
44
+ driver frowns upon it.)
45
+
46
+ See the `SeleniumTest` class documentation for a full list of available methods
47
+ and how to use them.
48
+
49
+ Next: [Customization](custom.md)
50
+
@@ -0,0 +1,4 @@
1
+ require 'rsel/selenium_test'
2
+
3
+ module Rsel
4
+ end
@@ -0,0 +1,4 @@
1
+ module Rsel
2
+ class LocatorNotFound < RuntimeError; end
3
+ class SeleniumNotRunning < RuntimeError; end
4
+ end
@@ -0,0 +1,524 @@
1
+ require File.join(File.dirname(__FILE__), 'exceptions')
2
+
3
+ require 'rubygems'
4
+ require 'xpath'
5
+ require 'selenium/client'
6
+
7
+ module Rsel
8
+
9
+ # Main Selenium-test class.
10
+ #
11
+ # @example
12
+ # !| 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.
26
+ #
27
+ class SeleniumTest
28
+
29
+ # Initialize a test, connecting to the given Selenium server.
30
+ #
31
+ # @param [String] url
32
+ # Full URL, including http://, of the system under test
33
+ # @param [String] host
34
+ # IP address or hostname where selenium-server is running
35
+ # @param [String] port
36
+ # Port number of selenium-server
37
+ # @param [String] browser
38
+ # Which browser to test with
39
+ #
40
+ # @example
41
+ # | script | selenium test | http://site.to.test/ |
42
+ # | script | selenium test | http://site.to.test/ | 192.168.0.3 | 4445 |
43
+ #
44
+ def initialize(url, host='localhost', port='4444', browser='*firefox')
45
+ @url = url
46
+ @browser = Selenium::Client::Driver.new(
47
+ :host => host,
48
+ :port => port,
49
+ :browser => browser,
50
+ :url => url)
51
+ end
52
+
53
+ attr_reader :url, :browser
54
+
55
+
56
+ # Start the session and open a browser to the URL defined at the start of
57
+ # the test.
58
+ #
59
+ # @example
60
+ # | Open browser |
61
+ #
62
+ def open_browser
63
+ begin
64
+ @browser.start_new_browser_session
65
+ rescue
66
+ # TODO: Find a way to make the test abort here
67
+ raise SeleniumNotRunning, "Could not start Selenium."
68
+ else
69
+ visit @url
70
+ end
71
+ end
72
+
73
+
74
+ # Stop the session and close the browser window.
75
+ #
76
+ # @example
77
+ # | Close browser |
78
+ #
79
+ def close_browser
80
+ @browser.close_current_browser_session
81
+ return true
82
+ end
83
+
84
+
85
+ # Load an absolute URL or a relative path in the browser.
86
+ #
87
+ # @param [String] path_or_url
88
+ # Relative path or absolute URL to load. Absolute URLs must be in the
89
+ # same domain as the URL that was passed to {#initialize}.
90
+ #
91
+ # @example
92
+ # | Visit | http://www.automation-excellence.com |
93
+ # | Visit | /software |
94
+ #
95
+ def visit(path_or_url)
96
+ return_error_status do
97
+ @browser.open(path_or_url)
98
+ end
99
+ end
100
+
101
+
102
+ # Click the Back button to navigate to the previous page.
103
+ #
104
+ # @example
105
+ # | Click back |
106
+ #
107
+ def click_back
108
+ return_error_status do
109
+ @browser.go_back
110
+ end
111
+ end
112
+
113
+
114
+ # Reload the current page.
115
+ #
116
+ # @example
117
+ # | refresh page |
118
+ #
119
+ def refresh_page
120
+ return_error_status do
121
+ @browser.refresh
122
+ end
123
+ end
124
+
125
+
126
+ # Maximize the browser window. May not work in some browsers.
127
+ #
128
+ # @example
129
+ # | maximize browser |
130
+ #
131
+ def maximize_browser
132
+ @browser.window_maximize
133
+ return true
134
+ end
135
+
136
+
137
+ # Ensure that the given text appears on the current page.
138
+ #
139
+ # @param [String] text
140
+ # Plain text that should be visible on the current page
141
+ #
142
+ # @example
143
+ # | See | Welcome, Marcus |
144
+ #
145
+ def see(text)
146
+ return @browser.text?(text)
147
+ end
148
+
149
+
150
+ # Ensure that the given text does not appear on the current page.
151
+ #
152
+ # @param [String] text
153
+ # Plain text that should not be visible on the current page
154
+ #
155
+ # @example
156
+ # | Do not see | Take a hike |
157
+ #
158
+ def do_not_see(text)
159
+ return !@browser.text?(text)
160
+ end
161
+
162
+
163
+ # Ensure that the current page has the given title text.
164
+ #
165
+ # @param [String] title
166
+ # Text of the page title that you expect to see
167
+ #
168
+ # @example
169
+ # | See title | Our Homepage |
170
+ #
171
+ def see_title(title)
172
+ return (@browser.get_title == title)
173
+ end
174
+
175
+
176
+ # Ensure that the current page does not have the given title text.
177
+ #
178
+ # @param [String] title
179
+ # Text of the page title that you should not see
180
+ #
181
+ # @example
182
+ # | Do not see title | Someone else's homepage |
183
+ #
184
+ def do_not_see_title(title)
185
+ return !(@browser.get_title == title)
186
+ end
187
+
188
+
189
+ # Type a value into the given field.
190
+ #
191
+ # @param [String] text
192
+ # What to type into the field
193
+ # @param [String] locator
194
+ # Label, name, or id of the field you want to type into
195
+ #
196
+ # @example
197
+ # | Type | Dale | into field | First name |
198
+ # | Type | Dale | into | First name | field |
199
+ #
200
+ def type_into_field(text, locator)
201
+ return_error_status do
202
+ @browser.type(xpath('field', locator), text)
203
+ end
204
+ end
205
+
206
+
207
+ # Fill in a field with the given text.
208
+ #
209
+ # @param [String] locator
210
+ # Label, name, or id of the field you want to type into
211
+ # @param [String] text
212
+ # What to type into the field
213
+ #
214
+ # @example
215
+ # | Fill in | First name | with | Eric |
216
+ #
217
+ def fill_in_with(locator, text)
218
+ type_into_field(text, locator)
219
+ end
220
+
221
+
222
+ # Verify that a text field contains the given text.
223
+ # The field may include additional text, as long as the
224
+ # expected value is in there somewhere.
225
+ #
226
+ # @param [String] locator
227
+ # Label, name, or id of the field you want to inspect
228
+ # @param [String] text
229
+ # Text to expect in the field
230
+ #
231
+ # @example
232
+ # | Field | First name | contains | Eric |
233
+ #
234
+ def field_contains(locator, text)
235
+ @browser.field(xpath('field', locator)).include?(text)
236
+ end
237
+
238
+
239
+ # Verify that a text field's value equals the given text.
240
+ # The value must match exactly.
241
+ #
242
+ # @param [String] locator
243
+ # Label, name, or id of the field you want to inspect
244
+ # @param [String] text
245
+ # Text to expect in the field
246
+ #
247
+ # @example
248
+ # | Field | First name | equals | Eric |
249
+ #
250
+ def field_equals(locator, text)
251
+ @browser.field(xpath('field', locator)) == text
252
+ end
253
+
254
+
255
+ # Click on a link or button, and wait for a page to load.
256
+ #
257
+ # @param [String] locator
258
+ # Text, value or id of the link or button to click
259
+ #
260
+ # @example
261
+ # | Click | Next |
262
+ # | Click | Logout |
263
+ #
264
+ def click(locator)
265
+ return_error_status do
266
+ @browser.click(xpath('link_or_button', locator))
267
+ end
268
+ end
269
+
270
+
271
+ # Click on a link.
272
+ #
273
+ # @param [String] locator
274
+ # Link text or id of the anchor element
275
+ #
276
+ # @example
277
+ # | Click | Logout | link |
278
+ # | Click link | Logout |
279
+ #
280
+ def click_link(locator)
281
+ return_error_status do
282
+ @browser.click(xpath('link', locator))
283
+ end
284
+ end
285
+
286
+
287
+ # Alias for {#click_link}.
288
+ #
289
+ # @example
290
+ # | Follow | Logout |
291
+ #
292
+ def follow(locator)
293
+ click_link(locator)
294
+ end
295
+
296
+
297
+ # Press a button.
298
+ #
299
+ # @param [String] locator
300
+ # Button text, value, or id of the button
301
+ #
302
+ # @example
303
+ # | Click | Login | button |
304
+ # | Click button | Login |
305
+ #
306
+ def click_button(locator)
307
+ return_error_status do
308
+ @browser.click(xpath('button', locator))
309
+ end
310
+ end
311
+
312
+
313
+ # Alias for {#click_button}
314
+ #
315
+ # @example
316
+ # | Press | Search |
317
+ #
318
+ def press(locator)
319
+ click_button(locator)
320
+ end
321
+
322
+
323
+ # Enable (check) a checkbox.
324
+ #
325
+ # @param [String] locator
326
+ # Label, value, or id of the checkbox to check
327
+ #
328
+ # @example
329
+ # | Enable | Send me spam | checkbox |
330
+ # | Enable checkbox | Send me spam |
331
+ #
332
+ def enable_checkbox(locator)
333
+ return_error_status do
334
+ @browser.check(xpath('checkbox', locator))
335
+ end
336
+ end
337
+
338
+
339
+ # Disable (uncheck) a checkbox.
340
+ #
341
+ # @param [String] locator
342
+ # Label, value, or id of the checkbox to uncheck
343
+ #
344
+ # @example
345
+ # | Disable | Send me spam | checkbox |
346
+ # | Disable checkbox | Send me spam |
347
+ #
348
+ def disable_checkbox(locator)
349
+ return_error_status do
350
+ @browser.uncheck(xpath('checkbox', locator))
351
+ end
352
+ end
353
+
354
+
355
+ # Verify that a given checkbox or radiobutton is enabled (checked)
356
+ #
357
+ # @param [String] locator
358
+ # Label, value, or id of the checkbox to inspect
359
+ #
360
+ # @example
361
+ # | Checkbox is enabled | send me spam |
362
+ # | Checkbox | send me spam | is enabled |
363
+ #
364
+ def checkbox_is_enabled(locator)
365
+ begin
366
+ enabled = @browser.checked?(xpath('checkbox', locator))
367
+ rescue
368
+ return false
369
+ else
370
+ return enabled
371
+ end
372
+ end
373
+
374
+
375
+ # Verify that a given checkbox or radiobutton is disabled (unchecked)
376
+ #
377
+ # @param [String] locator
378
+ # Label, value, or id of the checkbox to inspect
379
+ #
380
+ # @example
381
+ # | Checkbox is disabled | send me spam |
382
+ #
383
+ def checkbox_is_disabled(locator)
384
+ begin
385
+ enabled = @browser.checked?(xpath('checkbox', locator))
386
+ rescue
387
+ return false
388
+ else
389
+ return !enabled
390
+ end
391
+ end
392
+
393
+
394
+ # Select a radio button.
395
+ #
396
+ # @param [String] locator
397
+ # Label, id, or name of the radio button to select
398
+ #
399
+ # @example
400
+ # | Select | female | radio |
401
+ # | Select radio | female |
402
+ #
403
+ def select_radio(locator)
404
+ return_error_status do
405
+ @browser.click(xpath('radio_button', locator))
406
+ end
407
+ end
408
+
409
+
410
+ # Alias for {#checkbox_is_disabled}
411
+ #
412
+ # @example
413
+ # | Radio is disabled | medium |
414
+ # | Radio | medium | is disabled |
415
+ #
416
+ def radio_is_disabled(locator)
417
+ checkbox_is_disabled(locator)
418
+ end
419
+
420
+
421
+ # Alias for {#checkbox_is_enabled}
422
+ #
423
+ # @example
424
+ # | Radio is enabled | medium |
425
+ # | Radio | medium | is enabled |
426
+ #
427
+ def radio_is_enabled(locator)
428
+ checkbox_is_enabled(locator)
429
+ end
430
+
431
+
432
+ # Select a value from a dropdown/combo box.
433
+ #
434
+ # @param [String] value
435
+ # The value to choose from the dropdown
436
+ # @param [String] locator
437
+ # Label, name, or id of the dropdown
438
+ #
439
+ # @example
440
+ # | select | Tall | from | Height | dropdown |
441
+ # | select | Tall | from dropdown | Height |
442
+ #
443
+ def select_from_dropdown(value, locator)
444
+ return_error_status do
445
+ @browser.select(xpath('select', locator), value)
446
+ end
447
+ end
448
+
449
+
450
+ # Pause for a certain number of seconds.
451
+ #
452
+ # @param [String, Int] seconds
453
+ # How many seconds to pause
454
+ #
455
+ # @example
456
+ # | Pause | 5 | seconds |
457
+ #
458
+ def pause_seconds(seconds)
459
+ sleep seconds.to_i
460
+ return true
461
+ end
462
+
463
+
464
+ # Wait some number of seconds for the current page request to finish.
465
+ # Fails if the page does not finish loading within the specified time.
466
+ #
467
+ # @param [String, Int] seconds
468
+ # How long to wait for before timing out
469
+ #
470
+ # @example
471
+ # | Page loads in | 10 | seconds or less |
472
+ #
473
+ def page_loads_in_seconds_or_less(seconds)
474
+ return_error_status do
475
+ @browser.wait_for_page_to_load("#{seconds}000")
476
+ end
477
+ end
478
+
479
+
480
+ # Execute the given block, and return false if it raises an exception.
481
+ # Otherwise, return true.
482
+ #
483
+ # @example
484
+ # return_error_status do
485
+ # # some code that might raise an exception
486
+ # end
487
+ #
488
+ def return_error_status
489
+ begin
490
+ yield
491
+ rescue => e
492
+ #puts e.message
493
+ #puts e.backtrace
494
+ return false
495
+ else
496
+ return true
497
+ end
498
+ end
499
+
500
+ # Return a Selenium-style xpath for the given locator.
501
+ #
502
+ # @param [String] kind
503
+ # What kind of locator you're using (link, button, checkbox, field etc.).
504
+ # This must correspond to a method name in `XPath::HTML`.
505
+ # @param [String] locator
506
+ # Name, id, value, label or whatever locator the `XPath::HTML.<kind>`
507
+ # method accepts.
508
+ #
509
+ # @example
510
+ # xpath('link', 'Log in')
511
+ # xpath('button', 'Submit')
512
+ # xpath('field', 'First name')
513
+ #
514
+ def xpath(kind, locator)
515
+ # Doing explicit string-join here, so it'll work with older versions
516
+ # of the XPath module that don't have `Union#to_s` defined.
517
+ "xpath=" + XPath::HTML.send(kind, locator).to_xpaths.join(' | ')
518
+ end
519
+
520
+ end
521
+ end
522
+
523
+
524
+