firewatir 1.6.2 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +32 -0
- data/lib/firewatir.rb +40 -50
- data/lib/firewatir/container.rb +491 -534
- data/lib/firewatir/document.rb +239 -0
- data/lib/firewatir/element.rb +1365 -0
- data/lib/firewatir/element_collections.rb +314 -0
- data/lib/firewatir/elements/button.rb +15 -0
- data/lib/firewatir/elements/file_field.rb +29 -0
- data/lib/firewatir/elements/form.rb +40 -0
- data/lib/firewatir/elements/frame.rb +55 -0
- data/lib/firewatir/elements/hidden.rb +56 -0
- data/lib/firewatir/elements/image.rb +139 -0
- data/lib/firewatir/elements/input_element.rb +44 -0
- data/lib/firewatir/elements/link.rb +76 -0
- data/lib/firewatir/elements/non_control_element.rb +53 -0
- data/lib/firewatir/elements/non_control_elements.rb +108 -0
- data/lib/firewatir/elements/not_used.rb +278 -0
- data/lib/firewatir/elements/option.rb +131 -0
- data/lib/firewatir/elements/radio_check_common.rb +163 -0
- data/lib/firewatir/elements/select_list.rb +188 -0
- data/lib/firewatir/elements/table.rb +218 -0
- data/lib/firewatir/elements/table_cell.rb +54 -0
- data/lib/firewatir/elements/table_row.rb +100 -0
- data/lib/firewatir/elements/text_field.rb +218 -0
- data/lib/firewatir/exceptions.rb +10 -10
- data/lib/firewatir/firefox.rb +1040 -1127
- data/lib/firewatir/jssh_socket.rb +101 -0
- data/lib/firewatir/version.rb +5 -5
- data/unittests/attach_to_new_window_test.rb +49 -42
- data/unittests/bug_fixes_test.rb +195 -198
- data/unittests/buttons_xpath_test.rb +88 -88
- data/unittests/checkbox_test.rb +158 -155
- data/unittests/checkbox_xpath_test.rb +107 -107
- data/unittests/div_test.rb +275 -276
- data/unittests/filefield_test.rb +49 -45
- data/unittests/filefield_xpath_test.rb +35 -35
- data/unittests/form_test.rb +296 -308
- data/unittests/frame_test.rb +159 -152
- data/unittests/hidden_test.rb +85 -85
- data/unittests/hidden_xpath_test.rb +72 -72
- data/unittests/html/blankpage.html +11 -11
- data/unittests/html/buttons1.html +61 -61
- data/unittests/html/cssTest.html +42 -42
- data/unittests/html/div.html +72 -72
- data/unittests/html/fileupload.html +45 -45
- data/unittests/html/formTest1.html +38 -38
- data/unittests/html/forms2.html +45 -45
- data/unittests/html/frame_buttons.html +3 -3
- data/unittests/html/iframeTest.html +14 -14
- data/unittests/html/iframeTest1.html +13 -13
- data/unittests/html/iframeTest2.html +5 -5
- data/unittests/html/links1.html +42 -42
- data/unittests/html/nestedFrames.html +6 -6
- data/unittests/html/new_browser.html +1 -0
- data/unittests/html/new_browser_popup.html +8 -0
- data/unittests/html/pass.html +9 -9
- data/unittests/html/pre.html +27 -27
- data/unittests/html/redirect.html +10 -10
- data/unittests/html/redirect1.html +8 -8
- data/unittests/html/redirect2.html +8 -8
- data/unittests/html/redirect3.html +8 -8
- data/unittests/html/simple_table_columns.html +74 -74
- data/unittests/html/table1.html +165 -165
- data/unittests/html/textfields1.html +62 -62
- data/unittests/images_test.rb +198 -205
- data/unittests/images_xpath_test.rb +118 -119
- data/unittests/javascript_test.rb +75 -75
- data/unittests/links_test.rb +231 -232
- data/unittests/links_xpath_test.rb +79 -79
- data/unittests/mozilla_all_tests.rb +7 -7
- data/unittests/pre_test.rb +75 -76
- data/unittests/radios_xpath_test.rb +101 -101
- data/unittests/redirect_test.rb +41 -41
- data/unittests/selectbox_test.rb +142 -142
- data/unittests/selectbox_xpath_test.rb +129 -129
- data/unittests/setup.rb +29 -30
- data/unittests/table_test.rb +385 -373
- data/unittests/table_xpath_test.rb +185 -185
- data/unittests/textfields_test.rb +234 -233
- data/unittests/textfields_xpath_test.rb +113 -113
- metadata +33 -11
- data/lib/firewatir/MozillaBaseElement.rb +0 -1863
- data/lib/firewatir/htmlelements.rb +0 -1911
- data/unittests/iostring.rb +0 -30
- data/unittests/iostring_test.rb +0 -48
@@ -0,0 +1,100 @@
|
|
1
|
+
module FireWatir
|
2
|
+
#
|
3
|
+
# Description:
|
4
|
+
# Class for Table row element.
|
5
|
+
#
|
6
|
+
class TableRow < Element
|
7
|
+
attr_accessor :element_name
|
8
|
+
|
9
|
+
#
|
10
|
+
# Description:
|
11
|
+
# Locate the table row element on the page.
|
12
|
+
#
|
13
|
+
def locate
|
14
|
+
@o = nil
|
15
|
+
case @how
|
16
|
+
when :jssh_name
|
17
|
+
@element_name = @what
|
18
|
+
when :xpath
|
19
|
+
@element_name = element_by_xpath(@container, @what)
|
20
|
+
else
|
21
|
+
@element_name = locate_tagged_element("TR", @how, @what)
|
22
|
+
end
|
23
|
+
@o = self
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Description:
|
28
|
+
# Initializes the instance of table row object.
|
29
|
+
#
|
30
|
+
# Input:
|
31
|
+
# - how - Attribute to identify the table row element.
|
32
|
+
# - what - Value of that attribute.
|
33
|
+
#
|
34
|
+
def initialize(container, how, what)
|
35
|
+
@how = how
|
36
|
+
@what = what
|
37
|
+
@container = container
|
38
|
+
#super nil
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Description:
|
43
|
+
# Gets the length of columns in table row.
|
44
|
+
#
|
45
|
+
# Output:
|
46
|
+
# Length of columns in table row.
|
47
|
+
#
|
48
|
+
def column_count
|
49
|
+
assert_exists
|
50
|
+
arr_cells = cells
|
51
|
+
return arr_cells.length
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Description:
|
56
|
+
# Get cell at specified index in a row.
|
57
|
+
#
|
58
|
+
# Input:
|
59
|
+
# key - column index.
|
60
|
+
#
|
61
|
+
# Output:
|
62
|
+
# Table cell element at specified index.
|
63
|
+
#
|
64
|
+
def [] (key)
|
65
|
+
assert_exists
|
66
|
+
arr_cells = cells
|
67
|
+
return arr_cells[key - 1]
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Description:
|
72
|
+
# Iterate over each cell in a row.
|
73
|
+
#
|
74
|
+
def each
|
75
|
+
assert_exists
|
76
|
+
arr_cells = cells
|
77
|
+
for i in 0..arr_cells.length - 1 do
|
78
|
+
yield arr_cells[i]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Description:
|
84
|
+
# Get array of all cells in Table Row
|
85
|
+
#
|
86
|
+
# Output:
|
87
|
+
# Array containing Table Cell elements.
|
88
|
+
#
|
89
|
+
def cells
|
90
|
+
assert_exists
|
91
|
+
arr_cells = get_cells
|
92
|
+
row_cells = Array.new(arr_cells.length)
|
93
|
+
for i in 0..arr_cells.length - 1 do
|
94
|
+
row_cells[i] = TableCell.new(@container, :jssh_name, arr_cells[i])
|
95
|
+
end
|
96
|
+
return row_cells
|
97
|
+
end
|
98
|
+
|
99
|
+
end # TableRow
|
100
|
+
end # FireWatir
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module FireWatir
|
2
|
+
#
|
3
|
+
# Description:
|
4
|
+
# Class for Text Field element.
|
5
|
+
#
|
6
|
+
class TextField < InputElement
|
7
|
+
INPUT_TYPES = ["text", "password", "textarea"]
|
8
|
+
|
9
|
+
# Gets the size of the text field element.
|
10
|
+
def_wrap :size
|
11
|
+
# Gets max length of the text field element.
|
12
|
+
def_wrap :maxlength_string, :maxlength
|
13
|
+
def maxlength
|
14
|
+
maxlength_string.to_i
|
15
|
+
end
|
16
|
+
# Returns true if the text field is read only, false otherwise.
|
17
|
+
def_wrap :readonly?, :readOnly
|
18
|
+
|
19
|
+
#
|
20
|
+
# Description:
|
21
|
+
# Used to populate the properties in to_s method
|
22
|
+
#
|
23
|
+
#def text_string_creator
|
24
|
+
# n = []
|
25
|
+
# n << "length:".ljust(TO_S_SIZE) + self.size.to_s
|
26
|
+
# n << "max length:".ljust(TO_S_SIZE) + self.maxlength.to_s
|
27
|
+
# n << "read only:".ljust(TO_S_SIZE) + self.readonly?.to_s
|
28
|
+
#
|
29
|
+
# return n
|
30
|
+
#end
|
31
|
+
#private :text_string_creator
|
32
|
+
|
33
|
+
# TODO: Impelement the to_s method.
|
34
|
+
def to_s
|
35
|
+
assert_exists
|
36
|
+
super({"length" => "size","max length" => "maxlength","read only" => "readOnly" })
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Description:
|
41
|
+
# Checks if object is read-only or not.
|
42
|
+
#
|
43
|
+
def assert_not_readonly
|
44
|
+
raise ObjectReadOnlyException, "Textfield #{@how} and #{@what} is read only." if self.readonly?
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Description:
|
49
|
+
# Checks if the provided text matches with the contents of text field. Text can be a string or regular expression.
|
50
|
+
#
|
51
|
+
# Input:
|
52
|
+
# - containsThis - Text to verify.
|
53
|
+
#
|
54
|
+
# Output:
|
55
|
+
# True if provided text matches with the contents of text field, false otherwise.
|
56
|
+
#
|
57
|
+
def verify_contains( containsThis )
|
58
|
+
assert_exists
|
59
|
+
if containsThis.kind_of? String
|
60
|
+
return true if self.value == containsThis
|
61
|
+
elsif containsThis.kind_of? Regexp
|
62
|
+
return true if self.value.match(containsThis) != nil
|
63
|
+
end
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
# this method is used to drag the entire contents of the text field to another text field
|
68
|
+
# 19 Jan 2005 - It is added as prototype functionality, and may change
|
69
|
+
# * destination_how - symbol, :id, :name how we identify the drop target
|
70
|
+
# * destination_what - string or regular expression, the name, id, etc of the text field that will be the drop target
|
71
|
+
# TODO: Can we have support for this in Firefox.
|
72
|
+
#def drag_contents_to( destination_how , destination_what)
|
73
|
+
# assert_exists
|
74
|
+
# destination = element.text_field(destination_how, destination_what)
|
75
|
+
# raise UnknownObjectException , "Unable to locate destination using #{destination_how } and #{destination_what } " if destination.exists? == false
|
76
|
+
|
77
|
+
# @o.focus
|
78
|
+
# @o.select()
|
79
|
+
# value = self.value
|
80
|
+
|
81
|
+
# @o.fireEvent("onSelect")
|
82
|
+
# @o.fireEvent("ondragstart")
|
83
|
+
# @o.fireEvent("ondrag")
|
84
|
+
# destination.fireEvent("onDragEnter")
|
85
|
+
# destination.fireEvent("onDragOver")
|
86
|
+
# destination.fireEvent("ondrop")
|
87
|
+
|
88
|
+
# @o.fireEvent("ondragend")
|
89
|
+
# destination.value= ( destination.value + value.to_s )
|
90
|
+
# self.value = ""
|
91
|
+
#end
|
92
|
+
# alias dragContentsTo drag_contents_to
|
93
|
+
|
94
|
+
#
|
95
|
+
# Description:
|
96
|
+
# Clears the contents of the text field.
|
97
|
+
# Raises ObjectDisabledException if text field is disabled.
|
98
|
+
# Raises ObjectReadOnlyException if text field is read only.
|
99
|
+
#
|
100
|
+
def clear
|
101
|
+
assert_exists
|
102
|
+
assert_enabled
|
103
|
+
assert_not_readonly
|
104
|
+
|
105
|
+
highlight(:set)
|
106
|
+
|
107
|
+
@o.scrollIntoView
|
108
|
+
@o.focus
|
109
|
+
@o.select()
|
110
|
+
@o.fireEvent("onSelect")
|
111
|
+
@o.value = ""
|
112
|
+
@o.fireEvent("onKeyPress")
|
113
|
+
@o.fireEvent("onChange")
|
114
|
+
@container.wait()
|
115
|
+
highlight(:clear)
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Description:
|
120
|
+
# Append the provided text to the contents of the text field.
|
121
|
+
# Raises ObjectDisabledException if text field is disabled.
|
122
|
+
# Raises ObjectReadOnlyException if text field is read only.
|
123
|
+
#
|
124
|
+
# Input:
|
125
|
+
# - setThis - Text to be appended.
|
126
|
+
#
|
127
|
+
def append( setThis)
|
128
|
+
assert_exists
|
129
|
+
assert_enabled
|
130
|
+
assert_not_readonly
|
131
|
+
|
132
|
+
highlight(:set)
|
133
|
+
@o.scrollIntoView
|
134
|
+
@o.focus
|
135
|
+
doKeyPress( setThis )
|
136
|
+
highlight(:clear)
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Description:
|
141
|
+
# Sets the contents of the text field to the provided text. Overwrite the existing contents.
|
142
|
+
# Raises ObjectDisabledException if text field is disabled.
|
143
|
+
# Raises ObjectReadOnlyException if text field is read only.
|
144
|
+
#
|
145
|
+
# Input:
|
146
|
+
# - setThis - Text to be set.
|
147
|
+
#
|
148
|
+
def set( setThis )
|
149
|
+
assert_exists
|
150
|
+
assert_enabled
|
151
|
+
assert_not_readonly
|
152
|
+
|
153
|
+
highlight(:set)
|
154
|
+
@o.scrollIntoView
|
155
|
+
@o.focus
|
156
|
+
@o.select()
|
157
|
+
@o.fireEvent("onSelect")
|
158
|
+
@o.value = ""
|
159
|
+
@o.fireEvent("onKeyPress")
|
160
|
+
doKeyPress( setThis )
|
161
|
+
highlight(:clear)
|
162
|
+
@o.fireEvent("onChange")
|
163
|
+
@o.fireEvent("onBlur")
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# Description:
|
168
|
+
# Sets the text of the text field withoud firing the events like onKeyPress, onKeyDown etc. This should not be used generally, but it
|
169
|
+
# is useful in situations where you need to set large text to the text field and you know that you don't have any event to be
|
170
|
+
# fired.
|
171
|
+
#
|
172
|
+
# Input:
|
173
|
+
# - v - Text to be set.
|
174
|
+
#
|
175
|
+
#def value=(v)
|
176
|
+
# assert_exists
|
177
|
+
# @o.value = v.to_s
|
178
|
+
#end
|
179
|
+
|
180
|
+
#
|
181
|
+
# Description:
|
182
|
+
# Used to set the value of text box and fires the event onKeyPress, onKeyDown, onKeyUp after each character.
|
183
|
+
# Shouldnot be used externally. Used internally by set and append methods.
|
184
|
+
#
|
185
|
+
# Input:
|
186
|
+
# - value - The string to enter into the text field
|
187
|
+
#
|
188
|
+
def doKeyPress( value )
|
189
|
+
begin
|
190
|
+
max = maxlength
|
191
|
+
if (max > 0 && value.length > max)
|
192
|
+
original_value = value
|
193
|
+
value = original_value[0...max]
|
194
|
+
element.log " Supplied string is #{suppliedValue.length} chars, which exceeds the max length (#{max}) of the field. Using value: #{value}"
|
195
|
+
end
|
196
|
+
rescue
|
197
|
+
# probably a text area - so it doesnt have a max Length
|
198
|
+
end
|
199
|
+
for i in 0..value.length-1
|
200
|
+
#sleep element.typingspeed # typing speed
|
201
|
+
c = value[i,1]
|
202
|
+
#element.log " adding c.chr " + c #.chr.to_s
|
203
|
+
@o.value = "#{(@o.value.to_s + c)}" #c.chr
|
204
|
+
@o.fireEvent("onKeyDown")
|
205
|
+
@o.fireEvent("onKeyPress")
|
206
|
+
@o.fireEvent("onKeyUp")
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
private :doKeyPress
|
211
|
+
|
212
|
+
alias readOnly? :readonly?
|
213
|
+
alias getContents value
|
214
|
+
alias maxLength maxlength
|
215
|
+
|
216
|
+
end # TextField
|
217
|
+
end # FireWatir
|
218
|
+
|
data/lib/firewatir/exceptions.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'watir/exceptions' # from
|
2
|
-
|
3
|
-
module Watir
|
4
|
-
module Exception
|
5
|
-
# This exception is thrown if we are unable to connect to JSSh.
|
6
|
-
class UnableToStartJSShException < WatirException; end
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
|
1
|
+
require 'watir/exceptions' # from commonwatir
|
2
|
+
|
3
|
+
module Watir
|
4
|
+
module Exception
|
5
|
+
# This exception is thrown if we are unable to connect to JSSh.
|
6
|
+
class UnableToStartJSShException < WatirException; end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
data/lib/firewatir/firefox.rb
CHANGED
@@ -1,1127 +1,1040 @@
|
|
1
|
-
=begin rdoc
|
2
|
-
This is FireWatir, Web Application Testing In Ruby using Firefox browser
|
3
|
-
|
4
|
-
Typical usage:
|
5
|
-
# include the controller
|
6
|
-
require "firewatir"
|
7
|
-
|
8
|
-
# go to the page you want to test
|
9
|
-
ff = FireWatir::Firefox.start("http://myserver/mypage")
|
10
|
-
|
11
|
-
# enter "Angrez" into an input field named "username"
|
12
|
-
ff.text_field(:name, "username").set("Angrez")
|
13
|
-
|
14
|
-
# enter "Ruby Co" into input field with id "company_ID"
|
15
|
-
ff.text_field(:id, "company_ID").set("Ruby Co")
|
16
|
-
|
17
|
-
# click on a link that has "green" somewhere in the text that is displayed
|
18
|
-
# to the user, using a regular expression
|
19
|
-
ff.link(:text, /green/)
|
20
|
-
|
21
|
-
# click button that has a caption of "Cancel"
|
22
|
-
ff.button(:value, "Cancel").click
|
23
|
-
|
24
|
-
FireWatir allows your script to read and interact with HTML objects--HTML tags
|
25
|
-
and their attributes and contents. Types of objects that FireWatir can identify
|
26
|
-
include:
|
27
|
-
|
28
|
-
Type Description
|
29
|
-
=========== ===============================================================
|
30
|
-
button <input> tags, with the type="button" attribute
|
31
|
-
check_box <input> tags, with the type="checkbox" attribute
|
32
|
-
div <div> tags
|
33
|
-
form
|
34
|
-
frame
|
35
|
-
hidden hidden <input> tags
|
36
|
-
image <img> tags
|
37
|
-
label
|
38
|
-
link <a> (anchor) tags
|
39
|
-
p <p> (paragraph) tags
|
40
|
-
radio radio buttons; <input> tags, with the type="radio" attribute
|
41
|
-
select_list <select> tags, known informally as drop-down boxes
|
42
|
-
span <span> tags
|
43
|
-
table <table> tags
|
44
|
-
text_field <input> tags with the type="text" attribute (a single-line
|
45
|
-
text field), the type="text_area" attribute (a multi-line
|
46
|
-
text field), and the type="password" attribute (a
|
47
|
-
single-line field in which the input is replaced with asterisks)
|
48
|
-
|
49
|
-
In general, there are several ways to identify a specific object. FireWatir's
|
50
|
-
syntax is in the form (how, what), where "how" is a means of identifying
|
51
|
-
the object, and "what" is the specific string or regular expression
|
52
|
-
that FireWatir will seek, as shown in the examples above. Available "how"
|
53
|
-
options depend upon the type of object, but here are a few examples:
|
54
|
-
|
55
|
-
How Description
|
56
|
-
============ ===============================================================
|
57
|
-
:id Used to find an object that has an "id=" attribute. Since each
|
58
|
-
id should be unique, according to the XHTML specification,
|
59
|
-
this is recommended as the most reliable method to find an
|
60
|
-
object.
|
61
|
-
:name Used to find an object that has a "name=" attribute. This is
|
62
|
-
useful for older versions of HTML, but "name" is deprecated
|
63
|
-
in XHTML.
|
64
|
-
:value Used to find a text field with a given default value, or a
|
65
|
-
button with a given caption
|
66
|
-
:index Used to find the nth object of the specified type on a page.
|
67
|
-
For example, button(:index, 2) finds the second button.
|
68
|
-
Current versions of FireWatir use 1-based indexing, but future
|
69
|
-
versions will use 0-based indexing.
|
70
|
-
:xpath The xpath expression for identifying the element.
|
71
|
-
|
72
|
-
Note that the XHTML specification requires that tags and their attributes be
|
73
|
-
in lower case. FireWatir doesn't enforce this; FireWatir will find tags and
|
74
|
-
attributes whether they're in upper, lower, or mixed case. This is either
|
75
|
-
a bug or a feature.
|
76
|
-
|
77
|
-
FireWatir uses JSSh for interacting with the browser. For further information on
|
78
|
-
Firefox and DOM go to the following Web page:
|
79
|
-
|
80
|
-
http://www.xulplanet.com/references/objref/
|
81
|
-
|
82
|
-
=end
|
83
|
-
|
84
|
-
module FireWatir
|
85
|
-
include Watir::Exception
|
86
|
-
|
87
|
-
class Firefox
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
if
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
#
|
227
|
-
#
|
228
|
-
def
|
229
|
-
#
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
@
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
#
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
set_browser_document()
|
338
|
-
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
#
|
343
|
-
#
|
344
|
-
#
|
345
|
-
#
|
346
|
-
#
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
#
|
455
|
-
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
#
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
#
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
#
|
607
|
-
#
|
608
|
-
#
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
#
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
}
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
#
|
850
|
-
#
|
851
|
-
#
|
852
|
-
#
|
853
|
-
#
|
854
|
-
#
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
#
|
870
|
-
#
|
871
|
-
#
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
#
|
891
|
-
#
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
#
|
911
|
-
#
|
912
|
-
#
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
#
|
932
|
-
#
|
933
|
-
#
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
#
|
953
|
-
#
|
954
|
-
#
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
puts "
|
983
|
-
puts " index: #{
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
for(var i = 0; i < frameset.length; i++)
|
1042
|
-
{
|
1043
|
-
var frames = frameset[i].frames;
|
1044
|
-
for(var j = 0; j < frames.length; j++)
|
1045
|
-
{
|
1046
|
-
elements_frames.push(frames[j].frameElement);
|
1047
|
-
}
|
1048
|
-
}
|
1049
|
-
elements_frames.length;"
|
1050
|
-
|
1051
|
-
jssh_command.gsub!("\n", "")
|
1052
|
-
$jssh_socket.send("#{jssh_command};\n", 0)
|
1053
|
-
length = read_socket().to_i
|
1054
|
-
|
1055
|
-
puts "There are #{length} frames"
|
1056
|
-
|
1057
|
-
frames = Array.new(length)
|
1058
|
-
for i in 0..length - 1 do
|
1059
|
-
frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]")
|
1060
|
-
end
|
1061
|
-
|
1062
|
-
for i in 0..length - 1 do
|
1063
|
-
puts "frame: name: #{frames[i].name}"
|
1064
|
-
puts " index: #{i+1}"
|
1065
|
-
end
|
1066
|
-
end
|
1067
|
-
alias showFrames show_frames
|
1068
|
-
|
1069
|
-
# 5/16/08 Derek Berner
|
1070
|
-
# Wrapper method to send JS commands concisely,
|
1071
|
-
# and propagate errors
|
1072
|
-
def js_eval(str)
|
1073
|
-
#puts "JS Eval: #{str}"
|
1074
|
-
$jssh_socket.send("#{str};\n",0)
|
1075
|
-
value = read_socket()
|
1076
|
-
if md=/^(\w+)Error:(.*)$/.match(value)
|
1077
|
-
eval "class JS#{md[1]}Error\nend"
|
1078
|
-
raise (eval "JS#{md[1]}Error"), md[2]
|
1079
|
-
end
|
1080
|
-
#puts "Value: #{value}"
|
1081
|
-
value
|
1082
|
-
end
|
1083
|
-
|
1084
|
-
end # Class Firefox
|
1085
|
-
|
1086
|
-
#
|
1087
|
-
# Module for handling the Javascript pop-ups. Not in use currently, will be available in future.
|
1088
|
-
# Use ff.startClicker() method for clicking javascript pop ups. Refer to unit tests on how to handle
|
1089
|
-
# javascript pop up (unittests/javascript_test.rb)
|
1090
|
-
#module Dialog
|
1091
|
-
# # Class for handling javascript popup. Not in use currently, will be available in future. See unit tests on how to handle
|
1092
|
-
# # javascript pop up (unittests/javascript_test.rb).
|
1093
|
-
# class JSPopUp
|
1094
|
-
# include Container
|
1095
|
-
#
|
1096
|
-
# def has_appeared(text)
|
1097
|
-
# require 'socket'
|
1098
|
-
# sleep 4
|
1099
|
-
# shell = TCPSocket.new("localhost", 9997)
|
1100
|
-
# read_socket(shell)
|
1101
|
-
# #jssh_command = "var url = #{DOCUMENT_VAR}.URL;"
|
1102
|
-
# jssh_command = "var length = getWindows().length; var win;length;\n"
|
1103
|
-
# #jssh_command += "for(var i = 0; i < length; i++)"
|
1104
|
-
# #jssh_command += "{"
|
1105
|
-
# #jssh_command += " win = getWindows()[i];"
|
1106
|
-
# #jssh_command += " if(win.opener != null && "
|
1107
|
-
# #jssh_command += " win.title == \"[JavaScript Application]\" &&"
|
1108
|
-
# #jssh_command += " win.opener.document.URL == url)"
|
1109
|
-
# #jssh_command += " {"
|
1110
|
-
# #jssh_command += " break;"
|
1111
|
-
# #jssh_command += " }"
|
1112
|
-
# #jssh_command += "}"
|
1113
|
-
#
|
1114
|
-
# #jssh_command += " win.title;\n";
|
1115
|
-
# #jssh_command += "var dialog = win.document.childNodes[0];"
|
1116
|
-
# #jssh_command += "vbox = dialog.childNodes[1].childNodes[1];"
|
1117
|
-
# #jssh_command += "vbox.childNodes[1].childNodes[0].childNodes[0].textContent;\n"
|
1118
|
-
# puts jssh_command
|
1119
|
-
# shell.send("#{jssh_command}", 0)
|
1120
|
-
# jstext = read_socket(shell)
|
1121
|
-
# puts jstext
|
1122
|
-
# return jstext == text
|
1123
|
-
# end
|
1124
|
-
# end
|
1125
|
-
#end
|
1126
|
-
|
1127
|
-
end
|
1
|
+
=begin rdoc
|
2
|
+
This is FireWatir, Web Application Testing In Ruby using Firefox browser
|
3
|
+
|
4
|
+
Typical usage:
|
5
|
+
# include the controller
|
6
|
+
require "firewatir"
|
7
|
+
|
8
|
+
# go to the page you want to test
|
9
|
+
ff = FireWatir::Firefox.start("http://myserver/mypage")
|
10
|
+
|
11
|
+
# enter "Angrez" into an input field named "username"
|
12
|
+
ff.text_field(:name, "username").set("Angrez")
|
13
|
+
|
14
|
+
# enter "Ruby Co" into input field with id "company_ID"
|
15
|
+
ff.text_field(:id, "company_ID").set("Ruby Co")
|
16
|
+
|
17
|
+
# click on a link that has "green" somewhere in the text that is displayed
|
18
|
+
# to the user, using a regular expression
|
19
|
+
ff.link(:text, /green/)
|
20
|
+
|
21
|
+
# click button that has a caption of "Cancel"
|
22
|
+
ff.button(:value, "Cancel").click
|
23
|
+
|
24
|
+
FireWatir allows your script to read and interact with HTML objects--HTML tags
|
25
|
+
and their attributes and contents. Types of objects that FireWatir can identify
|
26
|
+
include:
|
27
|
+
|
28
|
+
Type Description
|
29
|
+
=========== ===============================================================
|
30
|
+
button <input> tags, with the type="button" attribute
|
31
|
+
check_box <input> tags, with the type="checkbox" attribute
|
32
|
+
div <div> tags
|
33
|
+
form
|
34
|
+
frame
|
35
|
+
hidden hidden <input> tags
|
36
|
+
image <img> tags
|
37
|
+
label
|
38
|
+
link <a> (anchor) tags
|
39
|
+
p <p> (paragraph) tags
|
40
|
+
radio radio buttons; <input> tags, with the type="radio" attribute
|
41
|
+
select_list <select> tags, known informally as drop-down boxes
|
42
|
+
span <span> tags
|
43
|
+
table <table> tags
|
44
|
+
text_field <input> tags with the type="text" attribute (a single-line
|
45
|
+
text field), the type="text_area" attribute (a multi-line
|
46
|
+
text field), and the type="password" attribute (a
|
47
|
+
single-line field in which the input is replaced with asterisks)
|
48
|
+
|
49
|
+
In general, there are several ways to identify a specific object. FireWatir's
|
50
|
+
syntax is in the form (how, what), where "how" is a means of identifying
|
51
|
+
the object, and "what" is the specific string or regular expression
|
52
|
+
that FireWatir will seek, as shown in the examples above. Available "how"
|
53
|
+
options depend upon the type of object, but here are a few examples:
|
54
|
+
|
55
|
+
How Description
|
56
|
+
============ ===============================================================
|
57
|
+
:id Used to find an object that has an "id=" attribute. Since each
|
58
|
+
id should be unique, according to the XHTML specification,
|
59
|
+
this is recommended as the most reliable method to find an
|
60
|
+
object.
|
61
|
+
:name Used to find an object that has a "name=" attribute. This is
|
62
|
+
useful for older versions of HTML, but "name" is deprecated
|
63
|
+
in XHTML.
|
64
|
+
:value Used to find a text field with a given default value, or a
|
65
|
+
button with a given caption
|
66
|
+
:index Used to find the nth object of the specified type on a page.
|
67
|
+
For example, button(:index, 2) finds the second button.
|
68
|
+
Current versions of FireWatir use 1-based indexing, but future
|
69
|
+
versions will use 0-based indexing.
|
70
|
+
:xpath The xpath expression for identifying the element.
|
71
|
+
|
72
|
+
Note that the XHTML specification requires that tags and their attributes be
|
73
|
+
in lower case. FireWatir doesn't enforce this; FireWatir will find tags and
|
74
|
+
attributes whether they're in upper, lower, or mixed case. This is either
|
75
|
+
a bug or a feature.
|
76
|
+
|
77
|
+
FireWatir uses JSSh for interacting with the browser. For further information on
|
78
|
+
Firefox and DOM go to the following Web page:
|
79
|
+
|
80
|
+
http://www.xulplanet.com/references/objref/
|
81
|
+
|
82
|
+
=end
|
83
|
+
|
84
|
+
module FireWatir
|
85
|
+
include Watir::Exception
|
86
|
+
|
87
|
+
class Firefox
|
88
|
+
include FireWatir::Container
|
89
|
+
|
90
|
+
# XPath Result type. Return only first node that matches the xpath expression.
|
91
|
+
# More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate"
|
92
|
+
FIRST_ORDERED_NODE_TYPE = 9
|
93
|
+
|
94
|
+
# Description:
|
95
|
+
# Starts the firefox browser.
|
96
|
+
# On windows this starts the first version listed in the registry.
|
97
|
+
#
|
98
|
+
# Input:
|
99
|
+
# options - Hash of any of the following options:
|
100
|
+
# :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds.
|
101
|
+
# This is done because if Firefox is not started and we try to connect
|
102
|
+
# to jssh on port 9997 an exception is thrown.
|
103
|
+
# :profile - The Firefox profile to use. If none is specified, Firefox will use
|
104
|
+
# the last used profile.
|
105
|
+
# :suppress_launch_process - do not create a new firefox process. Connect to an existing one.
|
106
|
+
|
107
|
+
# TODO: Start the firefox version given by user.
|
108
|
+
|
109
|
+
def initialize(options = {})
|
110
|
+
if(options.kind_of?(Integer))
|
111
|
+
options = {:waitTime => options}
|
112
|
+
end
|
113
|
+
|
114
|
+
# check for jssh not running, firefox may be open but not with -jssh
|
115
|
+
# if its not open at all, regardless of the :suppress_launch_process option start it
|
116
|
+
# error if running without jssh, we don't want to kill their current window (mac only)
|
117
|
+
jssh_down = false
|
118
|
+
begin
|
119
|
+
set_defaults()
|
120
|
+
rescue Watir::Exception::UnableToStartJSShException
|
121
|
+
jssh_down = true
|
122
|
+
end
|
123
|
+
|
124
|
+
if current_os == :macosx && !%x{ps x | grep firefox-bin | grep -v grep}.empty?
|
125
|
+
raise "Firefox is running without -jssh" if jssh_down
|
126
|
+
open_window unless options[:suppress_launch_process]
|
127
|
+
elsif not options[:suppress_launch_process]
|
128
|
+
launch_browser(options)
|
129
|
+
end
|
130
|
+
|
131
|
+
set_defaults()
|
132
|
+
get_window_number()
|
133
|
+
set_browser_document()
|
134
|
+
end
|
135
|
+
|
136
|
+
def inspect
|
137
|
+
'#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Launches firebox browser
|
142
|
+
# options as .new
|
143
|
+
|
144
|
+
def launch_browser(options = {})
|
145
|
+
|
146
|
+
if(options[:profile])
|
147
|
+
profile_opt = "-no-remote -P #{options[:profile]}"
|
148
|
+
else
|
149
|
+
profile_opt = ""
|
150
|
+
end
|
151
|
+
|
152
|
+
bin = path_to_bin()
|
153
|
+
@t = Thread.new { system("#{bin} -jssh #{profile_opt}") }
|
154
|
+
sleep options[:waitTime] || 2
|
155
|
+
|
156
|
+
end
|
157
|
+
private :launch_browser
|
158
|
+
|
159
|
+
# Creates a new instance of Firefox. Loads the URL and return the instance.
|
160
|
+
# Input:
|
161
|
+
# url - url of the page to be loaded.
|
162
|
+
def self.start(url)
|
163
|
+
ff = Firefox.new
|
164
|
+
ff.goto(url)
|
165
|
+
return ff
|
166
|
+
end
|
167
|
+
|
168
|
+
# Gets the window number opened.
|
169
|
+
# Currently, this returns the most recently opened window, which may or may
|
170
|
+
# not be the current window.
|
171
|
+
def get_window_number()
|
172
|
+
# If at any time a non-browser window like the "Downloads" window
|
173
|
+
# pops up, it will become the topmost window, so make sure we
|
174
|
+
# ignore it.
|
175
|
+
window_count = js_eval("getWindows().length").to_i - 1
|
176
|
+
while js_eval("getWindows()[#{window_count}].getBrowser") == ''
|
177
|
+
window_count -= 1;
|
178
|
+
end
|
179
|
+
|
180
|
+
# now correctly handles instances where only browserless windows are open
|
181
|
+
# opens one we can use if count is 0
|
182
|
+
|
183
|
+
if window_count < 0
|
184
|
+
open_window
|
185
|
+
window_count = 1
|
186
|
+
end
|
187
|
+
@window_index = window_count
|
188
|
+
end
|
189
|
+
private :get_window_number
|
190
|
+
|
191
|
+
# Loads the given url in the browser. Waits for the page to get loaded.
|
192
|
+
def goto(url)
|
193
|
+
get_window_number()
|
194
|
+
set_browser_document()
|
195
|
+
js_eval "#{browser_var}.loadURI(\"#{url}\")"
|
196
|
+
wait()
|
197
|
+
end
|
198
|
+
|
199
|
+
# Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.
|
200
|
+
def back()
|
201
|
+
js_eval "if(#{browser_var}.canGoBack) #{browser_var}.goBack()"
|
202
|
+
wait()
|
203
|
+
end
|
204
|
+
|
205
|
+
# Loads the next page (if there is any) in the browser. Waits for the page to get loaded.
|
206
|
+
def forward()
|
207
|
+
js_eval "if(#{browser_var}.canGoForward) #{browser_var}.goForward()"
|
208
|
+
wait()
|
209
|
+
end
|
210
|
+
|
211
|
+
# Reloads the current page in the browser. Waits for the page to get loaded.
|
212
|
+
def refresh()
|
213
|
+
js_eval "#{browser_var}.reload()"
|
214
|
+
wait()
|
215
|
+
end
|
216
|
+
|
217
|
+
# Executes the given JavaScript string
|
218
|
+
def execute_script(source)
|
219
|
+
result = js_eval source.to_s
|
220
|
+
wait()
|
221
|
+
|
222
|
+
result
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
# This function creates a new socket at port 9997 and sets the default values for instance and class variables.
|
227
|
+
# Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries.
|
228
|
+
def set_defaults(no_of_tries = 0)
|
229
|
+
# JSSH listens on port 9997. Create a new socket to connect to port 9997.
|
230
|
+
begin
|
231
|
+
$jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
|
232
|
+
$jssh_socket.sync = true
|
233
|
+
read_socket()
|
234
|
+
rescue
|
235
|
+
no_of_tries += 1
|
236
|
+
retry if no_of_tries < 3
|
237
|
+
raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
|
238
|
+
end
|
239
|
+
@error_checkers = []
|
240
|
+
end
|
241
|
+
|
242
|
+
# Sets the document, window and browser variables to point to correct object in JSSh.
|
243
|
+
def set_browser_document
|
244
|
+
# Add eventlistener for browser window so that we can reset the document back whenever there is redirect
|
245
|
+
# or browser loads on its own after some time. Useful when you are searching for flight results etc and
|
246
|
+
# page goes to search page after that it goes automatically to results page.
|
247
|
+
# Details : http://zenit.senecac.on.ca/wiki/index.php/Mozilla.dev.tech.xul#What_is_an_example_of_addProgressListener.3F
|
248
|
+
jssh_command = "var listObj = new Object();"; # create new object
|
249
|
+
jssh_command << "listObj.wpl = Components.interfaces.nsIWebProgressListener;"; # set the web progress listener.
|
250
|
+
jssh_command << "listObj.QueryInterface = function(aIID) {
|
251
|
+
if (aIID.equals(listObj.wpl) ||
|
252
|
+
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
|
253
|
+
aIID.equals(Components.interfaces.nsISupports))
|
254
|
+
return this;
|
255
|
+
throw Components.results.NS_NOINTERFACE;
|
256
|
+
};" # set function to locate the object via QueryInterface
|
257
|
+
jssh_command << "listObj.onStateChange = function(aProgress, aRequest, aFlag, aStatus) {
|
258
|
+
if (aFlag & listObj.wpl.STATE_STOP) {
|
259
|
+
if ( aFlag & listObj.wpl.STATE_IS_NETWORK ) {
|
260
|
+
#{document_var} = #{browser_var}.contentDocument;
|
261
|
+
#{body_var} = #{document_var}.body;
|
262
|
+
}
|
263
|
+
}
|
264
|
+
};" # add function to be called when window state is change. When state is STATE_STOP &
|
265
|
+
# STATE_IS_NETWORK then only everything is loaded. Now we can reset our variables.
|
266
|
+
jssh_command.gsub!(/\n/, "")
|
267
|
+
js_eval jssh_command
|
268
|
+
|
269
|
+
jssh_command = "var #{window_var} = getWindows()[#{@window_index}];"
|
270
|
+
jssh_command << "var #{browser_var} = #{window_var}.getBrowser();"
|
271
|
+
# Add listener create above to browser object
|
272
|
+
jssh_command << "#{browser_var}.addProgressListener( listObj,Components.interfaces.nsIWebProgress.NOTIFY_STATE_WINDOW );"
|
273
|
+
jssh_command << "var #{document_var} = #{browser_var}.contentDocument;"
|
274
|
+
jssh_command << "var #{body_var} = #{document_var}.body;"
|
275
|
+
js_eval jssh_command
|
276
|
+
|
277
|
+
@window_title = js_eval "#{document_var}.title"
|
278
|
+
@window_url = js_eval "#{document_var}.URL"
|
279
|
+
end
|
280
|
+
|
281
|
+
public
|
282
|
+
def window_var
|
283
|
+
"window"
|
284
|
+
end
|
285
|
+
#private
|
286
|
+
def browser_var
|
287
|
+
"browser"
|
288
|
+
end
|
289
|
+
def document_var # unfinished
|
290
|
+
"document"
|
291
|
+
end
|
292
|
+
def body_var # unfinished
|
293
|
+
"body"
|
294
|
+
end
|
295
|
+
|
296
|
+
public
|
297
|
+
# Closes the window.
|
298
|
+
def close
|
299
|
+
|
300
|
+
if js_eval("getWindows().length").to_i == 1
|
301
|
+
js_eval("getWindows()[0].close()")
|
302
|
+
|
303
|
+
if current_os == :macosx
|
304
|
+
%x{ osascript -e 'tell application "Firefox" to quit' }
|
305
|
+
end
|
306
|
+
|
307
|
+
# wait for the app to close properly
|
308
|
+
@t.join if @t
|
309
|
+
else
|
310
|
+
# Check if window exists, because there may be the case that it has been closed by click event on some element.
|
311
|
+
# For e.g: Close Button, Close this Window link etc.
|
312
|
+
window_number = find_window(:url, @window_url)
|
313
|
+
|
314
|
+
# If matching window found. Close the window.
|
315
|
+
if window_number > 0
|
316
|
+
js_eval "getWindows()[#{window_number}].close()"
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Used for attaching pop up window to an existing Firefox window, either by url or title.
|
323
|
+
# ff.attach(:url, 'http://www.google.com')
|
324
|
+
# ff.attach(:title, 'Google')
|
325
|
+
#
|
326
|
+
# Output:
|
327
|
+
# Instance of newly attached window.
|
328
|
+
def attach(how, what)
|
329
|
+
|
330
|
+
$stderr.puts("warning: #{self.class}.attach is experimental") if $VERBOSE
|
331
|
+
window_number = find_window(how, what)
|
332
|
+
|
333
|
+
if(window_number.nil?)
|
334
|
+
raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
|
335
|
+
elsif(window_number >= 0)
|
336
|
+
@window_index = window_number
|
337
|
+
set_browser_document()
|
338
|
+
end
|
339
|
+
self
|
340
|
+
end
|
341
|
+
|
342
|
+
# Class method to return a browser object if a window matches for how
|
343
|
+
# and what. Window can be referenced by url or title.
|
344
|
+
# The second argument can be either a string or a regular expression.
|
345
|
+
# Watir::Browser.attach(:url, 'http://www.google.com')
|
346
|
+
# Watir::Browser.attach(:title, 'Google')
|
347
|
+
def self.attach how, what
|
348
|
+
br = new :suppress_launch_process => true # don't create window
|
349
|
+
br.attach(how, what)
|
350
|
+
br
|
351
|
+
end
|
352
|
+
|
353
|
+
# loads up a new window in an existing process
|
354
|
+
# Watir::Browser.attach() with no arguments passed the attach method will create a new window
|
355
|
+
# this will only be called one time per instance we're only ever going to run in 1 window
|
356
|
+
|
357
|
+
def open_window
|
358
|
+
|
359
|
+
if @opened_new_window
|
360
|
+
return @opened_new_window
|
361
|
+
end
|
362
|
+
|
363
|
+
jssh_command = "var windows = getWindows(); var window = windows[0];
|
364
|
+
window.open();
|
365
|
+
var windows = getWindows(); var window_number = windows.length - 1;
|
366
|
+
window_number;"
|
367
|
+
|
368
|
+
window_number = js_eval(jssh_command).to_i
|
369
|
+
@opened_new_window = window_number
|
370
|
+
return window_number if window_number >= 0
|
371
|
+
end
|
372
|
+
private :open_window
|
373
|
+
|
374
|
+
# return the window index for the browser window with the given title or url.
|
375
|
+
# how - :url or :title
|
376
|
+
# what - string or regexp
|
377
|
+
# Start searching windows in reverse order so that we attach/find the latest opened window.
|
378
|
+
def find_window(how, what)
|
379
|
+
jssh_command = "var windows = getWindows(); var window_number = false; var found = false;
|
380
|
+
for(var i = windows.length - 1; i >= 0; i--)
|
381
|
+
{
|
382
|
+
var attribute = '';
|
383
|
+
if(typeof(windows[i].getBrowser) != 'function')
|
384
|
+
{
|
385
|
+
continue;
|
386
|
+
}
|
387
|
+
var browser = windows[i].getBrowser();
|
388
|
+
if(!browser)
|
389
|
+
{
|
390
|
+
continue;
|
391
|
+
}
|
392
|
+
if(\"#{how}\" == \"url\")
|
393
|
+
{
|
394
|
+
attribute = browser.contentDocument.URL;
|
395
|
+
}
|
396
|
+
if(\"#{how}\" == \"title\")
|
397
|
+
{
|
398
|
+
attribute = browser.contentDocument.title;
|
399
|
+
}"
|
400
|
+
if(what.class == Regexp)
|
401
|
+
jssh_command << "var regExp = new RegExp(#{what.inspect});
|
402
|
+
found = regExp.test(attribute);"
|
403
|
+
else
|
404
|
+
jssh_command << "found = (attribute == \"#{what}\");"
|
405
|
+
end
|
406
|
+
|
407
|
+
jssh_command << "if(found)
|
408
|
+
{
|
409
|
+
window_number = i;
|
410
|
+
break;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
window_number;"
|
414
|
+
window_number = js_eval(jssh_command).to_s
|
415
|
+
return window_number == 'false' ? nil : window_number.to_i
|
416
|
+
end
|
417
|
+
private :find_window
|
418
|
+
|
419
|
+
#
|
420
|
+
# Description:
|
421
|
+
# Matches the given text with the current text shown in the browser.
|
422
|
+
#
|
423
|
+
# Input:
|
424
|
+
# target - Text to match. Can be a string or regex
|
425
|
+
#
|
426
|
+
# Output:
|
427
|
+
# Returns the index if the specified text was found.
|
428
|
+
# Returns matchdata object if the specified regexp was found.
|
429
|
+
#
|
430
|
+
def contains_text(target)
|
431
|
+
#puts "Text to match is : #{match_text}"
|
432
|
+
#puts "Html is : #{self.text}"
|
433
|
+
case target
|
434
|
+
when Regexp
|
435
|
+
self.text.match(target)
|
436
|
+
when String
|
437
|
+
self.text.index(target)
|
438
|
+
else
|
439
|
+
raise TypeError, "Argument #{target} should be a string or regexp."
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns the url of the page currently loaded in the browser.
|
444
|
+
def url
|
445
|
+
@window_url = js_eval "#{document_var}.location"
|
446
|
+
end
|
447
|
+
|
448
|
+
# Returns the title of the page currently loaded in the browser.
|
449
|
+
def title
|
450
|
+
@window_title = js_eval "#{document_var}.title"
|
451
|
+
end
|
452
|
+
|
453
|
+
# Returns the Status of the page currently loaded in the browser from statusbar.
|
454
|
+
#
|
455
|
+
# Output:
|
456
|
+
# Status of the page.
|
457
|
+
#
|
458
|
+
def status
|
459
|
+
js_status = js_eval("#{window_var}.status")
|
460
|
+
js_status.empty? ? js_eval("#{WINDOW_VAR}.XULBrowserWindow.statusText;") : js_status
|
461
|
+
end
|
462
|
+
|
463
|
+
|
464
|
+
# Returns the html of the page currently loaded in the browser.
|
465
|
+
def html
|
466
|
+
result = js_eval("var htmlelem = #{document_var}.getElementsByTagName('html')[0]; htmlelem.innerHTML")
|
467
|
+
return "<html>" + result + "</html>"
|
468
|
+
end
|
469
|
+
|
470
|
+
# Returns the text of the page currently loaded in the browser.
|
471
|
+
def text
|
472
|
+
js_eval("#{body_var}.textContent").strip
|
473
|
+
end
|
474
|
+
|
475
|
+
# Maximize the current browser window.
|
476
|
+
def maximize()
|
477
|
+
js_eval "#{window_var}.maximize()"
|
478
|
+
end
|
479
|
+
|
480
|
+
# Minimize the current browser window.
|
481
|
+
def minimize()
|
482
|
+
js_eval "#{window_var}.minimize()"
|
483
|
+
end
|
484
|
+
|
485
|
+
# Waits for the page to get loaded.
|
486
|
+
def wait(last_url = nil)
|
487
|
+
#puts "In wait function "
|
488
|
+
isLoadingDocument = ""
|
489
|
+
start = Time.now
|
490
|
+
|
491
|
+
while isLoadingDocument != "false"
|
492
|
+
isLoadingDocument = js_eval("#{browser_var}=#{window_var}.getBrowser(); #{browser_var}.webProgress.isLoadingDocument;")
|
493
|
+
#puts "Is browser still loading page: #{isLoadingDocument}"
|
494
|
+
|
495
|
+
# Raise an exception if the page fails to load
|
496
|
+
if (Time.now - start) > 300
|
497
|
+
raise "Page Load Timeout"
|
498
|
+
end
|
499
|
+
end
|
500
|
+
# If the redirect is to a download attachment that does not reload this page, this
|
501
|
+
# method will loop forever. Therefore, we need to ensure that if this method is called
|
502
|
+
# twice with the same URL, we simply accept that we're done.
|
503
|
+
url = js_eval("#{browser_var}.contentDocument.URL")
|
504
|
+
|
505
|
+
if(url != last_url)
|
506
|
+
# Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
|
507
|
+
# doesn't detect any javascript redirects so check it here.
|
508
|
+
# If page redirects to itself that this code will enter in infinite loop.
|
509
|
+
# So we currently don't wait for such a page.
|
510
|
+
# wait variable in JSSh tells if we should wait more for the page to get loaded
|
511
|
+
# or continue. -1 means page is not redirected. Anyother positive values means wait.
|
512
|
+
jssh_command = "var wait = -1; var meta = null; meta = #{browser_var}.contentDocument.getElementsByTagName('meta');
|
513
|
+
if(meta != null)
|
514
|
+
{
|
515
|
+
var doc_url = #{browser_var}.contentDocument.URL;
|
516
|
+
for(var i=0; i< meta.length;++i)
|
517
|
+
{
|
518
|
+
var content = meta[i].content;
|
519
|
+
var regex = new RegExp(\"^refresh$\", \"i\");
|
520
|
+
if(regex.test(meta[i].httpEquiv))
|
521
|
+
{
|
522
|
+
var arrContent = content.split(';');
|
523
|
+
var redirect_url = null;
|
524
|
+
if(arrContent.length > 0)
|
525
|
+
{
|
526
|
+
if(arrContent.length > 1)
|
527
|
+
redirect_url = arrContent[1];
|
528
|
+
|
529
|
+
if(redirect_url != null)
|
530
|
+
{
|
531
|
+
regex = new RegExp(\"^.*\" + redirect_url + \"$\");
|
532
|
+
if(!regex.test(doc_url))
|
533
|
+
{
|
534
|
+
wait = arrContent[0];
|
535
|
+
}
|
536
|
+
}
|
537
|
+
break;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
}
|
541
|
+
}
|
542
|
+
wait;"
|
543
|
+
wait_time = js_eval(jssh_command).to_i
|
544
|
+
begin
|
545
|
+
if(wait_time != -1)
|
546
|
+
sleep(wait_time)
|
547
|
+
# Call wait again. In case there are multiple redirects.
|
548
|
+
js_eval "#{browser_var} = #{window_var}.getBrowser()"
|
549
|
+
wait(url)
|
550
|
+
end
|
551
|
+
rescue
|
552
|
+
end
|
553
|
+
end
|
554
|
+
set_browser_document()
|
555
|
+
run_error_checks()
|
556
|
+
return self
|
557
|
+
end
|
558
|
+
|
559
|
+
# Add an error checker that gets called on every page load.
|
560
|
+
# * checker - a Proc object
|
561
|
+
def add_checker(checker)
|
562
|
+
@error_checkers << checker
|
563
|
+
end
|
564
|
+
|
565
|
+
# Disable an error checker
|
566
|
+
# * checker - a Proc object that is to be disabled
|
567
|
+
def disable_checker(checker)
|
568
|
+
@error_checkers.delete(checker)
|
569
|
+
end
|
570
|
+
|
571
|
+
# Run the predefined error checks. This is automatically called on every page load.
|
572
|
+
def run_error_checks
|
573
|
+
@error_checkers.each { |e| e.call(self) }
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
#def jspopup_appeared(popupText = "", wait = 2)
|
578
|
+
# winHelper = WindowHelper.new()
|
579
|
+
# return winHelper.hasPopupAppeared(popupText, wait)
|
580
|
+
#end
|
581
|
+
|
582
|
+
#
|
583
|
+
# Description:
|
584
|
+
# Redefines the alert and confirm methods on the basis of button to be clicked.
|
585
|
+
# This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function.
|
586
|
+
#
|
587
|
+
# Typical Usage:
|
588
|
+
# ff.button(:id, "button").click_no_wait
|
589
|
+
# ff.click_jspopup_button("OK")
|
590
|
+
#
|
591
|
+
# Input:
|
592
|
+
# button - JavaScript button to be clicked. Values can be OK or Cancel
|
593
|
+
#
|
594
|
+
#def click_jspopup_button(button)
|
595
|
+
# button = button.downcase
|
596
|
+
# element = Element.new(nil)
|
597
|
+
# element.click_js_popup(button)
|
598
|
+
#end
|
599
|
+
|
600
|
+
#
|
601
|
+
# Description:
|
602
|
+
# Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches
|
603
|
+
# text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until
|
604
|
+
# pop up is dismissed by manual intervention.
|
605
|
+
#
|
606
|
+
# Input:
|
607
|
+
# button - JavaScript button to be clicked. Values can be OK or Cancel
|
608
|
+
# waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir.
|
609
|
+
# userInput - Not used just for compatibility with Watir
|
610
|
+
# text - Text that should appear on pop up.
|
611
|
+
#
|
612
|
+
def startClicker(button, waitTime = 1, userInput = nil, text = nil)
|
613
|
+
jssh_command = "var win = #{browser_var}.contentWindow;"
|
614
|
+
if(button =~ /ok/i)
|
615
|
+
jssh_command << "var popuptext = '';
|
616
|
+
var old_alert = win.alert;
|
617
|
+
var old_confirm = win.confirm;
|
618
|
+
win.alert = function(param) {"
|
619
|
+
if(text != nil)
|
620
|
+
jssh_command << "if(param == \"#{text}\") {
|
621
|
+
popuptext = param;
|
622
|
+
return true;
|
623
|
+
}
|
624
|
+
else {
|
625
|
+
popuptext = param;
|
626
|
+
win.alert = old_alert;
|
627
|
+
win.alert(param);
|
628
|
+
}"
|
629
|
+
else
|
630
|
+
jssh_command << "popuptext = param; return true;"
|
631
|
+
end
|
632
|
+
jssh_command << "};
|
633
|
+
win.confirm = function(param) {"
|
634
|
+
if(text != nil)
|
635
|
+
jssh_command << "if(param == \"#{text}\") {
|
636
|
+
popuptext = param;
|
637
|
+
return true;
|
638
|
+
}
|
639
|
+
else {
|
640
|
+
win.confirm = old_confirm;
|
641
|
+
win.confirm(param);
|
642
|
+
}"
|
643
|
+
else
|
644
|
+
jssh_command << "popuptext = param; return true;"
|
645
|
+
end
|
646
|
+
jssh_command << "};"
|
647
|
+
|
648
|
+
elsif(button =~ /cancel/i)
|
649
|
+
jssh_command = "var old_confirm = win.confirm;
|
650
|
+
win.confirm = function(param) {"
|
651
|
+
if(text != nil)
|
652
|
+
jssh_command << "if(param == \"#{text}\") {
|
653
|
+
popuptext = param;
|
654
|
+
return false;
|
655
|
+
}
|
656
|
+
else {
|
657
|
+
win.confirm = old_confirm;
|
658
|
+
win.confirm(param);
|
659
|
+
}"
|
660
|
+
else
|
661
|
+
jssh_command << "popuptext = param; return false;"
|
662
|
+
end
|
663
|
+
jssh_command << "};"
|
664
|
+
end
|
665
|
+
js_eval jssh_command
|
666
|
+
end
|
667
|
+
|
668
|
+
#
|
669
|
+
# Description:
|
670
|
+
# Returns text of javascript pop up in case it comes.
|
671
|
+
#
|
672
|
+
# Output:
|
673
|
+
# Text shown in javascript pop up.
|
674
|
+
#
|
675
|
+
def get_popup_text()
|
676
|
+
return_value = js_eval "popuptext"
|
677
|
+
# reset the variable
|
678
|
+
js_eval "popuptext = ''"
|
679
|
+
return return_value
|
680
|
+
end
|
681
|
+
|
682
|
+
# Returns the document element of the page currently loaded in the browser.
|
683
|
+
def document
|
684
|
+
Document.new(self)
|
685
|
+
end
|
686
|
+
|
687
|
+
# Returns the first element that matches the given xpath expression or query.
|
688
|
+
def element_by_xpath(xpath)
|
689
|
+
temp = Element.new(nil, self)
|
690
|
+
element_name = temp.element_by_xpath(self, xpath)
|
691
|
+
return element_factory(element_name)
|
692
|
+
end
|
693
|
+
|
694
|
+
# Return object of correct Element class while using XPath to get the element.
|
695
|
+
def element_factory(element_name)
|
696
|
+
jssh_type = Element.new(element_name,self).element_type
|
697
|
+
#puts "jssh type is : #{jssh_type}" # DEBUG
|
698
|
+
candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : ''
|
699
|
+
#puts candidate_class # DEBUG
|
700
|
+
if candidate_class == 'Input'
|
701
|
+
input_type = js_eval("#{element_name}.type").downcase.strip
|
702
|
+
firewatir_class = input_class(input_type)
|
703
|
+
else
|
704
|
+
firewatir_class = jssh2firewatir(candidate_class)
|
705
|
+
end
|
706
|
+
|
707
|
+
#puts firewatir_class # DEBUG
|
708
|
+
klass = FireWatir.const_get(firewatir_class)
|
709
|
+
|
710
|
+
if klass == Element
|
711
|
+
klass.new(element_name,self)
|
712
|
+
elsif klass == CheckBox
|
713
|
+
klass.new(self,:jssh_name,element_name,["checkbox"])
|
714
|
+
elsif klass == Radio
|
715
|
+
klass.new(self,:jssh_name,element_name,["radio"])
|
716
|
+
else
|
717
|
+
klass.new(self,:jssh_name,element_name)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
private :element_factory
|
721
|
+
|
722
|
+
# Return the class name for element of input type depending upon its type like checkbox, radio etc.
|
723
|
+
def input_class(input_type)
|
724
|
+
hash = {
|
725
|
+
'select-one' => 'SelectList',
|
726
|
+
'select-multiple' => 'SelectList',
|
727
|
+
'text' => 'TextField',
|
728
|
+
'password' => 'TextField',
|
729
|
+
'textarea' => 'TextField',
|
730
|
+
# TODO when there's no type, it's a TextField
|
731
|
+
'file' => 'FileField',
|
732
|
+
'checkbox' => 'CheckBox',
|
733
|
+
'radio' => 'Radio',
|
734
|
+
'reset' => 'Button',
|
735
|
+
'button' => 'Button',
|
736
|
+
'submit' => 'Button',
|
737
|
+
'image' => 'Button'
|
738
|
+
}
|
739
|
+
hash.default = 'Element'
|
740
|
+
|
741
|
+
hash[input_type]
|
742
|
+
end
|
743
|
+
private :input_class
|
744
|
+
|
745
|
+
# For a provided element type returned by JSSh like HTMLDivElement,
|
746
|
+
# returns its corresponding class in Firewatir.
|
747
|
+
def jssh2firewatir(candidate_class)
|
748
|
+
hash = {
|
749
|
+
'Div' => 'Div',
|
750
|
+
'Button' => 'Button',
|
751
|
+
'Frame' => 'Frame',
|
752
|
+
'Span' => 'Span',
|
753
|
+
'Paragraph' => 'P',
|
754
|
+
'Label' => 'Label',
|
755
|
+
'Form' => 'Form',
|
756
|
+
'Image' => 'Image',
|
757
|
+
'Table' => 'Table',
|
758
|
+
'TableCell' => 'TableCell',
|
759
|
+
'TableRow' => 'TableRow',
|
760
|
+
'Select' => 'SelectList',
|
761
|
+
'Link' => 'Link',
|
762
|
+
'Anchor' => 'Link' # FIXME is this right?
|
763
|
+
#'Option' => 'Option' #Option uses a different constructor
|
764
|
+
}
|
765
|
+
hash.default = 'Element'
|
766
|
+
hash[candidate_class]
|
767
|
+
end
|
768
|
+
private :jssh2firewatir
|
769
|
+
|
770
|
+
#
|
771
|
+
# Description:
|
772
|
+
# Returns the array of elements that matches the xpath query.
|
773
|
+
#
|
774
|
+
# Input:
|
775
|
+
# Xpath expression or query.
|
776
|
+
#
|
777
|
+
# Output:
|
778
|
+
# Array of elements matching xpath query.
|
779
|
+
#
|
780
|
+
def elements_by_xpath(xpath)
|
781
|
+
element = Element.new(nil, self)
|
782
|
+
elem_names = element.elements_by_xpath(self, xpath)
|
783
|
+
elem_names.inject([]) {|elements,name| elements << element_factory(name)}
|
784
|
+
end
|
785
|
+
|
786
|
+
#
|
787
|
+
# Description:
|
788
|
+
# Show all the forms available on the page.
|
789
|
+
#
|
790
|
+
# Output:
|
791
|
+
# Name, id, method and action of all the forms available on the page.
|
792
|
+
#
|
793
|
+
def show_forms
|
794
|
+
forms = Document.new(self).get_forms()
|
795
|
+
count = forms.length
|
796
|
+
puts "There are #{count} forms"
|
797
|
+
for i in 0..count - 1 do
|
798
|
+
puts "Form name: " + forms[i].name
|
799
|
+
puts " id: " + forms[i].id
|
800
|
+
puts " method: " + forms[i].attribute_value("method")
|
801
|
+
puts " action: " + forms[i].action
|
802
|
+
end
|
803
|
+
end
|
804
|
+
alias showForms show_forms
|
805
|
+
|
806
|
+
#
|
807
|
+
# Description:
|
808
|
+
# Show all the images available on the page.
|
809
|
+
#
|
810
|
+
# Output:
|
811
|
+
# Name, id, src and index of all the images available on the page.
|
812
|
+
#
|
813
|
+
def show_images
|
814
|
+
images = Document.new(self).get_images
|
815
|
+
puts "There are #{images.length} images"
|
816
|
+
index = 1
|
817
|
+
images.each do |l|
|
818
|
+
puts "image: name: #{l.name}"
|
819
|
+
puts " id: #{l.id}"
|
820
|
+
puts " src: #{l.src}"
|
821
|
+
puts " index: #{index}"
|
822
|
+
index += 1
|
823
|
+
end
|
824
|
+
end
|
825
|
+
alias showImages show_images
|
826
|
+
|
827
|
+
#
|
828
|
+
# Description:
|
829
|
+
# Show all the links available on the page.
|
830
|
+
#
|
831
|
+
# Output:
|
832
|
+
# Name, id, href and index of all the links available on the page.
|
833
|
+
#
|
834
|
+
def show_links
|
835
|
+
links = Document.new(self).get_links
|
836
|
+
puts "There are #{links.length} links"
|
837
|
+
index = 1
|
838
|
+
links.each do |l|
|
839
|
+
puts "link: name: #{l.name}"
|
840
|
+
puts " id: #{l.id}"
|
841
|
+
puts " href: #{l.href}"
|
842
|
+
puts " index: #{index}"
|
843
|
+
index += 1
|
844
|
+
end
|
845
|
+
end
|
846
|
+
alias showLinks show_links
|
847
|
+
|
848
|
+
#
|
849
|
+
# Description:
|
850
|
+
# Show all the divs available on the page.
|
851
|
+
#
|
852
|
+
# Output:
|
853
|
+
# Name, id, class and index of all the divs available on the page.
|
854
|
+
#
|
855
|
+
def show_divs
|
856
|
+
divs = Document.new(self).get_divs
|
857
|
+
puts "There are #{divs.length} divs"
|
858
|
+
index = 1
|
859
|
+
divs.each do |l|
|
860
|
+
puts "div: name: #{l.name}"
|
861
|
+
puts " id: #{l.id}"
|
862
|
+
puts " class: #{l.className}"
|
863
|
+
puts " index: #{index}"
|
864
|
+
index += 1
|
865
|
+
end
|
866
|
+
end
|
867
|
+
alias showDivs show_divs
|
868
|
+
|
869
|
+
#
|
870
|
+
# Description:
|
871
|
+
# Show all the tables available on the page.
|
872
|
+
#
|
873
|
+
# Output:
|
874
|
+
# Id, row count, column count (only first row) and index of all the tables available on the page.
|
875
|
+
#
|
876
|
+
def show_tables
|
877
|
+
tables = Document.new(self).get_tables
|
878
|
+
puts "There are #{tables.length} tables"
|
879
|
+
index = 1
|
880
|
+
tables.each do |l|
|
881
|
+
puts "table: id: #{l.id}"
|
882
|
+
puts " rows: #{l.row_count}"
|
883
|
+
puts " columns: #{l.column_count}"
|
884
|
+
puts " index: #{index}"
|
885
|
+
index += 1
|
886
|
+
end
|
887
|
+
end
|
888
|
+
alias showTables show_tables
|
889
|
+
|
890
|
+
#
|
891
|
+
# Description:
|
892
|
+
# Show all the pre elements available on the page.
|
893
|
+
#
|
894
|
+
# Output:
|
895
|
+
# Id, name and index of all the pre elements available on the page.
|
896
|
+
#
|
897
|
+
def show_pres
|
898
|
+
pres = Document.new(self).get_pres
|
899
|
+
puts "There are #{pres.length} pres"
|
900
|
+
index = 1
|
901
|
+
pres.each do |l|
|
902
|
+
puts "pre: id: #{l.id}"
|
903
|
+
puts " name: #{l.name}"
|
904
|
+
puts " index: #{index}"
|
905
|
+
index += 1
|
906
|
+
end
|
907
|
+
end
|
908
|
+
alias showPres show_pres
|
909
|
+
|
910
|
+
#
|
911
|
+
# Description:
|
912
|
+
# Show all the spans available on the page.
|
913
|
+
#
|
914
|
+
# Output:
|
915
|
+
# Name, id, class and index of all the spans available on the page.
|
916
|
+
#
|
917
|
+
def show_spans
|
918
|
+
spans = Document.new(self).get_spans
|
919
|
+
puts "There are #{spans.length} spans"
|
920
|
+
index = 1
|
921
|
+
spans.each do |l|
|
922
|
+
puts "span: name: #{l.name}"
|
923
|
+
puts " id: #{l.id}"
|
924
|
+
puts " class: #{l.className}"
|
925
|
+
puts " index: #{index}"
|
926
|
+
index += 1
|
927
|
+
end
|
928
|
+
end
|
929
|
+
alias showSpans show_spans
|
930
|
+
|
931
|
+
#
|
932
|
+
# Description:
|
933
|
+
# Show all the labels available on the page.
|
934
|
+
#
|
935
|
+
# Output:
|
936
|
+
# Name, id, for and index of all the labels available on the page.
|
937
|
+
#
|
938
|
+
def show_labels
|
939
|
+
labels = Document.new(self).get_labels
|
940
|
+
puts "There are #{labels.length} labels"
|
941
|
+
index = 1
|
942
|
+
labels.each do |l|
|
943
|
+
puts "label: name: #{l.name}"
|
944
|
+
puts " id: #{l.id}"
|
945
|
+
puts " for: #{l.for}"
|
946
|
+
puts " index: #{index}"
|
947
|
+
index += 1
|
948
|
+
end
|
949
|
+
end
|
950
|
+
alias showLabels show_labels
|
951
|
+
|
952
|
+
#
|
953
|
+
# Description:
|
954
|
+
# Show all the frames available on the page. Doesn't show nested frames.
|
955
|
+
#
|
956
|
+
# Output:
|
957
|
+
# Name, and index of all the frames available on the page.
|
958
|
+
#
|
959
|
+
def show_frames
|
960
|
+
jssh_command = "var frameset = #{window_var}.frames;
|
961
|
+
var elements_frames = new Array();
|
962
|
+
for(var i = 0; i < frameset.length; i++)
|
963
|
+
{
|
964
|
+
var frames = frameset[i].frames;
|
965
|
+
for(var j = 0; j < frames.length; j++)
|
966
|
+
{
|
967
|
+
elements_frames.push(frames[j].frameElement);
|
968
|
+
}
|
969
|
+
}
|
970
|
+
elements_frames.length;"
|
971
|
+
|
972
|
+
length = js_eval(jssh_command).to_i
|
973
|
+
|
974
|
+
puts "There are #{length} frames"
|
975
|
+
|
976
|
+
frames = Array.new(length)
|
977
|
+
for i in 0..length - 1 do
|
978
|
+
frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]")
|
979
|
+
end
|
980
|
+
|
981
|
+
for i in 0..length - 1 do
|
982
|
+
puts "frame: name: #{frames[i].name}"
|
983
|
+
puts " index: #{i+1}"
|
984
|
+
end
|
985
|
+
end
|
986
|
+
alias showFrames show_frames
|
987
|
+
|
988
|
+
private
|
989
|
+
|
990
|
+
def path_to_bin
|
991
|
+
path = case current_os()
|
992
|
+
when :windows
|
993
|
+
path_from_registry
|
994
|
+
when :macosx
|
995
|
+
path_from_spotlight
|
996
|
+
when :linux
|
997
|
+
`which firefox`.strip
|
998
|
+
end
|
999
|
+
|
1000
|
+
raise "unable to locate Firefox executable" if path.nil? || path.empty?
|
1001
|
+
|
1002
|
+
path
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def current_os
|
1006
|
+
return @current_os if defined?(@current_os)
|
1007
|
+
|
1008
|
+
platform = RUBY_PLATFORM =~ /java/ ? java.lang.System.getProperty("os.name") : RUBY_PLATFORM
|
1009
|
+
|
1010
|
+
@current_os = case platform
|
1011
|
+
when /mingw32|mswin|windows/i
|
1012
|
+
:windows
|
1013
|
+
when /darwin|mac os/i
|
1014
|
+
:macosx
|
1015
|
+
when /linux/i
|
1016
|
+
:linux
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def path_from_registry
|
1021
|
+
raise NotImplementedError, "(need to know how to access windows registry on JRuby)" if RUBY_PLATFORM =~ /java/
|
1022
|
+
require 'win32/registry.rb'
|
1023
|
+
lm = Win32::Registry::HKEY_LOCAL_MACHINE
|
1024
|
+
lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
|
1025
|
+
reg1 = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
|
1026
|
+
if entry = reg1.find { |key, type, data| key =~ /pathtoexe/i }
|
1027
|
+
return entry.last
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
def path_from_spotlight
|
1033
|
+
ff = %x[mdfind 'kMDItemCFBundleIdentifier == "org.mozilla.firefox"']
|
1034
|
+
ff = ff.empty? ? '/Applications/Firefox.app' : ff.split("\n").first
|
1035
|
+
|
1036
|
+
"#{ff}/Contents/MacOS/firefox-bin"
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
end # Firefox
|
1040
|
+
end # FireWatir
|