watir_robot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/KEYWORDS.rdoc +83 -0
- data/LICENSE +24 -0
- data/Manifest +28 -0
- data/README.rdoc +116 -0
- data/Rakefile +50 -0
- data/lib/watir_robot.rb +56 -0
- data/lib/watir_robot/exception.rb +76 -0
- data/lib/watir_robot/keywords/browser.rb +317 -0
- data/lib/watir_robot/keywords/button.rb +21 -0
- data/lib/watir_robot/keywords/checkbox.rb +53 -0
- data/lib/watir_robot/keywords/element.rb +95 -0
- data/lib/watir_robot/keywords/file_field.rb +30 -0
- data/lib/watir_robot/keywords/form.rb +143 -0
- data/lib/watir_robot/keywords/image.rb +18 -0
- data/lib/watir_robot/keywords/link.rb +21 -0
- data/lib/watir_robot/keywords/list.rb +42 -0
- data/lib/watir_robot/keywords/native.rb +148 -0
- data/lib/watir_robot/keywords/page.rb +324 -0
- data/lib/watir_robot/keywords/radio.rb +44 -0
- data/lib/watir_robot/keywords/select.rb +82 -0
- data/lib/watir_robot/keywords/table.rb +100 -0
- data/lib/watir_robot/keywords/text_field.rb +69 -0
- data/lib/watir_robot/parser.rb +170 -0
- data/scripts/example_tests/resource.txt +14 -0
- data/scripts/example_tests/test.txt +171 -0
- data/scripts/run_server.rb +13 -0
- data/watir_robot.gemspec +36 -0
- metadata +152 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.1.0. Initial gem package for Watir Robot. Includes healthy base of Robot Framework keywords for driving the browser, taking a screenshot and using the mouse natively.
|
data/KEYWORDS.rdoc
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= Currently Available Keywords
|
2
|
+
|
3
|
+
The following list of functions map to Robot Framework keywords. The keyword is derived from removing the hash symbol and underscores, and capitalizing the first letter of each word in the function name.
|
4
|
+
|
5
|
+
This documentation has been copied from YARD's output.
|
6
|
+
|
7
|
+
== Methods included from TextField
|
8
|
+
#input_password, #input_text, #textfield_should_contain, #textfield_text_should_be
|
9
|
+
|
10
|
+
== Methods included from Table
|
11
|
+
#get_table_cell, #table_cell_should_contain, #table_column_should_contain, #table_header_should_contain, #table_row_should_contain, #table_should_contain
|
12
|
+
|
13
|
+
== Methods included from Select
|
14
|
+
#clear_items_from_select_list, #select_all_items_from_list, #select_item_from_list
|
15
|
+
|
16
|
+
== Methods included from Radio
|
17
|
+
#radio_button_should_be_selected, #radio_button_should_not_be_selected, #select_radio_button
|
18
|
+
|
19
|
+
== Methods included from List
|
20
|
+
#get_list_items
|
21
|
+
|
22
|
+
== Methods included from Link
|
23
|
+
#click_link
|
24
|
+
|
25
|
+
== Methods included from Image
|
26
|
+
#click_image
|
27
|
+
|
28
|
+
== Methods included from CheckBox
|
29
|
+
#checkbox_should_be_selected, #checkbox_should_not_be_selected, #select_checkbox, #unselect_checkbox
|
30
|
+
|
31
|
+
== Methods included from Button
|
32
|
+
#click_button
|
33
|
+
|
34
|
+
== Methods included from Element
|
35
|
+
#click_element, #element_should_be_visible, #element_should_not_be_visible, #element_text_should_be, #element_text_should_contain, #focus, #get_element_attribute, #get_element_text
|
36
|
+
|
37
|
+
== Methods included from Page
|
38
|
+
#execute_javascript, #get_all_elements, #get_page_source, #get_page_status, #get_page_text, #get_title, #log_page_source, #page_should_contain, #page_should_contain_button, #page_should_contain_checkbox, #page_should_contain_element, #page_should_contain_image, #page_should_contain_link, #page_should_contain_list, #page_should_contain_radio_button, #page_should_contain_textfield, #page_should_not_contain, #page_should_not_contain_button, #page_should_not_contain_checkbox, #page_should_not_contain_element, #page_should_not_contain_image, #page_should_not_contain_link, #page_should_not_contain_list, #page_should_not_contain_radio_button, #page_should_not_contain_textfield, #title_should_be, #title_should_contain
|
39
|
+
|
40
|
+
== Methods included from Native
|
41
|
+
#capture_screenshot, #click_left_mouse_button, #click_right_mouse_button, #drag_and_drop, #move_mouse_to_position, #press_left_mouse_button, #press_right_mouse_button, #release_left_mouse_button, #release_right_mouse_button
|
42
|
+
|
43
|
+
== Methods included from Browser
|
44
|
+
#close_browser, #delete_all_cookies, #delete_cookie, #get_all_cookies, #get_cookie, #get_url, #go_back, #go_forward, #go_to, #maximize_browser_window, #open_browser, #refresh, #start_browser, #url_should_be, #url_should_contain
|
45
|
+
|
46
|
+
= Possible Future Keywords
|
47
|
+
* Alert Should Be Present (maybe)
|
48
|
+
* Choose Cancel On Next Confirmation (requires optional require: 'watir-webdriver/extensions/alerts')
|
49
|
+
* Close All Browsers (currently only support single browser)
|
50
|
+
* Close Window (WebDriver switches windows, you needn't close them, they'll close when Browser is killed)
|
51
|
+
* Confirm Action (optional require, see choose cancel above)
|
52
|
+
* Current Frame Should Contain
|
53
|
+
* Drag And Drop
|
54
|
+
* Frame Should Contain
|
55
|
+
* Get Alert Message
|
56
|
+
* Get All Links (return id, text;;)
|
57
|
+
* Get Horizontal Position (via JS)
|
58
|
+
* Get Matching Xpath Count = Get Number of Matches
|
59
|
+
* Get Vertical Position (via JS)
|
60
|
+
* Get Window Identifiers
|
61
|
+
* Get Window Names
|
62
|
+
* Get Window Titles
|
63
|
+
* List Selection Should Be
|
64
|
+
* List Should Have No Selections
|
65
|
+
* Mouse Down On Image
|
66
|
+
* Mouse Down On Link
|
67
|
+
* Mouse Out
|
68
|
+
* Mouse Over
|
69
|
+
* Open Context Menu (?)
|
70
|
+
* Press Key
|
71
|
+
* Press Key Native
|
72
|
+
* Register Keyword To Run On Failure
|
73
|
+
* Select Frame
|
74
|
+
* Select Window
|
75
|
+
* Simulate
|
76
|
+
* Switch Browser
|
77
|
+
* Table Footer Should Contain
|
78
|
+
* Unselect Frame
|
79
|
+
* Wait For Condition
|
80
|
+
* Wait Until Page Contains
|
81
|
+
* Wait Until Page Contains Element
|
82
|
+
* Wait Until Page Loaded
|
83
|
+
* Xpath Should Match X Times
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2010, Daniel L. Gregoire
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of Watir Robot, Semperos nor the
|
12
|
+
names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL DANIEL L. GREGOIRE BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Manifest
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
KEYWORDS.rdoc
|
3
|
+
LICENSE
|
4
|
+
Manifest
|
5
|
+
README.rdoc
|
6
|
+
Rakefile
|
7
|
+
lib/watir_robot.rb
|
8
|
+
lib/watir_robot/exception.rb
|
9
|
+
lib/watir_robot/keywords/browser.rb
|
10
|
+
lib/watir_robot/keywords/button.rb
|
11
|
+
lib/watir_robot/keywords/checkbox.rb
|
12
|
+
lib/watir_robot/keywords/element.rb
|
13
|
+
lib/watir_robot/keywords/file_field.rb
|
14
|
+
lib/watir_robot/keywords/form.rb
|
15
|
+
lib/watir_robot/keywords/image.rb
|
16
|
+
lib/watir_robot/keywords/link.rb
|
17
|
+
lib/watir_robot/keywords/list.rb
|
18
|
+
lib/watir_robot/keywords/native.rb
|
19
|
+
lib/watir_robot/keywords/page.rb
|
20
|
+
lib/watir_robot/keywords/radio.rb
|
21
|
+
lib/watir_robot/keywords/select.rb
|
22
|
+
lib/watir_robot/keywords/table.rb
|
23
|
+
lib/watir_robot/keywords/text_field.rb
|
24
|
+
lib/watir_robot/parser.rb
|
25
|
+
scripts/example_tests/resource.txt
|
26
|
+
scripts/example_tests/test.txt
|
27
|
+
scripts/run_server.rb
|
28
|
+
watir_robot.gemspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
= Watir Robot Library
|
2
|
+
|
3
|
+
Watir Robot is a testing library for Robot Framework which provides keywords for automated web testing, using the Watir-WebDriver library to drive the browser. Because Watir-WebDriver is written in Ruby, this project interoperates with Robot Framework via its Remote Library interface.
|
4
|
+
|
5
|
+
See {this project's wiki}[https://github.com/semperos/watir-robot/wiki] for tutorials and further documentation. See the Watir Robot Users and Watir Robot Devel Google groups to join in the discussion or ask questions of the community.
|
6
|
+
|
7
|
+
== Overview
|
8
|
+
|
9
|
+
Although Robot Framework is designed to work with Python and Java testing libraries natively, it also provides a Remote Library interface which allows testing libraries written in any language to be used with Robot Framework. These testing libraries provide the first, low-level "layer" of keywords, from which can be built more complex test suites. Watir Robot provides keywords specific to automated functional web testing, using the Watir library for driving the browser. The goal of this project is to provide a base of keywords similar to the existing SeleniumLibrary available for Robot Framework.
|
10
|
+
|
11
|
+
This project is designed to be used in tandem with the +robot_remote_server+ gem hosted on Rubygems.org, with source code {on Github}[http://github.com/semperos/robot-remote-server]. In order to supply Robot Framework with keywords from Watir Robot, it is necessary to install that gem and run the included server application, feeding it an instance of the WatirRobot class.
|
12
|
+
|
13
|
+
== Terminology
|
14
|
+
|
15
|
+
* <b>Robot Framework:</b> A generic keyword-driven test automation framework. Map keywords to functions, then build a higher-level DSL by building keywords from keywords.
|
16
|
+
* <b>Robot Framework's Remote Library:</b> One of the standard libraries provided by Robot Framework, this is the interface which allows you to write a Remote Server in any language, providing the bridge between the Python/Java world of Robot Framework and your own testing library.
|
17
|
+
* <b>Remote Server:</b> Not part of Robot Framework, you can write a remote server to create a bridge between your testing library and Robot Framework by following the conventions of its Remote Library. A Python and Ruby implementation are provided as examples in Robot Framework's source code. A Ruby gem called +robot_remote_server+ is available on Rubygems.org and {here on Github}[http://github.com/semperos/robot-remote-server]
|
18
|
+
* <b>Remote Testing Library:</b> Robot Framework runs on keywords; the testing library is that first layer of keywords that maps keywords to functions. These can be written in any language, as long as you write/use a Remote Server written in the same language. This project is a Testing Library which uses Ruby and Watir to provide that keyword base.
|
19
|
+
|
20
|
+
These represent the full stack; on top of this stack, you write actual Robot Framework test cases. These can be written in a number of formats (text, HTML, etc.) and can be edited using the {RIDE (Robot IDE)}[http://code.google.com/p/robotframework-ride].
|
21
|
+
|
22
|
+
== Installation
|
23
|
+
|
24
|
+
=== Ruby Compatibility
|
25
|
+
|
26
|
+
You must be running <b>JRuby >=1.5.3</b> to use this library.
|
27
|
+
|
28
|
+
*Note:* If you want to generate the YARD documentation for this project yourself, you should use MRI Ruby >=1.9.1 to do so, but JRuby for running the actual code.
|
29
|
+
|
30
|
+
=== Dependencies
|
31
|
+
|
32
|
+
* +robot_remote_server+, which provides the Ruby interface to Robot Framework' Remote Library
|
33
|
+
* +yard+, as a _runtime_ dependency, because the YARD Registry is used to feed Robot Framework and RIDE metadata about defined keywords.
|
34
|
+
|
35
|
+
== Usage
|
36
|
+
|
37
|
+
=== Context and Overview
|
38
|
+
|
39
|
+
This projects relies on the following stack (see Robot Framework's {Remote Library documentation}[http://code.google.com/p/robotframework/wiki/RemoteLibrary]):
|
40
|
+
|
41
|
+
* Robot Framework
|
42
|
+
* Remote Library
|
43
|
+
|
44
|
+
...which we talk to with this stack:
|
45
|
+
|
46
|
+
* Remote Server
|
47
|
+
* Test Library (this project)
|
48
|
+
* Application being tested
|
49
|
+
|
50
|
+
Robot Framework relies on keywords to execute tests. The framework itself defines some keywords for setting up test suite dependencies, converting values to different types, setting up conditional workflows, and defining test assertions (see full lists in the Robot Framework {Built-In Library documentation}[http://code.google.com/p/robotframework/wiki/BuiltInLibrary]). Watir Robot, as a Test Library, defines its own set of keywords specific to the domain of automated web testing, including interacting with web pages and manipulating web browsers.
|
51
|
+
|
52
|
+
So, you're going to write regular Robot Framework tests, leveraging the Watir Robot library for extra keywords. The one catch is that Watir is written in Ruby, and Robot Framework only supports test libraries in Python and Java by default. However, Robot Framework has a Remote Library which allows us to interface with the main framework by running a local Remote Server instance that can communicate with the main framework (via XML-RPC). By writing a Remote Server in the language of our Test Library, we can define keywords in any programming language and use them within Robot Framework.
|
53
|
+
|
54
|
+
A Remote Server has already been implemented in Ruby by the Robot Framework development team, and I've packaged it as a gem on Rubygems.org. So make sure you have the <tt>robot_remote_server</tt> gem installed in addition to the +watir_robot+ gem (+watir_robot+ defines +robot_remote_server+ as a dependency, but in case you've pulled this down and installed it manually, you must have both parts).
|
55
|
+
|
56
|
+
Once you've installed +robot_remote_server+, you'll need to start an instance of the server and "register" the Watir Robot library with it. Without a running server, there's no way for Watir Robot to tell Robot Framework what keywords it defines.
|
57
|
+
|
58
|
+
=== Practical Usage
|
59
|
+
|
60
|
+
To tell Robot Framework what keywords Watir Robot defines, we need to start the +robot_remote_server+ and pass in the main class that defines Watir Robot's keywords. You can accomplish this in two ways.
|
61
|
+
|
62
|
+
==== Start Remote Server and Run Tests
|
63
|
+
|
64
|
+
Run the file <tt>scripts/run_server.rb</tt> to start an instance of the remote server. This file feeds the remote server the <tt>WatirRobot::KeywordLibrary</tt> class. You can copy the code in that file and supply your own class name to provide your own keyword library class.
|
65
|
+
|
66
|
+
Once you've started the server, Robot Framework is aware of Watir Robot's keywords, and you can start building Robot Framework tests with Watir-based keywords!
|
67
|
+
|
68
|
+
For instructions on writing and running Robot Framework tests, see these resources:
|
69
|
+
|
70
|
+
* {Quick Start}[http://code.google.com/p/robotframework/wiki/QuickStartGuide]
|
71
|
+
* {User Guide}[http://code.google.com/p/robotframework/wiki/UserGuide]
|
72
|
+
* RIDE[http://code.google.com/p/robotframework-ride], an integrated development environment for writing Robot Framework tests
|
73
|
+
|
74
|
+
=== Keywords
|
75
|
+
|
76
|
+
This library primarily ships with Watir-based keywords for manipulating the browser. However, if used with JRuby, there are certain "native" keywords available for handling the mouse and keyboard.
|
77
|
+
|
78
|
+
This native functionality may eventually ship in a separate gem, but for now the <tt>lib/keywords/native.rb</tt> file contains the <tt>Native</tt> module. Require this module in <tt>watir_robot.rb</tt> and do <tt>include Native</tt> in the +WatirRobot+ class to leverage these keywords. They are currently disabled by default, since they necessitate the use of JRuby and Java dependencies.
|
79
|
+
|
80
|
+
=== Documentation
|
81
|
+
|
82
|
+
One of the facilities provided by the Remote Library/server system is the collection of documentation and method signatures for remote keywords. Due to the fact that Ruby completely ignores comments in source files, we have to use separate documentation tools to parse and store this information.
|
83
|
+
|
84
|
+
The YARD tool is a perfect fit for this situation due to its modular nature. YARD separates the concerns of parsing, storing and displaying documentation, allowing us to use any part independently of the others. Watir Robot leverages the storage component, specifically the "yardoc file" produced after a run of the +yardoc+ command, which contains metadata on every method defined in this code base.
|
85
|
+
|
86
|
+
==== Generate Documentation
|
87
|
+
|
88
|
+
If you need to re-generate the documentation locally, follow these instructions.
|
89
|
+
|
90
|
+
<b>Note:</b> MRI Ruby >=1.9.1 or later should be used to generate this project's documentation with YARD.
|
91
|
+
|
92
|
+
Make sure the +yard+ gem is installed for the version of Ruby you're using:
|
93
|
+
|
94
|
+
gem install yard
|
95
|
+
|
96
|
+
Then run the +yardoc+ command from this project's root. You should see output describing how many files were created and what percentage of your codebase is commented. You should also see a new folder called +.yardoc+ in your project's root; this is your "yardoc file."
|
97
|
+
|
98
|
+
By default, the +run_server.rb+ file which comes packaged with this project expects to find the yardoc file in the project's root folder. If you've adjusted the location of the yardoc file or adjusted the +run_server.rb+ file, you should ensure that it's pointing to the correct path.
|
99
|
+
|
100
|
+
= Why Watir? Which Watir?
|
101
|
+
|
102
|
+
First and most importantly, I feel that the Watir API is the cleanest and most intuitive among the open soure browser-driving frameworks available. Secondly, because it drives browsers using "native" drivers (not JavaScript manipulation), it is able to perform tasks that other tools are not.
|
103
|
+
|
104
|
+
I am currently using the Watir-WebDriver version of Watir to drive the browser, for the following reasons:
|
105
|
+
|
106
|
+
* It will become Watir 2.0
|
107
|
+
* It leverages the WebDriver library
|
108
|
+
* It supports more browsers than standard Watir (by virtue of WebDriver)
|
109
|
+
* It has one dependency, Selenium-WebDriver, whereas standard Watir requires a separate driver gem per browser
|
110
|
+
* It's receiving more active development than standard Watir at this time
|
111
|
+
|
112
|
+
If folks have strong opinions about the use of Watir vs Watir-WebDriver vs Tool Of Your Choice, please join the discussion {on the wiki page}[https://github.com/semperos/watir-robot/wiki/Watir-Discussion]
|
113
|
+
|
114
|
+
= To-Do's
|
115
|
+
|
116
|
+
* Implement keywords that interact with/collect values from multiple elements
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'yard'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'echoe'
|
6
|
+
|
7
|
+
PROJECT_DIR = File.expand_path(File.dirname(__FILE__))
|
8
|
+
|
9
|
+
Echoe.new('watir_robot') do |p|
|
10
|
+
p.description = "Watir Robot - Remote keyword library for Robot Framework"
|
11
|
+
p.url = "http://github.com/semperos/watir-robot"
|
12
|
+
p.author = "Daniel Gregoire"
|
13
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
14
|
+
p.runtime_dependencies = ['robot_remote_server >=2.5.5.2', 'watir-webdriver >=0.1.8']
|
15
|
+
p.need_tar_gz = false
|
16
|
+
end
|
17
|
+
|
18
|
+
YARD::Rake::YardocTask.new do |t|
|
19
|
+
# t.files = ['lib/**/*.rb', OTHER_PATHS]
|
20
|
+
t.options = ['-b', 'yardoc']
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
namespace :clean do
|
25
|
+
desc "Remove Robot Framework test-run output"
|
26
|
+
task :rf do
|
27
|
+
FileUtils.rm Dir.glob(File.join('**', 'example_tests', '*.html'))
|
28
|
+
FileUtils.rm Dir.glob(File.join('**', 'example_tests', '*.xml'))
|
29
|
+
FileUtils.rm Dir.glob(File.join('**', '*.png'))
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Remove YARD documentation"
|
33
|
+
task :doc do
|
34
|
+
doc_dir = File.join(PROJECT_DIR, "doc")
|
35
|
+
FileUtils.rm_rf(doc_dir) if File.exists?(doc_dir)
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Remote YARD registry cache"
|
39
|
+
task :yardoc do
|
40
|
+
yardoc_dir = File.join(PROJECT_DIR, "yardoc")
|
41
|
+
FileUtils.rm_rf(yardoc_dir) if File.exists?(yardoc_dir)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Cleanup all generated files"
|
45
|
+
task :all do
|
46
|
+
Rake::Task['clean:rf'].invoke
|
47
|
+
Rake::Task['clean:doc'].invoke
|
48
|
+
Rake::Task['clean:yardoc'].invoke
|
49
|
+
end
|
50
|
+
end
|
data/lib/watir_robot.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'watir-webdriver'
|
2
|
+
|
3
|
+
require 'watir_robot/parser'
|
4
|
+
require 'watir_robot/exception'
|
5
|
+
|
6
|
+
require 'watir_robot/keywords/browser'
|
7
|
+
require 'watir_robot/keywords/element'
|
8
|
+
require 'watir_robot/keywords/button'
|
9
|
+
require 'watir_robot/keywords/checkbox'
|
10
|
+
require 'watir_robot/keywords/file_field'
|
11
|
+
require 'watir_robot/keywords/form'
|
12
|
+
require 'watir_robot/keywords/image'
|
13
|
+
require 'watir_robot/keywords/link'
|
14
|
+
require 'watir_robot/keywords/list'
|
15
|
+
require 'watir_robot/keywords/native'
|
16
|
+
require 'watir_robot/keywords/page'
|
17
|
+
require 'watir_robot/keywords/radio'
|
18
|
+
require 'watir_robot/keywords/select'
|
19
|
+
require 'watir_robot/keywords/table'
|
20
|
+
require 'watir_robot/keywords/text_field'
|
21
|
+
|
22
|
+
#
|
23
|
+
# The overarching module which contains all keyword definitions and utilities
|
24
|
+
#
|
25
|
+
module WatirRobot
|
26
|
+
|
27
|
+
#
|
28
|
+
# The class which defines Robot Framework keywords.
|
29
|
+
#
|
30
|
+
# Feed an instance of this class to an instance of +RobotRemoteServer+
|
31
|
+
# provided by the +robot_remote_server+ gem to make these keywords
|
32
|
+
# available to your Robot Framework tests and to RIDE.
|
33
|
+
#
|
34
|
+
class KeywordLibrary
|
35
|
+
include WatirRobot::Exception
|
36
|
+
include WatirRobot::Parser
|
37
|
+
|
38
|
+
include WatirRobot::Browser
|
39
|
+
include WatirRobot::Button
|
40
|
+
include WatirRobot::CheckBox
|
41
|
+
include WatirRobot::Element
|
42
|
+
include WatirRobot::FileField
|
43
|
+
include WatirRobot::Form
|
44
|
+
include WatirRobot::Image
|
45
|
+
include WatirRobot::Link
|
46
|
+
include WatirRobot::List
|
47
|
+
include WatirRobot::Native
|
48
|
+
include WatirRobot::Page
|
49
|
+
include WatirRobot::Radio
|
50
|
+
include WatirRobot::Select
|
51
|
+
include WatirRobot::Table
|
52
|
+
include WatirRobot::TextField
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module WatirRobot
|
2
|
+
|
3
|
+
#
|
4
|
+
# Custom exceptions for Watir Robot test cases
|
5
|
+
#
|
6
|
+
module Exception
|
7
|
+
|
8
|
+
#
|
9
|
+
# Exception when an object cannot be found on the given page
|
10
|
+
#
|
11
|
+
class ObjectDoesNotExist < RuntimeError; end
|
12
|
+
#
|
13
|
+
# Exception when an HTML element cannot be found on the given page
|
14
|
+
#
|
15
|
+
class ElementDoesNotExist < ObjectDoesNotExist; end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Exception when an object does not match expected parameters
|
19
|
+
#
|
20
|
+
class ObjectMatchError < RuntimeError; end
|
21
|
+
#
|
22
|
+
# Exception when an element does not match expected parameters
|
23
|
+
#
|
24
|
+
class ElementMatchError < ObjectMatchError; end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Exception when the page does not match expected parameters
|
28
|
+
#
|
29
|
+
class PageMatchError < ObjectMatchError; end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Exception when the page title does not match expected string value
|
33
|
+
#
|
34
|
+
class TitleMatchError < ObjectMatchError; end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Exception when the URL does not match the expected string value
|
38
|
+
#
|
39
|
+
class UrlMatchError < ObjectMatchError; end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Exception when trying to match a Window is unsuccessful
|
43
|
+
#
|
44
|
+
class WindowMatchError < ObjectMatchError; end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Exception when an object is visible or invisible and should be the opposite
|
48
|
+
#
|
49
|
+
class ObjectVisibilityError < RuntimeError; end
|
50
|
+
#
|
51
|
+
# Exception when an HTML element is visible or invisible and should be the opposite
|
52
|
+
#
|
53
|
+
class ElementVisibilityError < ObjectVisibilityError; end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Exception when object is "selected" or "unselected" when it should be the opposite
|
57
|
+
#
|
58
|
+
class ObjectSelectionError < RuntimeError; end
|
59
|
+
#
|
60
|
+
# Exception when a checkbox is selected or unselected when it should be the opposite
|
61
|
+
#
|
62
|
+
class CheckboxSelectionError < RuntimeError; end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Exception when a radio button is selected or unselected when it should be the opposite
|
66
|
+
#
|
67
|
+
class RadioSelectionError < RuntimeError; end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Exception when a select list has items selected or unselected which should be the opposite
|
71
|
+
#
|
72
|
+
class SelectListSelectionError < RuntimeError; end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
module WatirRobot
|
2
|
+
|
3
|
+
#
|
4
|
+
# Functionality at the browser level, including browser history and cookie handling
|
5
|
+
#
|
6
|
+
module Browser
|
7
|
+
|
8
|
+
### Actions ###
|
9
|
+
|
10
|
+
#
|
11
|
+
# Start browser
|
12
|
+
#
|
13
|
+
# @param [String] browser the name of the browser to use, must match a name recognized by WebDriver
|
14
|
+
# @return [Object] the browser object with which to drive the browser
|
15
|
+
#
|
16
|
+
def open_browser(browser = 'firefox')
|
17
|
+
@browser = Watir::Browser.new browser.to_sym
|
18
|
+
@driver = @browser.driver
|
19
|
+
@window_id = 0
|
20
|
+
return @browser
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Open browser and go to specific URL
|
25
|
+
#
|
26
|
+
# @param [String] url the URL to navigate to
|
27
|
+
# @param [String] browser the name of the browser to use, must match a name recognized by WebDriver
|
28
|
+
# @return [Object] the browser object with which to drive the browser
|
29
|
+
#
|
30
|
+
def start_browser(url, browser = 'firefox')
|
31
|
+
self.open_browser browser
|
32
|
+
@browser.goto url
|
33
|
+
return @browser
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Get current URL
|
38
|
+
#
|
39
|
+
# @return [String] URL of the current page
|
40
|
+
#
|
41
|
+
def get_url
|
42
|
+
@browser.url
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Go to specific URL in already-opened browser
|
47
|
+
#
|
48
|
+
# @param [String] url the URL to navigate to
|
49
|
+
#
|
50
|
+
def go_to(url)
|
51
|
+
@browser.goto(url)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Go back in browsing history
|
56
|
+
#
|
57
|
+
def go_back
|
58
|
+
@browser.back
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Go forward in browsing history
|
63
|
+
#
|
64
|
+
def go_forward
|
65
|
+
@browser.forward
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Refresh the current page
|
70
|
+
#
|
71
|
+
def refresh
|
72
|
+
@browser.refresh
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Get all cookies defined in the current session
|
77
|
+
#
|
78
|
+
# @return [Array<Hash>] list of cookies
|
79
|
+
#
|
80
|
+
def get_all_cookies
|
81
|
+
@driver.manage.all_cookies
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Get a cookie by name
|
86
|
+
#
|
87
|
+
# @param [String] name name of the cookie
|
88
|
+
# @return [Hash, nil] the cookie or nil, if it doesn't exist
|
89
|
+
#
|
90
|
+
def get_cookie(name)
|
91
|
+
@driver.manage.cookie_named(name)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Delete all browser cookies
|
96
|
+
#
|
97
|
+
def delete_all_cookies
|
98
|
+
@browser.clear_cookies
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Delete individual cookie, identified by name
|
103
|
+
#
|
104
|
+
# @param [String] name name of the cookie
|
105
|
+
#
|
106
|
+
def delete_cookie(name)
|
107
|
+
@driver.manage.delete_cookie(name)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Close current browser
|
112
|
+
#
|
113
|
+
def close_browser
|
114
|
+
@browser.close
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Maximize browser window (uses JavaScript)
|
119
|
+
#
|
120
|
+
def maximize_browser_window
|
121
|
+
@browser.execute_script(
|
122
|
+
"if (window.screen) {
|
123
|
+
window.moveTo(0, 0);
|
124
|
+
window.resizeTo(window.screen.availWidth, window.screen.availHeight);
|
125
|
+
};")
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Switch to "other" window; assumes there are only 2 open
|
130
|
+
#
|
131
|
+
def switch_to_other_window
|
132
|
+
@window_id = (@window_id - 1).abs
|
133
|
+
if @window_id != 0 and @window_id !=1
|
134
|
+
puts @window_id
|
135
|
+
raise(Exception::WindowMatchError, "You cannot use this keyword when more than 2 windows are open; you must use 'Switch To Window', 'Switch to Next Window', or 'Switch to Previous Window'")
|
136
|
+
end
|
137
|
+
|
138
|
+
@browser.windows[@window_id].use
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Switch to a specified opened window
|
143
|
+
#
|
144
|
+
# @param [String] loc attribute/value pairs that match an HTML element
|
145
|
+
#
|
146
|
+
def switch_to_window(loc)
|
147
|
+
if loc[0..3] == 'url=' or loc[0..5] == 'title='
|
148
|
+
@browser.window(parse_location(loc)).use
|
149
|
+
else
|
150
|
+
# assume loc is an integer
|
151
|
+
# since Robot Framework sends all args as text, the above check for
|
152
|
+
# "url=" and "title=" is the best we can do to ensure argument correctness
|
153
|
+
loc = loc.to_i
|
154
|
+
# the number of the window
|
155
|
+
# user-facing numbers are 1-based, internal we use 0-based because @browser.windows
|
156
|
+
# is a Ruby array, so minus 1
|
157
|
+
@window_id = loc - 1
|
158
|
+
if @window_id == -1
|
159
|
+
# either the user has been too smart for his/her own good and thinks the windows are 0-based,
|
160
|
+
# or they've entered text that doesn't match 'url=' or 'title=', in which case
|
161
|
+
# the above loc.to_i will make loc equal 0
|
162
|
+
raise(ArgumentError, "You must provide the url or title of the window in the format 'url=' or 'title=', or you must provide the number of the window, starting with 1 for the first window opened.")
|
163
|
+
end
|
164
|
+
# this will throw its own error if the index is out of range
|
165
|
+
@browser.windows[loc].use
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Switch to "next" window, the order depending on order of original opening.
|
171
|
+
#
|
172
|
+
# If the "last" window is active and you ask for the next, you will be taken
|
173
|
+
# back to the first window (i.e. it wraps around)
|
174
|
+
#
|
175
|
+
def switch_to_next_window
|
176
|
+
@window_id += 1
|
177
|
+
if @window_id >= @browser.windows.count
|
178
|
+
# wrap back to the first
|
179
|
+
@window_id = 0
|
180
|
+
end
|
181
|
+
|
182
|
+
@browser.windows[@window_id].use
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Switch to "previous" window, the order depending on order of original opening.
|
187
|
+
#
|
188
|
+
# If the "first" window is active and you ask for the previous, you will be taken
|
189
|
+
# around to the last window (i.e. it wraps around)
|
190
|
+
#
|
191
|
+
def switch_to_previous_window
|
192
|
+
@window_id -= 1
|
193
|
+
if @window_id < 0
|
194
|
+
# wrap back to the last
|
195
|
+
@window_id = @browser.windows.count - 1
|
196
|
+
end
|
197
|
+
|
198
|
+
@browser.windows[@window_id].use
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Close "other" window; assumes there are only 2 open
|
203
|
+
#
|
204
|
+
def close_other_window
|
205
|
+
@window_id = (@window_id - 1).abs
|
206
|
+
if @window_id != 0 and @window_id !=1
|
207
|
+
raise(Exception::WindowMatchError, "You cannot use this keyword when more than 2 windows are open; you must use 'Switch To Window', 'Switch to Next Window', or 'Switch to Previous Window'")
|
208
|
+
end
|
209
|
+
|
210
|
+
@browser.windows[@window_id].close
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
# Close a single browser window
|
215
|
+
#
|
216
|
+
# If you have multiple windows open and you close a non-active window, the window is simply closed.
|
217
|
+
# If you have multiple windows open and you close the active window, by default,
|
218
|
+
# the "previous" window will become the active one before closing the specified window
|
219
|
+
# (to avoid browser crashes). You can optionally specify which window should be used
|
220
|
+
# upon closing the active one.
|
221
|
+
# If you have only one window open and you issue this command, it is the same as calling
|
222
|
+
# "Close Browser", and the entire browser instance will be closed.
|
223
|
+
#
|
224
|
+
# @param [String] close_loc the identifier for the window you wish to close, either title, url, or integer
|
225
|
+
#
|
226
|
+
def close_window(close_loc = :current, active_loc = nil)
|
227
|
+
if @browser.windows.count == 1
|
228
|
+
# doesn't matter what they enter, bc closing the only window of a browser
|
229
|
+
# instance causes the browser to crash
|
230
|
+
@browser.close
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
if close_loc == :current
|
235
|
+
# if the current window is being closed, we have to move to another before closing it
|
236
|
+
if active_loc.nil?
|
237
|
+
# if new active window is unspecified, make the previous window the new active one
|
238
|
+
self.switch_to_previous_window
|
239
|
+
else
|
240
|
+
self.switch_to_window(active_loc)
|
241
|
+
end
|
242
|
+
else
|
243
|
+
# a specific window to be closed has been specified (though it may still be the active one)
|
244
|
+
if close_loc[0..3] == 'url=' or close_loc[0..5] == 'title='
|
245
|
+
if @browser.window(parse_location(close_loc)).current?
|
246
|
+
# if the current window is being closed, we have to move to another before closing it
|
247
|
+
if active_loc.nil?
|
248
|
+
# if new active window is unspecified, make the previous window the new active one
|
249
|
+
self.switch_to_previous_window
|
250
|
+
else
|
251
|
+
self.switch_to_window(active_loc)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
@browser.window(parse_location(close_loc)).close
|
255
|
+
else
|
256
|
+
# assume close_loc is an integer
|
257
|
+
# since Robot Framework sends all args as text, the above check for
|
258
|
+
# "url=" and "title=" is the best we can do to ensure argument correctness
|
259
|
+
close_loc = close_loc.to_i
|
260
|
+
# the number of the window
|
261
|
+
# user-facing numbers are 1-based, internal we use 0-based because @browser.windows
|
262
|
+
# is a Ruby array, so minus 1
|
263
|
+
window_id = close_loc - 1
|
264
|
+
if window_id == -1
|
265
|
+
# either the user has been too smart for his/her own good and thinks the windows are 0-based,
|
266
|
+
# or they've entered text that doesn't match 'url=' or 'title=', in which case
|
267
|
+
# the above loc.to_i will make loc equal 0
|
268
|
+
raise(ArgumentError, "You must provide the url or title of the window in the format 'url=' or 'title=', or you must provide the number of the window, starting with 1 for the first window opened.")
|
269
|
+
end
|
270
|
+
|
271
|
+
if @browser.windows[window_id].current?
|
272
|
+
# if the current window is being closed, we have to move to another before closing it
|
273
|
+
if active_loc.nil?
|
274
|
+
# if new active window is unspecified, make the previous window the new active one
|
275
|
+
self.switch_to_previous_window
|
276
|
+
else
|
277
|
+
self.switch_to_window(active_loc)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
# this will throw its own error if the index is out of range
|
281
|
+
@browser.windows[window_id].close
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
# Return the number of open windows
|
288
|
+
#
|
289
|
+
def get_window_count
|
290
|
+
return @browser.windows.count
|
291
|
+
end
|
292
|
+
|
293
|
+
### Conditions ###
|
294
|
+
|
295
|
+
#
|
296
|
+
# Verify that the current URL matches a given string
|
297
|
+
#
|
298
|
+
# @param [String] url the URL to match against
|
299
|
+
#
|
300
|
+
def url_should_be(url)
|
301
|
+
raise(Exception::UrlMatchError, "The URL #{@browser.url} is not correct; it should be #{url}") unless
|
302
|
+
@browser.url == url
|
303
|
+
end
|
304
|
+
|
305
|
+
#
|
306
|
+
# Verify that URL contains certain text
|
307
|
+
#
|
308
|
+
# @param [String] text the text which should appear in the URL
|
309
|
+
#
|
310
|
+
def url_should_contain(text)
|
311
|
+
raise(Exception::UrlMatchError, "The URL #{@browser.url} is not correct; it should contain #{text}") unless
|
312
|
+
@browser.url.include?(text)
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|