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.
Files changed (85) hide show
  1. data/LICENSE +32 -0
  2. data/lib/firewatir.rb +40 -50
  3. data/lib/firewatir/container.rb +491 -534
  4. data/lib/firewatir/document.rb +239 -0
  5. data/lib/firewatir/element.rb +1365 -0
  6. data/lib/firewatir/element_collections.rb +314 -0
  7. data/lib/firewatir/elements/button.rb +15 -0
  8. data/lib/firewatir/elements/file_field.rb +29 -0
  9. data/lib/firewatir/elements/form.rb +40 -0
  10. data/lib/firewatir/elements/frame.rb +55 -0
  11. data/lib/firewatir/elements/hidden.rb +56 -0
  12. data/lib/firewatir/elements/image.rb +139 -0
  13. data/lib/firewatir/elements/input_element.rb +44 -0
  14. data/lib/firewatir/elements/link.rb +76 -0
  15. data/lib/firewatir/elements/non_control_element.rb +53 -0
  16. data/lib/firewatir/elements/non_control_elements.rb +108 -0
  17. data/lib/firewatir/elements/not_used.rb +278 -0
  18. data/lib/firewatir/elements/option.rb +131 -0
  19. data/lib/firewatir/elements/radio_check_common.rb +163 -0
  20. data/lib/firewatir/elements/select_list.rb +188 -0
  21. data/lib/firewatir/elements/table.rb +218 -0
  22. data/lib/firewatir/elements/table_cell.rb +54 -0
  23. data/lib/firewatir/elements/table_row.rb +100 -0
  24. data/lib/firewatir/elements/text_field.rb +218 -0
  25. data/lib/firewatir/exceptions.rb +10 -10
  26. data/lib/firewatir/firefox.rb +1040 -1127
  27. data/lib/firewatir/jssh_socket.rb +101 -0
  28. data/lib/firewatir/version.rb +5 -5
  29. data/unittests/attach_to_new_window_test.rb +49 -42
  30. data/unittests/bug_fixes_test.rb +195 -198
  31. data/unittests/buttons_xpath_test.rb +88 -88
  32. data/unittests/checkbox_test.rb +158 -155
  33. data/unittests/checkbox_xpath_test.rb +107 -107
  34. data/unittests/div_test.rb +275 -276
  35. data/unittests/filefield_test.rb +49 -45
  36. data/unittests/filefield_xpath_test.rb +35 -35
  37. data/unittests/form_test.rb +296 -308
  38. data/unittests/frame_test.rb +159 -152
  39. data/unittests/hidden_test.rb +85 -85
  40. data/unittests/hidden_xpath_test.rb +72 -72
  41. data/unittests/html/blankpage.html +11 -11
  42. data/unittests/html/buttons1.html +61 -61
  43. data/unittests/html/cssTest.html +42 -42
  44. data/unittests/html/div.html +72 -72
  45. data/unittests/html/fileupload.html +45 -45
  46. data/unittests/html/formTest1.html +38 -38
  47. data/unittests/html/forms2.html +45 -45
  48. data/unittests/html/frame_buttons.html +3 -3
  49. data/unittests/html/iframeTest.html +14 -14
  50. data/unittests/html/iframeTest1.html +13 -13
  51. data/unittests/html/iframeTest2.html +5 -5
  52. data/unittests/html/links1.html +42 -42
  53. data/unittests/html/nestedFrames.html +6 -6
  54. data/unittests/html/new_browser.html +1 -0
  55. data/unittests/html/new_browser_popup.html +8 -0
  56. data/unittests/html/pass.html +9 -9
  57. data/unittests/html/pre.html +27 -27
  58. data/unittests/html/redirect.html +10 -10
  59. data/unittests/html/redirect1.html +8 -8
  60. data/unittests/html/redirect2.html +8 -8
  61. data/unittests/html/redirect3.html +8 -8
  62. data/unittests/html/simple_table_columns.html +74 -74
  63. data/unittests/html/table1.html +165 -165
  64. data/unittests/html/textfields1.html +62 -62
  65. data/unittests/images_test.rb +198 -205
  66. data/unittests/images_xpath_test.rb +118 -119
  67. data/unittests/javascript_test.rb +75 -75
  68. data/unittests/links_test.rb +231 -232
  69. data/unittests/links_xpath_test.rb +79 -79
  70. data/unittests/mozilla_all_tests.rb +7 -7
  71. data/unittests/pre_test.rb +75 -76
  72. data/unittests/radios_xpath_test.rb +101 -101
  73. data/unittests/redirect_test.rb +41 -41
  74. data/unittests/selectbox_test.rb +142 -142
  75. data/unittests/selectbox_xpath_test.rb +129 -129
  76. data/unittests/setup.rb +29 -30
  77. data/unittests/table_test.rb +385 -373
  78. data/unittests/table_xpath_test.rb +185 -185
  79. data/unittests/textfields_test.rb +234 -233
  80. data/unittests/textfields_xpath_test.rb +113 -113
  81. metadata +33 -11
  82. data/lib/firewatir/MozillaBaseElement.rb +0 -1863
  83. data/lib/firewatir/htmlelements.rb +0 -1911
  84. data/unittests/iostring.rb +0 -30
  85. 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
