firewatir 1.6.5 → 1.6.6.rc1

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