+
@@ -1,10 +1,10 @@
1
- require 'watir/exceptions' # from watir-common
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
+
@@ -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
- include FireWatir::Container
90
-
91
- # XPath Result type. Return only first node that matches the xpath expression.
92
- # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate"
93
- FIRST_ORDERED_NODE_TYPE = 9
94
-
95
- # variable to check if firefox browser has been started or not. Currently this is
96
- # used only while starting firefox on windows. For other platforms you need to start
97
- # firefox manually.
98
- #@@firefox_started = false
99
-
100
- # Stack to hold windows.
101
- @@window_stack = Array.new
102
-
103
- # This allows us to identify the window uniquely and close them accordingly.
104
- @window_title = nil
105
- @window_url = nil
106
-
107
- # Description:
108
- # Starts the firefox browser.
109
- # On windows this starts the first version listed in the registry.
110
- #
111
- # Input:
112
- # options - Hash of any of the following options:
113
- # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds.
114
- # This is done because if Firefox is not started and we try to connect
115
- # to jssh on port 9997 an exception is thrown.
116
- # :profile - The Firefox profile to use. If none is specified, Firefox will use
117
- # the last used profile.
118
-
119
- # TODO: Start the firefox version given by user. For example
120
- # ff = FireWatir::Firefox.new("1.5.0.4")
121
- #
122
-
123
- def initialize(options = {})
124
- if(options.kind_of?(Integer))
125
- options = {:waitTime => options}
126
- end
127
-
128
- if(options[:profile])
129
- profile_opt = "-no-remote -P #{options[:profile]}"
130
- else
131
- profile_opt = ""
132
- end
133
-
134
- waitTime = options[:waitTime] || 2
135
-
136
- case RUBY_PLATFORM
137
- when /mswin/
138
- # Get the path to Firefox.exe using Registry.
139
- require 'win32/registry.rb'
140
- path_to_bin = ""
141
- Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
142
- keys = reg.keys
143
- reg1 = Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{keys[0]}\\Main")
144
- reg1.each do |subkey, type, data|
145
- if(subkey =~ /pathtoexe/i)
146
- path_to_bin = data
147
- end
148
- end
149
- end
150
-
151
- when /linux/i
152
- path_to_bin = `which firefox`.strip
153
- when /darwin/i
154
- path_to_bin = '/Applications/Firefox.app/Contents/MacOS/firefox'
155
- when /java/
156
- raise "Not implemented: Create a browser finder in JRuby"
157
- end
158
- @t = Thread.new { system("#{path_to_bin} -jssh #{profile_opt}")}
159
- sleep waitTime
160
-
161
- set_defaults()
162
- get_window_number()
163
- set_browser_document()
164
- end
165
-
166
- #
167
- # Description:
168
- # Creates a new instance of Firefox. Loads the URL and return the instance.
169
- #
170
- # Input:
171
- # url - url of the page to be loaded.
172
- #
173
- # Output:
174
- # New instance of firefox browser with the given url loaded.
175
- #
176
- def self.start(url)
177
- ff = Firefox.new
178
- ff.goto(url)
179
- return ff
180
- end
181
-
182
- #
183
- # Description:
184
- # Gets the window number opened. Used internally by Firewatir.
185
- #
186
- def get_window_number()
187
- $jssh_socket.send("getWindows().length;\n", 0)
188
- @@current_window = read_socket().to_i - 1
189
-
190
- # Derek Berner 5/16/08
191
- # If at any time a non-browser window like the "Downloads" window
192
- # pops up, it will become the topmost window, so make sure we
193
- # ignore it.
194
- @@current_window = js_eval("getWindows().length").to_i - 1
195
- while js_eval("getWindows()[#{@@current_window}].getBrowser") == ''
196
- @@current_window -= 1;
197
- end
198
-
199
- # This will store the information about the window.
200
- #@@window_stack.push(@@current_window)
201
- #puts "here in get_window_number window number is #{@@current_window}"
202
- return @@current_window
203
- end
204
- private :get_window_number
205
-
206
- #
207
- # Description:
208
- # Loads the given url in the browser. Waits for the page to get loaded.
209
- #
210
- # Input:
211
- # url - url to be loaded.
212
- #
213
- def goto(url)
214
- #set_defaults()
215
- get_window_number()
216
- set_browser_document()
217
- # Load the given url.
218
- $jssh_socket.send("#{BROWSER_VAR}.loadURI(\"#{url}\");\n" , 0)
219
- read_socket()
220
-
221
- wait()
222
- end
223
-
224
- #
225
- # Description:
226
- # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.
227
- #
228
- def back()
229
- #set_browser_document()
230
- $jssh_socket.send("if(#{BROWSER_VAR}.canGoBack) #{BROWSER_VAR}.goBack();\n", 0)
231
- read_socket();
232
- wait()
233
- end
234
-
235
- #
236
- # Description:
237
- # Loads the next page (if there is any) in the browser. Waits for the page to get loaded.
238
- #
239
- def forward()
240
- #set_browser_document()
241
- $jssh_socket.send("if(#{BROWSER_VAR}.canGoForward) #{BROWSER_VAR}.goForward();\n", 0)
242
- read_socket();
243
- wait()
244
- end
245
-
246
- #
247
- # Description:
248
- # Reloads the current page in the browser. Waits for the page to get loaded.
249
- #
250
- def refresh()
251
- #set_browser_document()
252
- $jssh_socket.send("#{BROWSER_VAR}.reload();\n", 0)
253
- read_socket();
254
- wait()
255
- end
256
-
257
- #
258
- # Description:
259
- # This function creates a new socket at port 9997 and sets the default values for instance and class variables.
260
- # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries.
261
- #
262
- def set_defaults(no_of_tries = 0)
263
- # JSSH listens on port 9997. Create a new socket to connect to port 9997.
264
- begin
265
- $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
266
- $jssh_socket.sync = true
267
- read_socket()
268
- rescue
269
- no_of_tries += 1
270
- retry if no_of_tries < 3
271
- 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"
272
- end
273
- @error_checkers = []
274
- end
275
- private :set_defaults
276
-
277
- def set_slow_speed
278
- @typingspeed = DEFAULT_TYPING_SPEED
279
- @defaultSleepTime = DEFAULT_SLEEP_TIME
280
- end
281
- private :set_slow_speed
282
-
283
- #
284
- # Description:
285
- # Sets the document, window and browser variables to point to correct object in JSSh.
286
- #
287
- def set_browser_document
288
- # Get the window in variable WINDOW_VAR.
289
- # Get the browser in variable BROWSER_VAR.
290
- jssh_command = "var #{WINDOW_VAR} = getWindows()[#{@@current_window}];"
291
- jssh_command += " var #{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser();"
292
- # Get the document and body in variable DOCUMENT_VAR and BODY_VAR respectively.
293
- jssh_command += "var #{DOCUMENT_VAR} = #{BROWSER_VAR}.contentDocument;"
294
- jssh_command += "var #{BODY_VAR} = #{DOCUMENT_VAR}.body;"
295
-
296
- $jssh_socket.send("#{jssh_command}\n", 0)
297
- read_socket()
298
-
299
- # Get window and window's parent title and url
300
- $jssh_socket.send("#{DOCUMENT_VAR}.title;\n", 0)
301
- @window_title = read_socket()
302
- $jssh_socket.send("#{DOCUMENT_VAR}.URL;\n", 0)
303
- @window_url = read_socket()
304
- end
305
- private :set_browser_document
306
-
307
- #
308
- # Description:
309
- # Closes the window.
310
- #
311
- def close()
312
- #puts "current window number is : #{@@current_window}"
313
- # Derek Berner 5/16/08
314
- # Try to join thread only if there is exactly one open window
315
- if js_eval("getWindows().length").to_i == 1
316
- $jssh_socket.send(" getWindows()[0].close(); \n", 0)
317
- @t.join if @t != nil
318
- #sleep 5
319
- else
320
- # Check if window exists, because there may be the case that it has been closed by click event on some element.
321
- # For e.g: Close Button, Close this Window link etc.
322
- window_number = find_window("url", @window_url)
323
-
324
- # If matching window found. Close the window.
325
- if(window_number > 0)
326
- $jssh_socket.send(" getWindows()[#{window_number}].close();\n", 0)
327
- read_socket();
328
- end
329
-
330
- #Get the parent window url from the stack and return that window.
331
- #@@current_window = @@window_stack.pop()
332
- @window_url = @@window_stack.pop()
333
- @window_title = @@window_stack.pop()
334
- # Find window with this url.
335
- window_number = find_window("url", @window_url)
336
- @@current_window = window_number
337
- set_browser_document()
338
- end
339
- end
340
-
341
- #
342
- # Description:
343
- # Used for attaching pop up window to an existing Firefox window, either by url or title.
344
- # ff.attach(:url, 'http://www.google.com')
345
- # ff.attach(:title, 'Google')
346
- #
347
- # Output:
348
- # Instance of newly attached window.
349
- #
350
- def attach(how, what)
351
- window_number = find_window(how, what)
352
-
353
- if(window_number == 0)
354
- raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
355
- elsif(window_number > 0)
356
- # Push the window_title and window_url of parent window. So that when we close the child window
357
- # appropriate handle of parent window is returned back.
358
- @@window_stack.push(@window_title)
359
- @@window_stack.push(@window_url)
360
-
361
- @@current_window = window_number.to_i
362
- set_browser_document()
363
- end
364
- self
365
- end
366
-
367
- #
368
- # Description:
369
- # Finds a Firefox browser window with a given title or url.
370
- #
371
- def find_window(how, what)
372
- jssh_command = "getWindows().length;";
373
- $jssh_socket.send("#{jssh_command}\n", 0)
374
- @@total_windows = read_socket()
375
- #puts "total windows are : " + @@total_windows.to_s
376
-
377
- jssh_command = "var windows = getWindows(); var window_number = 0;var found = false;
378
- for(var i = 0; i < windows.length; i++)
379
- {
380
- var attribute = '';
381
- if(\"#{how}\" == \"url\")
382
- {
383
- attribute = windows[i].getBrowser().contentDocument.URL;
384
- }
385
- if(\"#{how}\" == \"title\")
386
- {
387
- attribute = windows[i].getBrowser().contentDocument.title;
388
- }"
389
- if(what.class == Regexp)
390
- # Construct the regular expression because we can't use it directly by converting it to string.
391
- # If reg ex is /Google/i then its string conversion will be (?i-mx:Google) so we can't use it.
392
- # Construct the regular expression again from the string conversion.
393
- oldRegExp = what.to_s
394
- newRegExp = "/" + what.source + "/"
395
- flags = oldRegExp.slice(2, oldRegExp.index(':') - 2)
396
-
397
- for i in 0..flags.length do
398
- flag = flags[i, 1]
399
- if(flag == '-')
400
- break;
401
- else
402
- newRegExp << flag
403
- end
404
- end
405
-
406
- jssh_command += "var regExp = new RegExp(#{newRegExp});
407
- found = regExp.test(attribute);"
408
- else
409
- jssh_command += "found = (attribute == \"#{what}\");"
410
- end
411
-
412
- jssh_command += "if(found)
413
- {
414
- window_number = i;
415
- break;
416
- }
417
- }
418
- window_number;"
419
-
420
- jssh_command.gsub!(/\n/, "")
421
- #puts "jssh_command is : #{jssh_command}"
422
- $jssh_socket.send("#{jssh_command}\n", 0)
423
- window_number = read_socket()
424
- #puts "window number is : " + window_number.to_s
425
-
426
- return window_number.to_i
427
- end
428
- private :find_window
429
-
430
- #
431
- # Description:
432
- # Matches the given text with the current text shown in the browser.
433
- #
434
- # Input:
435
- # target - Text to match. Can be a string or regex
436
- #
437
- # Output:
438
- # Returns the index if the specified text was found.
439
- # Returns matchdata object if the specified regexp was found.
440
- #
441
- def contains_text(target)
442
- #puts "Text to match is : #{match_text}"
443
- #puts "Html is : #{self.text}"
444
- case target
445
- when Regexp
446
- self.text.match(target)
447
- when String
448
- self.text.index(target)
449
- else
450
- raise ArgumentError, "Argument #{target} should be a string or regexp."
451
- end
452
- end
453
-
454
- #
455
- # Description:
456
- # Returns the url of the page currently loaded in the browser.
457
- #
458
- # Output:
459
- # URL of the page.
460
- #
461
- def url()
462
- @window_url
463
- end
464
-
465
- #
466
- # Description:
467
- # Returns the title of the page currently loaded in the browser.
468
- #
469
- # Output:
470
- # Title of the page.
471
- #
472
- def title()
473
- @window_title
474
- end
475
-
476
- #
477
- # Description:
478
- # Returns the html of the page currently loaded in the browser.
479
- #
480
- # Output:
481
- # HTML shown on the page.
482
- #
483
- def html()
484
- $jssh_socket.send("var htmlelem = #{DOCUMENT_VAR}.getElementsByTagName('html')[0]; htmlelem.innerHTML;\n", 0)
485
- #$jssh_socket.send("#{BODY_VAR}.innerHTML;\n", 0)
486
- result = read_socket()
487
- return "<html>" + result + "</html>"
488
- end
489
-
490
- #
491
- # Description:
492
- # Returns the text of the page currently loaded in the browser.
493
- #
494
- # Output:
495
- # Text shown on the page.
496
- #
497
- def text()
498
- $jssh_socket.send("#{BODY_VAR}.textContent;\n", 0)
499
- return read_socket().strip
500
- end
501
-
502
- #
503
- # Description:
504
- # Maximize the current browser window.
505
- #
506
- def maximize()
507
- $jssh_socket.send("#{WINDOW_VAR}.maximize();\n", 0)
508
- read_socket()
509
- end
510
-
511
- #
512
- # Description:
513
- # Minimize the current browser window.
514
- #
515
- def minimize()
516
- $jssh_socket.send("#{WINDOW_VAR}.minimize();\n", 0)
517
- read_socket()
518
- end
519
-
520
- #
521
- # Description:
522
- # Waits for the page to get loaded.
523
- #
524
- def wait(last_url = nil)
525
- #puts "In wait function "
526
- isLoadingDocument = ""
527
- start = Time.now
528
-
529
- while isLoadingDocument != "false"
530
- isLoadingDocument = js_eval("#{BROWSER_VAR}=#{WINDOW_VAR}.getBrowser(); #{BROWSER_VAR}.webProgress.isLoadingDocument;")
531
- #puts "Is browser still loading page: #{isLoadingDocument}"
532
-
533
- # Derek Berner 5/16/08
534
- # Raise an exception if the page fails to load
535
- if (Time.now - start) > 300
536
- raise "Page Load Timeout"
537
- end
538
- end
539
- # Derek Berner 5/16/08
540
- # If the redirect is to a download attachment that does not reload this page, this
541
- # method will loop forever. Therefore, we need to ensure that if this method is called
542
- # twice with the same URL, we simply accept that we're done.
543
- $jssh_socket.send("#{BROWSER_VAR}.contentDocument.URL;\n", 0)
544
- url = read_socket()
545
-
546
- if(url != last_url)
547
- # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
548
- # doesn't detect any javascript redirects so check it here.
549
- # If page redirects to itself that this code will enter in infinite loop.
550
- # So we currently don't wait for such a page.
551
- # wait variable in JSSh tells if we should wait more for the page to get loaded
552
- # or continue. -1 means page is not redirected. Anyother positive values means wait.
553
- jssh_command = "var wait = -1; var meta = null; meta = #{BROWSER_VAR}.contentDocument.getElementsByTagName('meta');
554
- if(meta != null)
555
- {
556
- var doc_url = #{BROWSER_VAR}.contentDocument.URL;
557
- for(var i=0; i< meta.length;++i)
558
- {
559
- var content = meta[i].content;
560
- var regex = new RegExp(\"^refresh$\", \"i\");
561
- if(regex.test(meta[i].httpEquiv))
562
- {
563
- var arrContent = content.split(';');
564
- var redirect_url = null;
565
- if(arrContent.length > 0)
566
- {
567
- if(arrContent.length > 1)
568
- redirect_url = arrContent[1];
569
-
570
- if(redirect_url != null)
571
- {
572
- regex = new RegExp(\"^.*\" + redirect_url + \"$\");
573
- if(!regex.test(doc_url))
574
- {
575
- wait = arrContent[0];
576
- }
577
- }
578
- break;
579
- }
580
- }
581
- }
582
- }
583
- wait;"
584
- #puts "command in wait is : #{jssh_command}"
585
- jssh_command = jssh_command.gsub(/\n/, "")
586
- $jssh_socket.send("#{jssh_command}; \n", 0)
587
- wait_time = read_socket();
588
- #puts "wait time is : #{wait_time}"
589
- begin
590
- wait_time = wait_time.to_i
591
- if(wait_time != -1)
592
- sleep(wait_time)
593
- # Call wait again. In case there are multiple redirects.
594
- $jssh_socket.send("#{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser(); \n",0)
595
- read_socket()
596
- wait(url)
597
- end
598
- rescue
599
- end
600
- end
601
- set_browser_document()
602
- run_error_checks()
603
- return self
604
- end
605
-
606
- # Add an error checker that gets called on every page load.
607
- #
608
- # * checker - a Proc object
609
- def add_checker(checker)
610
- @error_checkers << checker
611
- end
612
-
613
- # Disable an error checker
614
- #
615
- # * checker - a Proc object that is to be disabled
616
- def disable_checker(checker)
617
- @error_checkers.delete(checker)
618
- end
619
-
620
- # Run the predefined error checks. This is automatically called on every page load.
621
- def run_error_checks
622
- @error_checkers.each { |e| e.call(self) }
623
- end
624
-
625
-
626
- #def jspopup_appeared(popupText = "", wait = 2)
627
- # winHelper = WindowHelper.new()
628
- # return winHelper.hasPopupAppeared(popupText, wait)
629
- #end
630
-
631
- #
632
- # Description:
633
- # Redefines the alert and confirm methods on the basis of button to be clicked.
634
- # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function.
635
- #
636
- # Typical Usage:
637
- # ff.button(:id, "button").click_no_wait
638
- # ff.click_jspopup_button("OK")
639
- #
640
- # Input:
641
- # button - JavaScript button to be clicked. Values can be OK or Cancel
642
- #
643
- #def click_jspopup_button(button)
644
- # button = button.downcase
645
- # element = Element.new(nil)
646
- # element.click_js_popup(button)
647
- #end
648
-
649
- #
650
- # Description:
651
- # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches
652
- # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until
653
- # pop up is dismissed by manual intervention.
654
- #
655
- # Input:
656
- # button - JavaScript button to be clicked. Values can be OK or Cancel
657
- # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir.
658
- # userInput - Not used just for compatibility with Watir
659
- # text - Text that should appear on pop up.
660
- #
661
- def startClicker(button, waitTime = 1, userInput = nil, text = nil)
662
- jssh_command = "var win = #{BROWSER_VAR}.contentWindow;"
663
- if(button =~ /ok/i)
664
- jssh_command += "var popuptext = '';
665
- var old_alert = win.alert;
666
- var old_confirm = win.confirm;
667
- win.alert = function(param) {"
668
- if(text != nil)
669
- jssh_command += "if(param == \"#{text}\") {
670
- popuptext = param;
671
- return true;
672
- }
673
- else {
674
- popuptext = param;
675
- win.alert = old_alert;
676
- win.alert(param);
677
- }"
678
- else
679
- jssh_command += "popuptext = param; return true;"
680
- end
681
- jssh_command += "};
682
- win.confirm = function(param) {"
683
- if(text != nil)
684
- jssh_command += "if(param == \"#{text}\") {
685
- popuptext = param;
686
- return true;
687
- }
688
- else {
689
- win.confirm = old_confirm;
690
- win.confirm(param);
691
- }"
692
- else
693
- jssh_command += "popuptext = param; return true;"
694
- end
695
- jssh_command += "};"
696
-
697
- elsif(button =~ /cancel/i)
698
- jssh_command = "var old_confirm = win.confirm;
699
- win.confirm = function(param) {"
700
- if(text != nil)
701
- jssh_command += "if(param == \"#{text}\") {
702
- popuptext = param;
703
- return false;
704
- }
705
- else {
706
- win.confirm = old_confirm;
707
- win.confirm(param);
708
- }"
709
- else
710
- jssh_command += "popuptext = param; return false;"
711
- end
712
- jssh_command += "};"
713
- end
714
- jssh_command.gsub!(/\n/, "")
715
- #puts "jssh command sent for js pop up is : #{jssh_command}"
716
- $jssh_socket.send("#{jssh_command}\n", 0)
717
- read_socket()
718
- end
719
-
720
- #
721
- # Description:
722
- # Returns text of javascript pop up in case it comes.
723
- #
724
- # Output:
725
- # Text shown in javascript pop up.
726
- #
727
- def get_popup_text()
728
- $jssh_socket.send("popuptext;\n", 0)
729
- return_value = read_socket()
730
- # reset the variable
731
- $jssh_socket.send("popuptext = '';\n", 0)
732
- read_socket()
733
- return return_value
734
- end
735
-
736
- #
737
- # Description:
738
- # Returns the document element of the page currently loaded in the browser.
739
- #
740
- # Output:
741
- # Document element.
742
- #
743
- def document
744
- Document.new("#{DOCUMENT_VAR}")
745
- end
746
-
747
- #
748
- # Description:
749
- # Returns the first element that matches the xpath query.
750
- #
751
- # Input:
752
- # Xpath expression or query.
753
- #
754
- # Output:
755
- # Element matching the xpath query.
756
- #
757
- def element_by_xpath(xpath)
758
- temp = Element.new(nil, self)
759
- element_name = temp.element_by_xpath(self, xpath)
760
- return element_factory(element_name)
761
- end
762
-
763
- #
764
- # Description:
765
- # Factory method to create object of correct Element class while using XPath to get the element.
766
- #
767
- def element_factory(element_name)
768
- jssh_type = Element.new(element_name,self).element_type
769
- #puts "jssh type is : #{jssh_type}" # DEBUG
770
- candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : ''
771
- #puts candidate_class # DEBUG
772
- if candidate_class == 'Input'
773
- $jssh_socket.send("#{element_name}.type;\n", 0)
774
- input_type = read_socket().downcase.strip
775
- puts input_type # DEBUG
776
- firewatir_class = input_class(input_type)
777
- else
778
- firewatir_class = jssh2firewatir(candidate_class)
779
- end
780
-
781
- #puts firewatir_class # DEBUG
782
- klass = FireWatir.const_get(firewatir_class)
783
-
784
- if klass == Element
785
- klass.new(element_name,self)
786
- elsif klass == CheckBox
787
- klass.new(self,:jssh_name,element_name,["checkbox"])
788
- elsif klass == Radio
789
- klass.new(self,:jssh_name,element_name,["radio"])
790
- else
791
- klass.new(self,:jssh_name,element_name)
792
- end
793
- end
794
- private :element_factory
795
-
796
- #
797
- # Description:
798
- # Get the class name for element of input type depending upon its type like checkbox, radio etc.
799
- #
800
- def input_class(input_type)
801
- hash = {
802
- 'select-one' => 'SelectList',
803
- 'select-multiple' => 'SelectList',
804
- 'text' => 'TextField',
805
- 'password' => 'TextField',
806
- 'textarea' => 'TextField',
807
- # TODO when there's no type, it's a TextField
808
- 'file' => 'FileField',
809
- 'checkbox' => 'CheckBox',
810
- 'radio' => 'Radio',
811
- 'reset' => 'Button',
812
- 'button' => 'Button',
813
- 'submit' => 'Button',
814
- 'image' => 'Button'
815
- }
816
- hash.default = 'Element'
817
-
818
- hash[input_type]
819
- end
820
- private :input_class
821
-
822
- #
823
- # Description:
824
- # Converts element type returned by JSSh like HTMLDivElement to its corresponding class in Firewatir.
825
- #
826
- def jssh2firewatir(candidate_class)
827
- hash = {
828
- 'Div' => 'Div',
829
- 'Button' => 'Button',
830
- 'Frame' => 'Frame',
831
- 'Span' => 'Span',
832
- 'Paragraph' => 'P',
833
- 'Label' => 'Label',
834
- 'Form' => 'Form',
835
- 'Image' => 'Image',
836
- 'Table' => 'Table',
837
- 'TableCell' => 'TableCell',
838
- 'TableRow' => 'TableRow',
839
- 'Select' => 'SelectList',
840
- 'Link' => 'Link',
841
- 'Anchor' => 'Link' # FIXME is this right?
842
- #'Option' => 'Option' #Option uses a different constructor
843
- }
844
- hash.default = 'Element'
845
- hash[candidate_class]
846
- end
847
- private :jssh2firewatir
848
-
849
- #
850
- # Description:
851
- # Returns the array of elements that matches the xpath query.
852
- #
853
- # Input:
854
- # Xpath expression or query.
855
- #
856
- # Output:
857
- # Array of elements matching xpath query.
858
- #
859
- def elements_by_xpath(xpath)
860
- element = Element.new(nil, self)
861
- elem_names = element.elements_by_xpath(self, xpath)
862
- elem_names.inject([]) {|elements,name| elements << element_factory(name)}
863
- end
864
-
865
- #
866
- # Description:
867
- # Show all the forms available on the page.
868
- #
869
- # Output:
870
- # Name, id, method and action of all the forms available on the page.
871
- #
872
- def show_forms
873
- forms = Document.new(self).get_forms()
874
- count = forms.length
875
- puts "There are #{count} forms"
876
- for i in 0..count - 1 do
877
- puts "Form name: " + forms[i].name
878
- puts " id: " + forms[i].id
879
- puts " method: " + forms[i].attribute_value("method")
880
- puts " action: " + forms[i].action
881
- end
882
- end
883
- alias showForms show_forms
884
-
885
- #
886
- # Description:
887
- # Show all the images available on the page.
888
- #
889
- # Output:
890
- # Name, id, src and index of all the images available on the page.
891
- #
892
- def show_images
893
- images = Document.new(self).get_images
894
- puts "There are #{images.length} images"
895
- index = 1
896
- images.each do |l|
897
- puts "image: name: #{l.name}"
898
- puts " id: #{l.id}"
899
- puts " src: #{l.src}"
900
- puts " index: #{index}"
901
- index += 1
902
- end
903
- end
904
- alias showImages show_images
905
-
906
- #
907
- # Description:
908
- # Show all the links available on the page.
909
- #
910
- # Output:
911
- # Name, id, href and index of all the links available on the page.
912
- #
913
- def show_links
914
- links = Document.new(self).get_links
915
- puts "There are #{links.length} links"
916
- index = 1
917
- links.each do |l|
918
- puts "link: name: #{l.name}"
919
- puts " id: #{l.id}"
920
- puts " href: #{l.href}"
921
- puts " index: #{index}"
922
- index += 1
923
- end
924
- end
925
- alias showLinks show_links
926
-
927
- #
928
- # Description:
929
- # Show all the divs available on the page.
930
- #
931
- # Output:
932
- # Name, id, class and index of all the divs available on the page.
933
- #
934
- def show_divs
935
- divs = Document.new(self).get_divs
936
- puts "There are #{divs.length} divs"
937
- index = 1
938
- divs.each do |l|
939
- puts "div: name: #{l.name}"
940
- puts " id: #{l.id}"
941
- puts " class: #{l.className}"
942
- puts " index: #{index}"
943
- index += 1
944
- end
945
- end
946
- alias showDivs show_divs
947
-
948
- #
949
- # Description:
950
- # Show all the tables available on the page.
951
- #
952
- # Output:
953
- # Id, row count, column count (only first row) and index of all the tables available on the page.
954
- #
955
- def show_tables
956
- tables = Document.new(self).get_tables
957
- puts "There are #{tables.length} tables"
958
- index = 1
959
- tables.each do |l|
960
- puts "table: id: #{l.id}"
961
- puts " rows: #{l.row_count}"
962
- puts " columns: #{l.column_count}"
963
- puts " index: #{index}"
964
- index += 1
965
- end
966
- end
967
- alias showTables show_tables
968
-
969
- #
970
- # Description:
971
- # Show all the pre elements available on the page.
972
- #
973
- # Output:
974
- # Id, name and index of all the pre elements available on the page.
975
- #
976
- def show_pres
977
- pres = Document.new(self).get_pres
978
- puts "There are #{pres.length} pres"
979
- index = 1
980
- pres.each do |l|
981
- puts "pre: id: #{l.id}"
982
- puts " name: #{l.name}"
983
- puts " index: #{index}"
984
- index += 1
985
- end
986
- end
987
- alias showPres show_pres
988
-
989
- #
990
- # Description:
991
- # Show all the spans available on the page.
992
- #
993
- # Output:
994
- # Name, id, class and index of all the spans available on the page.
995
- #
996
- def show_spans
997
- spans = Document.new(self).get_spans
998
- puts "There are #{spans.length} spans"
999
- index = 1
1000
- spans.each do |l|
1001
- puts "span: name: #{l.name}"
1002
- puts " id: #{l.id}"
1003
- puts " class: #{l.className}"
1004
- puts " index: #{index}"
1005
- index += 1
1006
- end
1007
- end
1008
- alias showSpans show_spans
1009
-
1010
- #
1011
- # Description:
1012
- # Show all the labels available on the page.
1013
- #
1014
- # Output:
1015
- # Name, id, for and index of all the labels available on the page.
1016
- #
1017
- def show_labels
1018
- labels = Document.new(self).get_labels
1019
- puts "There are #{labels.length} labels"
1020
- index = 1
1021
- labels.each do |l|
1022
- puts "label: name: #{l.name}"
1023
- puts " id: #{l.id}"
1024
- puts " for: #{l.for}"
1025
- puts " index: #{index}"
1026
- index += 1
1027
- end
1028
- end
1029
- alias showLabels show_labels
1030
-
1031
- #
1032
- # Description:
1033
- # Show all the frames available on the page. Doesn't show nested frames.
1034
- #
1035
- # Output:
1036
- # Name, and index of all the frames available on the page.
1037
- #
1038
- def show_frames
1039
- jssh_command = "var frameset = #{WINDOW_VAR}.frames;
1040
- var elements_frames = new Array();
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